0

I'm learning Web Api and have bumped into the following issue. Similar to this question.

My exceptions are still returning as XML in Chrome, but JSON in IE. The exceptions are thrown as XML when inheriting from the ODataController but are correctly thrown as JSON when inheriting from ApiController.

My WebApiConfig class:

public static void Register(HttpConfiguration config)
{
    // Web API configuration and services
    config.Filters.Add(new NotImplementedExceptionFilterAttribute());
    config.Filters.Add(new MethodAttributeExceptionHandling());

    config.Services.Replace(typeof(IHttpActionInvoker), new CustomApiControllerActionInvoker());

    // Web API routes
    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<EventType>("EventTypes"); 

    config.Routes.MapODataRoute("ODataRoutes", "api", builder.GetEdmModel());

    //Route Configuration
    config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}"
    );


    var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
    config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);

}

I have a CustomApiControllerActionInvoker verbatim from: http://www.codeproject.com/Articles/733512/Exception-Handling-in-WebAPI

My TestController inheriting from ApiController:

public HttpResponseMessage GetGlobalErrorMessage()
{
    int i = 0;
    var val = 10 / i;
    return new
        HttpResponseMessage(HttpStatusCode.OK);
}

returns JSON in Chrome:

{
"Message": "Oops some internal Exception. Please contact your administrator",
"ErrorCode": 500
}

However, if I inherit from ODataController with similar code:

[Queryable]
public IQueryable<EventType> Get()
{
    int i = 0;
    var val = 10 / i;
    return _repo.EventTypes();
}

I'm returned XML from the exception in Chrome:

<m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<m:code/>
<m:message xml:lang="en-US">
Oops some internal Exception. Please contact your administrator
</m:message>
</m:error>

Summary:

How do I force consistent content types for payloads as well as exceptions, still allowing flexibility for native content negotiation using OData Web Api?

Community
  • 1
  • 1
Jim
  • 563
  • 1
  • 5
  • 19

2 Answers2

1

As @Kiran said above, here is a working example

[ODataFormattingOnlyJSON]
public class BaseODataController
    : ODataController
{
}
public class ODataFormattingOnlyJSONAttribute : ODataFormattingAttribute, IControllerConfiguration
{
    void IControllerConfiguration.Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
    {
        base.Initialize(controllerSettings, controllerDescriptor);
        //remove all the dirty non json formatters
        var remove = controllerSettings.Formatters.Where(m => !m.SupportedMediaTypes.Any(t => t.MediaType.IndexOf("json", StringComparison.OrdinalIgnoreCase) >= 0)).ToArray();
        foreach (var item in remove)
        {
            controllerSettings.Formatters.Remove(item);
        }
    }
}

Then all your controllers inherit from BaseODataController

DATEx2
  • 3,585
  • 1
  • 24
  • 26
0

The difference in response formats between these browsers is due to the kind of Accept headers that they send. Depending on Accept header, Web API runs content-negotiation and returns the appropriate response. ODataController is special in that it does not use the formatters that regular ApiController uses. Instead ODataController has a Per-Controller Configuration attribute called ODataFormattingAttribute from which you can derive to build a custom list of formatters through its CreateODataFormatters method and use this attribute on all your ODataControllers.

Kiran
  • 56,921
  • 15
  • 176
  • 161
  • What I fail to understand is: if I have the XmlFormatter removing the application/xml media type (in the Register method of the WebApiConfig class) allowing all non-exceptioned requests to be returned as JSON (the next accepted media type in line for Chrome's Accept Header); why then does the response return XML for exceptions? If it succeeds, I receive the expected JSON; if it fails, I receive XML. It's being inconsistent even when the Accept Headers do not change. – Jim May 23 '14 at 19:07
  • OASIS only accepts Json as the part of the OData v4 protocol, and the latest WebApi OData only supports this format accordingly out of box. please upgrade to the latest version if possible. Nuget source: http://www.myget.org/F/aspnetwebstacknightly/api/v2, package id: Microsoft.AspNet.OData. – Tan Jinfu May 23 '14 at 22:45
  • I have derived from `ODataFormattingAttribute` but the `CreateOdataFormatters` method is not being called at all. @TanJinfu, your comment is rather useless, since I am already using WebAPI 2.2 – DATEx2 Nov 26 '14 at 15:01