How do I get Web API 2 to return JSON and no other content type?

4.5k views Asked by At

In the latest Web API 2, how do I configure it so that it will only return a reply if the Accept header is application/json? This API will only support json, if any other accept header is sent then an error must be thrown. There will be no xml and even no html interface.

If the client asks for xml or html or anything, we need to throw an error to let them know they used the wrong accept type. We must not mask this problem by replying with the correct json when they have requested a type that is not actually supported.

var request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/json";
var response = request.GetResponse();

And the json result is returned successfully. But if there is any other Accept then an error is returned

var request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/xml"; // or text/html or text/plain or anything
var response = request.GetResponse();

Returns HTTP 501 Not Implemented or similar http error code.

This question is not a duplicate of How do I get ASP.NET Web API to return JSON instead of XML using Chrome? - that question asks how to also return json. My question is how to only return json, and only if the client asks for json. If the client asks for any other type like xml or html, then an error is returned.

2

There are 2 answers

0
Tom W On

This page shows how to access content negotiation directly. You could conceivably instead pass some filtered subset of this.Configuration.Formatters containing only the desired formatters to IContentNegotiator.negotiate, like so:

ContentNegotiationResult result = negotiator.Negotiate(
        typeof(Product), this.Request, this.Configuration.Formatters.Where(/* some expression that excludes all but the json formatter */);

This looks quite clumsy and would be a lot of dull boilerplate, so Javad_Amiry's answer is probably better, but this is another option that might be useful in specific cases.

2
amiry jd On

You can clear all formatters except JSON:

configuration.Formatters.Clear();
configuration.Formatters.Add(new JsonMediaTypeFormatter());

Or you can change the default Web API’s content negotiation mechanism:

public class JsonContentNegotiator : IContentNegotiator
{
    private readonly JsonMediaTypeFormatter _jsonFormatter;

    public JsonContentNegotiator(JsonMediaTypeFormatter formatter) 
    {
        _jsonFormatter = formatter;    
    }

    public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
    {
        var result = new ContentNegotiationResult(_jsonFormatter, new MediaTypeHeaderValue("application/json"));
        return result;
    }
}

// in app_start:

var jsonFormatter = new JsonMediaTypeFormatter();
config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));

See the article.

UPDATE:

Well, if you want to return a HTTP error on non-json request, you can do it by implementing a custom IHttpModule for checking header. But, for self-host apps it won't work. So, it's better to use extend a custom DelegatingHandler. For example, you can use this one:

public class FilterJsonHeaderHandler : DelegatingHandler {
    protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken){
        if (request.Headers.Accept.All(a => a.MediaType == "application/json")){
            // if we have only application/json, so the pipeline continues
            return base.SendAsync(request, cancellationToken);
        }
        // otherwise, do whatever you want:
        var response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
        var completionSource = new TaskCompletionSource<HttpResponseMessage>();
        completionSource.SetResult(response);
        return completionSource.Task;
    }
}

and register it in app_start:

public static class WebApiConfig {
    public static void Register(HttpConfiguration config) {
        config.MessageHandlers.Add(new FilterJsonHeaderHandler());
        // your other settings...
    }
}

NOTE: the code is not tested. Please let me know if there is any error.