Friday, May 10, 2019

What are your opinions on how to structure consistent api responses in .net core api.

I'm looking to implement a consistent json api response across all action methods. ideally a no fuss pattern that isn't over engineered.

Does anyone have a good pattern / preference?

Successful responses return the payload in a field called data with a status object with no errors. if there are error messages the payload returns a status object with an error code and a message or messages.

I'll demonstrate what i mean with a few request / responses to get a person.

happy path

Request:

GET https://www.somedomain.com/api/person/1 HTTP/1.1

Response:

HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 ...other http headers ommitted { "status": { "error_code": 0, "error_message": null }, "data": { "User": { "Id": 1, "Address": { "Street": "100 fake street", "Country": "USA" }, "Name": "Donnie Darko" }, "last_updated": "2019-05-10T21:57:00.000Z" } } 

Unhappy path

Request:

GET https://www.somedomain.com/api/person/donnie HTTP/1.1

Response:

HTTP/1.1 400 Bad Request Content-Type: application/json; charset=utf-8 { "status": { "error_code": 221, "error_message": "Id must be numeric", } } 

I'd like to structure my responses similar to this.

I found this article that implements something similar, though i would probably tweek it a bit e.g. passing the http status code as a field is not required.

here is the article:

https://www.devtrends.co.uk/blog/handling-errors-in-asp.net-core-web-api

Does anyone have a good pattern / preference?

code example used in the article:

public class ApiResponse { public int StatusCode { get; } [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string Message { get; } public ApiResponse(int statusCode, string message = null) { StatusCode = statusCode; Message = message ?? GetDefaultMessageForStatusCode(statusCode); } private static string GetDefaultMessageForStatusCode(int statusCode) { switch (statusCode) { ... case 404: return "Resource not found"; case 500: return "An unhandled error occurred"; default: return null; } } } public class ApiOkResponse : ApiResponse { public object Result { get; } public ApiOkResponse(object result) :base(200) { Result = result; } } public class ApiBadRequestResponse : ApiResponse { public IEnumerable<string> Errors { get; } public ApiBadRequestResponse(ModelStateDictionary modelState) : base(400) { if (modelState.IsValid) { throw new ArgumentException("ModelState must be invalid", nameof(modelState)); } Errors = modelState.SelectMany(x => x.Value.Errors) .Select(x => x.ErrorMessage).ToArray(); } } // finally it being used in a controller [HttpGet("product")] public async Task<IActionResult> GetProduct(GetProductRequest request) { if (!ModelState.IsValid) { return BadRequest(new ApiBadRequestResponse(ModelState)); } var model = await _db.Get(...); if (model == null) { return NotFound(new ApiResponse(404, $"Product not found with id {request.ProductId}")); } return Ok(new ApiOkResponse(model)); } 
What are your opinions on how to structure consistent api responses in .net core api. Click here
  • Blogger Comment
  • Facebook Comment

0 comments:

Post a Comment

The webdev Team