As a follow up to my investigation on API Responses I’ve decided to not create yet another random format and just stick to JSON API even if it means more parsing on the front-end.

That being said, ASP .NET Core’s standard response format for ModelState validation doesn’t follow the convention, so I’ve written a small helper class.

Unfortunately in .NET Core 2.0 I couldn’t figure out if there’s a way of overwriting the default response format (maybe with an extension method? let me know below) so I’ve just intercepted the ModelState.IsValid.

Here’s a small snippet from an user registration method

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
[AllowAnonymous]
[HttpPost]
public async Task<IActionResult> Store([FromBody] Models.Validators.UserRegister item)
{
    if (item is null)
    {
        return BadRequest(_apiResponse.AddErrorResponse(code: "InvalidFormData").ErrorReponse);
    }

    if (!ModelState.IsValid)
    {
        return BadRequest(_apiResponse.ParseModelStateResponse(ModelState).ErrorReponse);
    }

    var user = new User { UserName = item.Email, Email = item.Email };
    var result = await _users.UserManager.CreateAsync(user, item.Password);

    return Ok(result);
}

And here’s the class (and a gist for copy pasting):

Nothing too special, just a bit of method chaining and optional named parameters.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
using Newtonsoft.Json.Linq;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace Craidd.Helpers
{
    /// <summary>
    /// Manage JSON Api response
    /// </summary>
    public class ApiResponseHelper : IApiResponseHelper
    {
        public JObject ErrorReponse { get; set; } = new JObject(new JProperty("errors", new JArray()));
        
        /// <summary>
        /// Create a JSON API Error Response
        /// </summary>
        /// <param name="title"></param>
        /// <param name="detail"></param>
        /// <param name="code"></param>
        /// <param name="sourceParameter"></param>
        /// <returns></returns>
        public ApiResponseHelper AddErrorResponse(string title = null, string detail = null, string code = null, string sourceParameter = null)
        {
            var error = new JObject();
            
            if (! (title is null) )
            {
                error.Add(new JProperty("title", title));
            }
            
            if (! (detail is null) )
            {
                error.Add(new JProperty("detail", detail));
            }
            
            if (! (code is null) )
            {
                error.Add(new JProperty("code", code));
            }
            
            if (! (sourceParameter is null) )
            {
                error.Add(new JProperty("source", new JObject( new JProperty("parameter", sourceParameter))));
            }
            
            ErrorReponse["errors"].Value<JArray>().Add(error);

            return this;
        }

        
        /// <summary>
        /// Parse a ModelState validation into a JSON API Error response
        /// </summary>
        /// <param name="modelState"></param>
        /// <returns></returns>
        public ApiResponseHelper ParseModelStateResponse(ModelStateDictionary modelState)
        {
            var errorData = modelState.Where(ms => ms.Value.Errors.Any())
                                      .Select(x => new { x.Key, x.Value.Errors });

            foreach (var stateError in errorData)
            {
                var errorMessages = stateError.Errors;

                foreach (var errorMessage in errorMessages)
                {
                    AddErrorResponse(sourceParameter: stateError.Key, detail: errorMessage.ErrorMessage);
                }
            }

            return this;
        }
    }
}

How are you handling API responses in your apps ?