إنشاء BasketController
لإدارة الـ Customer Basket
1. إنشاء الـ Controller
هنبدأ بإنشاء Controller جديد داخل مشروع الـ API، ونسميه BasketController
. الهدف من الـ Controller هو توفير Endpoints للتعامل مع الـ BasketRepository اللي أنشأناه في الخطوات السابقة.
الكود:
public class BasketController : BaseApiController
{
private readonly IBasketRepository _basketRepository;
public BasketController(IBasketRepository basketRepository)
{
_basketRepository = basketRepository;
}
// Endpoints هنا هنضيف الـ
}
2. إعداد الـ Dependency Injection
هنقوم بتسجيل الـ BasketRepository
في الـ DI Container في ملف Program.cs
:
الكود:
// Register IBasketRepository with its implementation
builder.Services.AddScoped<IBasketRepository, BasketRepository>();
ملاحظات:
- استخدمنا Scoped لأن الـ
BasketRepository
مش محتاج يكون موجود طول عمر التطبيق، بعكس الـ Redis Connection اللي لازم يكون Singleton.
3. إنشاء Endpoints
3.1. Get Basket
- يسترجع بيانات سلة المشتريات بناءً على الـ
id
. - لو السلة غير موجودة (Expired أو مفيش بيانات)، يتم إنشاء سلة جديدة بنفس الـ
id
.
الكود:
[HttpGet("{id}")] // GET : /api/basket/id
public async Task<ActionResult<CustomerBasket>> GetBasket(string id)
{
var basket = await _basketRepository.GetBasketAsync(id);
return Ok(basket ?? new CustomerBasket(id)); // لو السلة مش موجودة، يتم إنشاء سلة جديدة فارغة
}
// Constructor for creating a new CustomerBasket
// Make Also Empty Ctor
public CustomerBasket(string id)
{
Id = id;
Items = new List<BasketItem>(); // قائمة فارغة
}
ملاحظة:
-
يمكن استخدام Query Parameter بدلًا من Segment:
[HttpGet] // GET: /api/basket?id=1
3.2. Update Basket
- يسمح بإنشاء أو تحديث سلة المشتريات.
- لو حصل خطأ أثناء العملية، يتم إرجاع استجابة خطأ (
400 Bad Request
).
الكود:
[HttpPost] // POST: /api/basket
public async Task<ActionResult<CustomerBasket>> UpdateBasket(CustomerBasket basket)
{
var createdOrUpdatedBasket = await _basketRepository.UpdateBasketAsync(basket);
if (createdOrUpdatedBasket == null)
return BadRequest(new ApiResponse(400)); // خطأ في العملية
return Ok(createdOrUpdatedBasket);
}
3.3. Delete Basket
- يقوم بحذف سلة المشتريات بناءً على الـ
id
.
الكود:
[HttpDelete] // DELETE: /api/basket?id=1
public async Task<ActionResult> DeleteBasket(string id)
{
await _basketRepository.DeleteBasketAsync(id);
return NoContent(); // 204 No Content
}
4. الكود النهائي لـ BasketController
public class BasketController : BaseApiController
{
private readonly IBasketRepository _basketRepository;
public BasketController(IBasketRepository basketRepository)
{
_basketRepository = basketRepository;
}
[HttpGet("{id}")] // GET : /api/basket/id
public async Task<ActionResult<CustomerBasket>> GetBasket(string id)
{
var basket = await _basketRepository.GetBasketAsync(id);
return Ok(basket ?? new CustomerBasket(id));
}
[HttpPost] // POST: /api/basket
public async Task<ActionResult<CustomerBasket>> UpdateBasket(CustomerBasket basket)
{
var createdOrUpdatedBasket = await _basketRepository.UpdateBasketAsync(basket);
if (createdOrUpdatedBasket == null)
return BadRequest(new ApiResponse(400));
return Ok(createdOrUpdatedBasket);
}
[HttpDelete] // DELETE: /api/basket?id=1
public async Task<ActionResult> DeleteBasket(string id)
{
await _basketRepository.DeleteBasketAsync(id);
return NoContent();
}
}
5. النقاط المهمة في التصميم
- Singleton vs Scoped:
- الـ Redis Connection تم تسجيله كـ Singleton لأنه يتم استخدامه في أكثر من مكان ويحتاج للبقاء مفتوحًا طوال عمر التطبيق.
- أما الـ
BasketRepository
تم تسجيله كـ Scoped لأنه خاص بكل طلب HTTP على حدة.
- استجابة السلة الجديدة:
- في حالة عدم وجود سلة، يتم إنشاء سلة جديدة وإرجاعها مباشرةً للعميل.
- التعامل مع الأخطاء:
- تم إضافة استجابة
400 Bad Request
عند حدوث أي مشكلة أثناء تحديث السلة.
- تم إضافة استجابة
- مرونة في التصميم:
- يمكن استخدام Query Parameters بدلًا من Route Segments حسب الحاجة.