简体   繁体   中英

Two HTTP Post method with different parameters in one Web API controller

I'm trying to implement a Controller with more than one POST method in one controller. I have the following:

public class PatientController : ApiController
{
    [HttpGet]
    public IEnumerable<Patient> All() { ... }

    [HttpGet]
    public Patient ByIndex(int index) { ... }

    [HttpPost]
    public HttpResponseMessage Add([FromBody]Patient patient) { ... }
}

And i have a this on my routing:

GlobalConfiguration.Configuration.Routes.MapHttpRoute(
    "API_1",
    "{controller}/{index}",
    new { index = RouteParameter.Optional });

Everything works as expected :)

Now, i would like to add the following action:

    [HttpPost, ActionName("save")]
    public void Save(int not_used = -1) { ... }

Without adding anything to routing, i get the following error in to Fiddler: Multiple actions were found that match the request.

If i add this to my routing (as a second or first, doesn't matter):

GlobalConfiguration.Configuration.Routes.MapHttpRoute(
    "API_2",
    "{controller}/{action}/{not_used}",
    new { not_used = RouteParameter.Optional },
    new { action = "save|reset" }); // Action must be either save or reset

Ill get the same error in to Fiddler.

Is this even possible? Can i have more than one POST, with different (type) parameters, in one controller?

Your problem is that you have two methods: Save and Add , and both match your route API_1 . The fact that you have another route API_2 that could have matched if the url had been a little different doesn't matter: you have two matching methods for this route.

You've got a few options:

  1. Put the save method in a different controller, and for that controller always map action names.
  2. Ensure that Save doesn't match the default route. In particular, you've included an optional parameter in Save and that means it's OK to omit. If the parameter were non-optional, it wouldn't match the route.
  3. Change your architecture to use a message-based format; ie rather than distinguishing based on actions, simply pass one class and distinguish based on how that's been set (a little unusual in web api, but that's what ServiceStack does
  4. Change your routing to always include the action name.

I can't really say what's best without understanding your exact scenario better; Though personally I'd avoid the trickiness of getting all those arguments and simultaneous routes working on multiple actions - either always be explicit about the action, or address any possible message in code (ie options 3 or 4). Complex routes in the face of optional arguments are simply a pain.

It seems that i have to modify my routing...

GlobalConfiguration.Configuration.Routes.MapHttpRoute(
    name: "API_2",
    routeTemplate: "{controller}/{action}/{not_used}",
    defaults: new { not_used = "-1" },
    constraints: new { action = "save|reset" });

GlobalConfiguration.Configuration.Routes.MapHttpRoute(
    name: "API_1",
    routeTemplate: "{controller}/{action}/{index}",
    defaults: new { action = "EMPTY", index = RouteParameter.Optional });

...and add the ActionName-attribute to all methods:

[HttpGet, ActionName("EMPTY")]
public IEnumerable<Patient> All()

[HttpGet, ActionName("EMPTY")]
public Patient ByIndex(int index)

[HttpPost, ActionName("EMPTY")]
public HttpResponseMessage Add([FromBody]Patient patient)

[HttpPost, ActionName("save")]
public void Save(int not_used = -1)

After those modifications, i can call the save like this:

localhost:6850/Patient/save

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM