4

I would like to route requests to different actions based on the value of a particular json parameter.

For example, given the following json data:

{ type: "type1", type1data: "type1value" } 

and

{ type: "type2", type2data: "type2value" } 

I'd like to be able to have 2 different actions on my ApiController:

void CreateType1(string type1data) { // ... } void CreateType2(string type2data) { //... } 

How can something like this be done?

Update:

I'd like the same URL if possible. Something like /objects/create.

3
  • do you want change route from json data or type? Commented May 13, 2013 at 14:43
  • Preferably json data ('type' being the parameter to base the route on) Commented May 13, 2013 at 14:49
  • you can add one action eg create and switch type and other methods or you can add same action Create (SameModel<E> myodel) E:your type and SameModel have a property like public X myModel { get; set; } Commented May 13, 2013 at 15:17

2 Answers 2

9

I'd much rather use a custom ApiControllerActionSelector.

public class MyActionSelector : ApiControllerActionSelector { public override HttpActionDescriptor SelectAction( HttpControllerContext context) { HttpMessageContent requestContent = new HttpMessageContent( context.Request); var json = requestContent.HttpRequestMessage.Content .ReadAsStringAsync().Result; string type = (string)JObject.Parse(json)["type"]; var actionMethod = context.ControllerDescriptor.ControllerType .GetMethods(BindingFlags.Instance | BindingFlags.Public) .FirstOrDefault(m => m.Name == "Create" + type); if (actionMethod != null) { return new ReflectedHttpActionDescriptor( context.ControllerDescriptor, actionMethod); } return base.SelectAction(context); } } 

Here is the model. I gave it a weird name of Abc.

public class Abc { public string Type { get; set; } public string Type1Data { get; set; } } 

Here is the action method.

public void Createtype1(Abc a) { } 

Finally, plug-in the action selector.

public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Services.Replace(typeof(IHttpActionSelector), new MyActionSelector()); } } 

If you now POST to http://localhost:port/api/yourapicontroller, depending on the value in type field in JSON, the action method Create* will be selected.

Sign up to request clarification or add additional context in comments.

2 Comments

I like the sounds of this approach.
Way better than mine! More extensible!
0

Something like this. For simplicity, this example is not a proper async implementation.

using System.Net.Http; using System.Web.Http; namespace MyApi.Controllers { public class MyType { public string type {get; set;} } public class MyType1 { public string type {get; set;} public string type1data {get; set;} } public class MyType2 { public string type {get; set;} public string type2data {get; set;} } public class ObjectsController : ApiController { // // api/objects/create // public void Create() { // buffer the content to allow the dual reads below Request.Content.LoadIntoBufferAsync().Wait(); // get the body as a object so we can access its type property MyType postedObject = Request.Content.ReadAsAsync<MyType>().Result; // decide switch (postedObject.type) { case "type1": CreateType1(Request.Content.ReadAsAsync<MyType1>().Result.type1data); break; case "type2": CreateType2(Request.Content.ReadAsAsync<MyType2>().Result.type2data); break; default: // 400 Bad Request would be best, I think. break; } } private void CreateType1(string type1data) { // ... } private void CreateType2(string type2data) { // ... } } } 

5 Comments

My type1data parameters were meant to represent values inside the json data, not just the json data in string form. Imagine a model class instead.
Though you've accepted Badri's much better answer, I have corrected mine in response to your comment. Is that what you meant?
Almost. The CreateType1 function should look like CreateType1(MyType1 model) { ... }
Sorry, I was trying to conform to the CreateType1 method in your question.
I understand the confusion. My updated signature for CreateType1 accommodates additional members on the model structure than just type1data.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.