Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 927
DNRN Avatar asked Oct 16 '15 10:10

DNRN


2 Answers

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";
    }
}
like image 38
Viktor Bahtev Avatar answered Sep 27 '22 18:09

Viktor Bahtev


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);
}
like image 50
Yuval Itzchakov Avatar answered Sep 27 '22 18:09

Yuval Itzchakov