Search code examples
c#asp.net.netjsonjson-api

Modify .NET ApiController "BadResult()" to print custom JSON


I am using the .NET pattern where my controller action has a signature as such

public async Task<IHttpActionResult> GetTimeSeries(string deviceId, string tag) { ... }

and inside the controller I want to send a custom error message adhering to the JSON API Spec

My goal is when encountering an error to use

return BadRequest(someJSONErrorObject); 

or

return NotFound(someJSONErrorObject); 

rather than throw an exception.

Currently if I do

return BadRequest(JsonConvert.SerializeObject(someJSONErrorObject));

the JSON I get back looks like

{
"Message": "{\"data\":null,\"errors\":[{\"detail\":\"The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.\"}],\"meta\":null}"
}

Solution

  • Create a custom IHttpActionResult

    public class CustomResponseResult<T> : IHttpActionResult {
    
        public CustomResponseResult(HttpStatusCode statusCode, T content, HttpRequestMessage request) {
            Content = content;
            Request = request;
            StatusCode = statusCode;
        }
    
        public T Content { get; private set; }
        public HttpRequestMessage Request { get; private set; }
        public HttpStatusCode StatusCode { get; private set; }
    
        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) {
            return Task.FromResult(Execute());
        }
    
        private HttpResponseMessage Execute() {
            var response = Request.CreateResponse(StatusCode, Content);
            response.RequestMessage = Request;
            response.ReasonPhrase = StatusCode.ToString();
            return response;
        }
    }
    

    and then in order to maintain usability create extension methods

    public static class ApiControllerExtensions {
        public static IHttpActionResult BadRequest<T>(this ApiController controller, T content) {
            return controller.CustomResponse(HttpStatusCode.BadRequest, content);
        }
    
        public static IHttpActionResult NotFound<T>(this ApiController controller, T content) {
            return controller.CustomResponse(HttpStatusCode.NotFound, content);
        }
    
        public static IHttpActionResult CustomResponse<T>(this ApiController controller, HttpStatusCode statusCode, T content) {
            var request = controller.Request;
            var result = new CustomResponseResult<T>(statusCode, content, request);
            return result;
        }
    }
    

    that allowed the calls wanted

    return this.BadRequest(someJSONErrorObject);
    

    or

    return this.NotFound(someJSONErrorObject);