2

I don't know if this is the right way to approach something, but I'm hoping it is. The example below is a heavy controller and is absolutely the wrong approach, but it get's the idea of what I'm looking for across.

public class PeopleController : Controller
{
    public ActionResult List(string? api)
    {
        MyViewModel Model = new MyViewModel();

        if (api == "json") {

            // I'd like to return the Model as JSON

        } else if (api == "XML") {

            // I'd like to return the Model as XML

        } else {

            return View(Model);
        }
    }
}

Now what I need to be able to do is return the Model to the View if it's being requested like this:

http://example.com/People/List

But I'd like it to output JSON if it's requested like this:

http://example.com/People/List/?api=json

Or output XML if it's requested like this:

http://example.com/People/List/?api=xml

Is this just plain wrong? If not, what is the best approach to achieve this?

I was thinking of achieving it with a Custom MultiPurposeResult that could do all the filtering for me and then return it as this

public class PeopleController : Controller
{
    public MultiPurposeResult List(string? api)
    {
        MyViewModel Model = new MyViewModel();
        return MultiPurpose(Model);      }
    }
}
double-beep
  • 5,031
  • 17
  • 33
  • 41
Chase Florell
  • 46,378
  • 57
  • 186
  • 376

2 Answers2

5

Agree with @Matt. Don't explicitly ask for the response type, infer it from the contentType in the request, which is more RESTful.

For example, created a basic enum type to encapsulate the response types you want:

public enum RestfulResultType
{
   Json,
   Html,
   Xml
}

Then create a custom model binder than sets this property in your action, depending on the content type.

Then your controller could look like this:

public ActionResult List(RestfulResultType resultType)
{
   var data = repo.GetSomeData();

   switch (resultType)
   {
      case RestfulResultType.Json:
         return Json(data);
      case RestfulResultType.Xml:
         return XmlResult(data); // MvcContrib
      case RestfulResultType.Html:
         return View(data);
   }
}

If you need any more customization than the regular helpers provide, then create custom ActionResult's.

You can leave the return type as ActionResult - that's the point, so that the controller can return different formats.

ResfulResultTypeModelBinder.cs:

public class ResfulResultTypeModelBinder: IModelBinder
{
   public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   {
     if (controllerContext.HttpContext.Request.ContentType == "application/json")
        return RestfulResultType.Json;
     // other formats, etc.
   }
}

Global.asax:

ModelBinders.Binders.Add(typeof(RestfulResultType), new RestfulResultTypeModelBinder());
RPM1984
  • 72,246
  • 58
  • 225
  • 350
  • I like this, except I don't want to write a switch for each ActionResult (I have dozens throughout my app)... I think I'll still create a custom return type. Could you elaborate a little on the `enum RestfulResultType`? – Chase Florell Sep 30 '11 at 01:32
  • @rock - nothing much to it. Just an alternative to magic strings. If not a switch, then how can you return different types? What do you mean "each ActionResult"? Do you mean each "result type" (e.g json, xml). How can there be dozens? – RPM1984 Sep 30 '11 at 01:47
  • In my restful app I've got dozens of Actions... (sorry, not ActionResults). I don't want to write a switch into each Action for each Controller, I'd rather write a Custom Result Type, and write the switch into it. – Chase Florell Sep 30 '11 at 01:49
  • As for the enum, how does it get set? Do I still pass the format in the querystring? Sorry, I don't know enough about the actual Http request. – Chase Florell Sep 30 '11 at 01:51
  • @rock - ahh, i see. Well, why not create a base controller, with a helper called `public ActionResult ResfulResult (object model)`, then you can access the content type from the http context in there, and do the switch. Then just do `return ResfulResult(data)` in your action. – RPM1984 Sep 30 '11 at 01:53
  • @rock - the enum get's set be the custom model binder, either via a global one, or explicitly set in the action (global better, to reduce code) – RPM1984 Sep 30 '11 at 01:54
  • 1
    i added an example for the model binder – RPM1984 Sep 30 '11 at 01:57
1

You can create a custom MultiPurposeResult but I personally would lose the string? api from the method signature, instead have the MultiPurpose look for the presence of Request["format"] and then make the determination of what format to possible output the results in. Since the format doesn't nessecarily have anything to do with the ActionResult but more the format of the response.

Chase Florell
  • 46,378
  • 57
  • 186
  • 376
Matt
  • 3,638
  • 2
  • 26
  • 33