حل مشكلة الـ 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
اللي بتسمح بالمرونة.
الخطوات:
- ننشئ Class جديد في فولدر
Helpers
ونسميهProductPictureUrlResolver
. - الكلاس ده هيعمل 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. الخطوات التفصيلية:
أولًا: إنشاء فولدر للصور
- لو مفيش فولدر باسم
wwwroot
:- أنشئ فولدر في مشروع الـ API وسمّه
wwwroot
.
- أنشئ فولدر في مشروع الـ API وسمّه
- داخل فولدر
wwwroot
:- أنشئ فولدر جديد وسمّه
Images
. - انسخ الصور اللي عندك وحطها في فولدر
Images
.
- أنشئ فولدر جديد وسمّه
- تأكد إن أسماء الصور هي نفسها المسجلة في الـ Database.
ثانيًا: إضافة Middleware للـ Static Files
- افتح ملف
Program.cs
أو أي ملف بتعمل فيه إعدادات الـ Pipeline. - أضف السطر التالي تحت
app.UseHttpsRedirection()
:
app.UseStaticFiles();
الخلاصة:
- الصور تعتبر Static Files، فالسيرفر محتاج Middleware (
UseStaticFiles
) للتعامل معها. - ضع الصور في فولدر
wwwroot/Images
وتأكد من أسماء الملفات. - أضف Middleware للـ Static Files تحت
app.UseHttpsRedirection
.
الخلاصة
- الهدف هو إنك تضيف الـ Base Url لكل الصور ديناميكيًا بناءً على الـ Environment.
- استخدمنا الـ
IConfiguration
لإضافة المرونة. - حللنا المشكلة عن طريق إنشاء Resolver جديد بدل ما نثبت الـ Base Url في الكود.