19

Is it possible to return { } instead of null when webApi returns a null object? This, to prevent my user from getting errors while parsing the response. And to make the response a valid Json Response?

I know that i could be setting it everywhere manually. That when null is the response, an empty Json object should be returned. But, is there a way to do it automaticly for every response?

5
  • 2
    If you have nothing to return, the response shouldn't be null, but 404 (Not Found). Commented Mar 31, 2014 at 14:44
  • 2
    If you are using WebApi for the purposes of building a RESTful api, then I agree with @Mark Seemann. If you are just using webApi for the url routing and don't really intend to follow REST practices then you can return whatever you want. Commented Mar 31, 2014 at 14:59
  • 1
    Indeed my project is trying to follow the REST practices, so i agree with @MarkSeemann. Please put you're comment as an answer and i will accept it. Commented Mar 31, 2014 at 15:08
  • @spons Done :) Commented Mar 31, 2014 at 15:24
  • just send new object {} instead of null .. tried following but they are returning "{}" .. I tried this -- new object {} Commented Oct 21, 2016 at 8:10

4 Answers 4

22

If you are building a RESTful service, and have nothing to return from the resource, I believe that it would be more correct to return 404 (Not Found) than a 200 (OK) response with an empty body.

Sign up to request clarification or add additional context in comments.

15 Comments

Wouldn't that get confusing? As in, you will get a 404 if the server is down as well. How can you tell the difference?
@andrewjboyd Yes, for a list I would return an empty list, but that's also conceptually a different kind of resource. A list is always there, but sometimes, it can be empty. A 'singleton' resource, on the other hand, either exists or it doesn't exist.
Wrong answer, you need to return 204. See my answer stackoverflow.com/a/32804589/631527
Don't just say "Wrong answer" unless you are 100.000% sure. It's really confusing to people trying to find a solution. The answer is RIGHT for RESTfull services. More over, your proposed solution is kinda wrong in all but a few situations (examples do not fit in a comment though)
Re-reading this discussion, I realise that I've implicitly interpreted the question as being about GET requests. With that interpretation, I believe my answer is correct. The OP isn't specific about this, though, so my interpretation may be incorrect. If, for example, we are discussing POST requests, I'd consider 404 to be clearly incorrect if the operation succeeds.
|
9

You can use a HttpMessageHandler to perform behaviour on all requests. The example below is one way to do it. Be warned though, I whipped this up very quickly and it probably has a bunch of edge case bugs, but it should give you the idea of how it can be done.

  public class NullJsonHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {

            var response = await base.SendAsync(request, cancellationToken);
            if (response.Content == null)
            {
                response.Content = new StringContent("{}");
            } else if (response.Content is ObjectContent)
            {
                var objectContent = (ObjectContent) response.Content;
                if (objectContent.Value == null)
                {
                    response.Content = new StringContent("{}");
                }

            }
            return response;
        }
    }

You can enable this handler by doing,

config.MessageHandlers.Add(new NullJsonHandler());

9 Comments

A great piece of code; I would still follow Mark Seemann's advice in the accepted answer and instead of returning the empty JSON object, return an HTTP 404 Not Found message.
@DavidKeaveny Sometimes a 204 would be more appropriate than a 404. Imagine the URL /AccidentsToday or /FailedTests
Those URIs imply the return of a collection, in which case I would be inclined to always a return an empty array with an HTTP 200.As for HTTP 204, I return 204s for succesful PUT and DELETE operations; so in my NullJsonHandler, I check the request method to make sure it is a GET.
@DavidKeaveny, I'm getting "Server cannot append header after HTTP headers have been sent". Tried 'ContinueWith' to no avail, could you shed some light.
@Samuel What version of Web API are you using? The alternative approach is to create a customized JsonFormatter. Derive from the standard one and override one of the standard methods.
|
0

Thanks to Darrel Miller, I for now use this solution.


WebApi messes with StringContent "{}" again in some environment, so serialize through HttpContent.

/// <summary>
/// Sends HTTP content as JSON
/// </summary>
/// <remarks>Thanks to Darrel Miller</remarks>
/// <seealso cref="http://www.bizcoder.com/returning-raw-json-content-from-asp-net-web-api"/>
public class JsonContent : HttpContent
{
    private readonly JToken jToken;

    public JsonContent(String json) { jToken = JObject.Parse(json); }

    public JsonContent(JToken value)
    {
        jToken = value;
        Headers.ContentType = new MediaTypeHeaderValue("application/json");
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        var jw = new JsonTextWriter(new StreamWriter(stream))
        {
            Formatting = Formatting.Indented
        };
        jToken.WriteTo(jw);
        jw.Flush();
        return Task.FromResult<object>(null);
    }

    protected override bool TryComputeLength(out long length)
    {
        length = -1;
        return false;
    }
}

Derived from OkResult to take advantage Ok() in ApiController

public class OkJsonPatchResult : OkResult
{
    readonly MediaTypeWithQualityHeaderValue acceptJson = new MediaTypeWithQualityHeaderValue("application/json");

    public OkJsonPatchResult(HttpRequestMessage request) : base(request) { }
    public OkJsonPatchResult(ApiController controller) : base(controller) { }

    public override Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var accept = Request.Headers.Accept;
        var jsonFormat = accept.Any(h => h.Equals(acceptJson));

        if (jsonFormat)
        {
            return Task.FromResult(ExecuteResult());
        }
        else
        {
            return base.ExecuteAsync(cancellationToken);
        }
    }

    public HttpResponseMessage ExecuteResult()
    {
        return new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new JsonContent("{}"),
            RequestMessage = Request
        };
    }
}

Override Ok() in ApiController

public class BaseApiController : ApiController
{
    protected override OkResult Ok()
    {
        return new OkJsonPatchResult(this);
    }
}

Comments

0

Maybe better solution is using Custom Message Handler.

A delegating handler can also skip the inner handler and directly create the response.

Custom Message Handler:

public class NullJsonHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {

            var updatedResponse = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = null
            };

            var response = await base.SendAsync(request, cancellationToken);

            if (response.Content == null)
            {
                response.Content = new StringContent("{}");
            }

            else if (response.Content is ObjectContent)
            {

                var contents = await response.Content.ReadAsStringAsync();

                if (contents.Contains("null"))
                {
                    contents = contents.Replace("null", "{}");
                }

                updatedResponse.Content = new StringContent(contents,Encoding.UTF8,"application/json");

            }

            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(updatedResponse);   
            return await tsc.Task;
        }
    }

Register the Handler:

In Global.asax file inside Application_Start() method register your Handler by adding below code.

GlobalConfiguration.Configuration.MessageHandlers.Add(new NullJsonHandler());

Now all the Asp.NET Web API Response which contains null will be replaced with empty Json body {}.

References:
- https://stackoverflow.com/a/22764608/2218697
- https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/http-message-handlers

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.