حل مشكلة الـ Picture Url عشان يظهر بـ Full Path

المشكلة:

  • الصور محفوظة في الـ Database بدون Base Url، وده:
    • بيعمل تكرار لو أضفت الـ Url لكل صورة.
    • بيصعب الموضوع لو الـ Environment متغير (Development, Staging, Production).

الحل:

بدل ما أثبت الـ Base Url، نستخدم appsettings، ونعمل الـ Mapping بطريقة ديناميكية.

خطوات حل المشكلة

1. تعديل الـ appsettings

  • في ملف appsettings الخاص بالـ API نضيف المفتاح ده:
"ApiBaseUrl": "https://localhost:7265"

2. تعديل MappingProfile باستخدام Fixed Url

  • في البداية، ممكن نعدّل الـ Mapping مباشرة باستخدام الـ MapFrom بس ده هيكون Static Url، وده مش الطريقة الأفضل.
.ForMember(d => d.PictureUrl, O => O.MapFrom(s => $"https://localhost:7265/{s.PictureUrl}"));
  • لكن، دي طريقة مؤقتة وغير مرنة لأنك بتثبّت الـ Base Url.

3. استخدام الـ IConfiguration

  • بدل التثبيت، محتاج أكلم الـ appsettings وأجيب منه الـ Base Url.
  • نعمل Object من IConfiguration ونمرره في الـ Constructor، عشان أقدر أستخدمه مباشرة.
مشكلة Empty Constructor:

لما تضيف Object للـ IConfiguration، ممكن تظهر مشكلة إنك بتستخدم Constructor فاضي في مكان آخر، وبالتالي محتاج طريقة مختلفة لحل المشكلة.


4. الحل: استخدام Resolver مع MapFrom

  • نستخدم النسخة الـ Generic من MapFrom اللي بتسمح بالمرونة.
الخطوات:
  1. ننشئ Class جديد في فولدر Helpers ونسميه ProductPictureUrlResolver.
  2. الكلاس ده هيعمل Map للـ Picture Url بإضافة الـ Base Url.
الكود:
public class ProductPictureUrlResolver : IValueResolver<Product, ProductToReturnDto, string>
{
    private readonly IConfiguration _configuration;
 
    // Constructor عشان نمرر الـ IConfiguration
    public ProductPictureUrlResolver(IConfiguration configuration)
    {
        _configuration = configuration;
    }
 
    // Resolve Method لإضافة الـ Base Url
    public string Resolve(Product source, ProductToReturnDto destination, string destMember, ResolutionContext context)
    {
        if (!string.IsNullOrEmpty(source.PictureUrl))
        {
            return $"{_configuration["ApiBaseUrl"]}/{source.PictureUrl}";
        }
        return string.Empty;
    }
}

5. تعديل MappingProfiles

  • في ملف MappingProfiles نضيف الـ Resolver الجديد بدل الطريقة الـ Static:
public class MappingProfiles : Profile
{
    public MappingProfiles()
    {
        CreateMap<Product, ProductToReturnDto>()
            .ForMember(d => d.Brand, O => O.MapFrom(s => s.Brand.Name))
            .ForMember(d => d.Category, O => O.MapFrom(s => s.Category.Name))
            .ForMember(d => d.PictureUrl, O => O.MapFrom<ProductPictureUrlResolver>());
    }
}

6. تسجيل AutoMapper

  • في Program.cs أو المكان الخاص بتسجيل الـ Services نضيف السطر ده:
builder.Services.AddAutoMapper(typeof(MappingProfiles));

7. إضافة الصور كـ Static Files في المشروع

1. المشكلة:

لما تحاول تعمل Request للصورة بالـ Postman من خلال الـ URL الناتج، بيظهر خطأ Not Found لأن السيرفر مش مهيأ للتعامل مع الصور كـ Static Files.

2. الحل:
  • تحتاج تهيئة السيرفر عشان يقدر يخدم الصور الموجودة في فولدر wwwroot.
3. الخطوات التفصيلية:
أولًا: إنشاء فولدر للصور
  1. لو مفيش فولدر باسم wwwroot:
    • أنشئ فولدر في مشروع الـ API وسمّه wwwroot.
  2. داخل فولدر wwwroot:
    • أنشئ فولدر جديد وسمّه Images.
    • انسخ الصور اللي عندك وحطها في فولدر Images.
  3. تأكد إن أسماء الصور هي نفسها المسجلة في الـ Database.

ثانيًا: إضافة Middleware للـ Static Files
  1. افتح ملف Program.cs أو أي ملف بتعمل فيه إعدادات الـ Pipeline.
  2. أضف السطر التالي تحت app.UseHttpsRedirection():
app.UseStaticFiles();

الخلاصة:
  • الصور تعتبر Static Files، فالسيرفر محتاج Middleware (UseStaticFiles) للتعامل معها.
  • ضع الصور في فولدر wwwroot/Images وتأكد من أسماء الملفات.
  • أضف Middleware للـ Static Files تحت app.UseHttpsRedirection.

الخلاصة

  • الهدف هو إنك تضيف الـ Base Url لكل الصور ديناميكيًا بناءً على الـ Environment.
  • استخدمنا الـ IConfiguration لإضافة المرونة.
  • حللنا المشكلة عن طريق إنشاء Resolver جديد بدل ما نثبت الـ Base Url في الكود.