8
votes

I have a web api, where the global configuration is configured to use: XmlMediaTypeFormatter

My problem is I wont to extend this web api with a new controller, that uses the JsonMediaTypeFormatter instead.

Is it possible to change the MediaTypeFormatter to JSON for only one API Controller class?

My problem is not returning JSON, I have accumplished this with returning HttpResponseMessage:

return new HttpResponseMessage
{
    Content = new ObjectContent<string>("Hello world", new JsonMediaTypeFormatter()),
    StatusCode = HttpStatusCode.OK
};

It's on the request I get the problem. If I have an object with two properties:

public class VMRegistrant 
{
    public int MerchantId { get; set; }
    public string Email { get; set; }
}

And my controller action takes the VMRegistrant as argument:

public HttpResponseMessage CreateRegistrant(VMRegistrant registrant)
{
    // Save registrant in db...
}

But the problem is when I call the action with JSON it fails.

3
Do you include "application/json" in the content-type header?Yuval Itzchakov
Yes and I am sure, just did a another check. Checked with fiddler.DNRN
Not sure this will work, but try using the [FromBody] attribute. Not sure it will work if the pipeline is configured only for xml.Yuval Itzchakov

3 Answers

7
votes

You can have your controller return an IHttpActionResult and use the extension method HttpRequestMessageExtensions.CreateResponse<T> and specify the formatter you want to use:

public IHttpActionResult Foo()
{
    var bar = new Bar { Message = "Hello" };
    return Request.CreateResponse(HttpStatusCode.OK, bar, new MediaTypeHeaderValue("application/json"));
}

Another possibility is to use the ApiController.Content method:

public IHttpActionResult Foo()
{
    var bar = new Bar { Message = "Hello" };
    return Content(HttpStatusCode.OK, bar, new JsonMediaTypeFormatter(), new MediaTypeHeaderValue("application/json"));
}

Edit:

One possibility is to read and deserialize the content yourself from the Request object via reading from the stream and using a JSON parser such as Json.NET to create the object from JSON:

public async Task<IHttpActionResult> FooAsync()
{
      var json = await Request.Content.ReadAsStringAsync();
      var content = JsonConvert.DeserializeObject<VMRegistrant>(json);
}
3
votes

Yes, it's possible to change the MediaTypeFormatters for only one class/controller. If you want to save and restore the default formatters you can follow these steps:

  • In the beginning of the request save old formatters
  • Clear the formatters collection
  • Add the desired formatter
  • At the end of the request copy back the old formatters

I think this is easily done by an ActionFilterAttribute:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class ChangeFormatterAttribute : ActionFilterAttribute
{
    private IEnumerable<MediaTypeFormatter> oldFormatters;
    private MediaTypeFormatter desiredFormatter;

    public ChangeFormatterAttribute(Type formatterType)
    {
        this.desiredFormatter = Activator.CreateInstance(formatterType) as MediaTypeFormatter;
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var formatters = actionContext.ControllerContext.Configuration.Formatters;

        oldFormatters = formatters.ToList();

        formatters.Clear();
        formatters.Add(desiredFormatter);

        base.OnActionExecuting(actionContext);
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        var formatters = actionExecutedContext.ActionContext.ControllerContext.Configuration.Formatters;

        formatters.Clear();
        formatters.AddRange(oldFormatters);

        base.OnActionExecuted(actionExecutedContext);
    }
}

And the usage:

[ChangeFormatterAttribute(typeof(JsonMediaTypeFormatter))]
public class HomeController : ApiController
{
    public string Get()
    {
        return "ok";
    }
}

// ...

[ChangeFormatterAttribute(typeof(XmlMediaTypeFormatter))]
public class ValuesController : ApiController
{
    public string Get()
    {
        return "ok";
    }
}
0
votes

Maybe you could have your media type formatter only accept the type that is handled by your controller:

public class Dog
{
    public string Name { get; set; }
}

public class DogMediaTypeFormatter : JsonMediaTypeFormatter
{
    public override bool CanReadType(Type type)
    {
        return type == typeof (Dog);
    }
}

Probably not the best solution though :I