14. Simplifying API Responses in .NET Core Minimal API
Handling consistent and structured API responses is crucial for building robust web services. In this post, we will create a standardized APIResponse class and demonstrate its implementation in a .NET API.
Defining the APIResponse Class
The APIResponse class ensures that all responses from your API have a consistent format. This helps clients of your API handle responses uniformly, whether they are successful or contain errors.
public class APIResponse
{
public APIResponse()
{
ErrorMessages = new List<string>();
}
public bool IsSuccess { get; set; }
public object Result { get; set; }
public HttpStatusCode StatusCode { get; set; }
public List<string> ErrorMessages { get; set; }
}
Implementing API Endpoints
Let's see how to use the APIResponse class in your .NET API endpoints.
Setting Up the Application
First, set up your application using the .NET minimal API template.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
GET Endpoint
Implement a GET endpoint to retrieve all coupons.
app.MapGet("/api/coupon", (ILogger<Program> _logger) => {
APIResponse response = new();
_logger.Log(LogLevel.Information, "Getting all Coupons");
response.Result = CouponStore.couponList;
response.IsSuccess = true;
response.StatusCode = HttpStatusCode.OK;
return Results.Ok(response);
}).WithName("GetCoupons").Produces<APIResponse>(200);
POST Endpoint
Implement a POST endpoint to create a new coupon.
app.MapPost("/api/coupon", async (IMapper _mapper, IValidator<CouponCreateDTO> _validation, [FromBody] CouponCreateDTO coupon_C_DTO) => {
APIResponse response = new() { IsSuccess = false, StatusCode = HttpStatusCode.BadRequest };
var validationResult = await _validation.ValidateAsync(coupon_C_DTO);
if (!validationResult.IsValid)
{
response.ErrorMessages.Add(validationResult.Errors.FirstOrDefault()?.ToString());
return Results.BadRequest(response);
}
if (CouponStore.couponList.Any(u => u.Name.ToLower() == coupon_C_DTO.Name.ToLower()))
{
response.ErrorMessages.Add("Coupon Name already Exists");
return Results.BadRequest(response);
}
Coupon coupon = _mapper.Map<Coupon>(coupon_C_DTO);
coupon.Id = CouponStore.couponList.Max(u => u.Id) + 1;
CouponStore.couponList.Add(coupon);
CouponDTO couponDTO = _mapper.Map<CouponDTO>(coupon);
response.Result = couponDTO;
response.IsSuccess = true;
response.StatusCode = HttpStatusCode.Created;
return Results.Ok(response);
}).WithName("CreateCoupon").Accepts<CouponCreateDTO>("application/json").Produces<APIResponse>(201).Produces(400);
PUT Endpoint
Implement a PUT endpoint to update an existing coupon.
app.MapPut("/api/coupon", async (IMapper _mapper, IValidator<CouponUpdateDTO> _validation, [FromBody] CouponUpdateDTO coupon_U_DTO) => {
APIResponse response = new() { IsSuccess = false, StatusCode = HttpStatusCode.BadRequest };
var validationResult = await _validation.ValidateAsync(coupon_U_DTO);
if (!validationResult.IsValid)
{
response.ErrorMessages.Add(validationResult.Errors.FirstOrDefault()?.ToString());
return Results.BadRequest(response);
}
var couponFromStore = CouponStore.couponList.FirstOrDefault(u => u.Id == coupon_U_DTO.Id);
if (couponFromStore == null)
{
response.ErrorMessages.Add("Coupon not found");
return Results.NotFound(response);
}
_mapper.Map(coupon_U_DTO, couponFromStore);
response.Result = _mapper.Map<CouponDTO>(couponFromStore);
response.IsSuccess = true;
response.StatusCode = HttpStatusCode.OK;
return Results.Ok(response);
}).WithName("UpdateCoupon").Accepts<CouponUpdateDTO>("application/json").Produces<APIResponse>(200).Produces(400).Produces(404);
Running the Application
Finally, run the application.
app.Run();
Conclusion
By implementing the APIResponse class, you ensure that your .NET API returns consistent and structured responses. This practice enhances the clarity and maintainability of your API, making it easier for clients to consume and handle responses effectively.
Feel free to share your thoughts or ask any questions in the comments below!
Happy coding!