When using the following routes:
config.Routes.MapHttpRoute(
name: "new_device",
routeTemplate: "api/v1/devices",
defaults: new { controller = "Devices", action = "new_device" }
);
config.Routes.MapHttpRoute(
name: "devices_list",
routeTemplate: "api/v1/devices",
defaults: new { controller = "Devices", action = "devices_list", httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
);
The controller looks as follows:
public class DevicesController : ApiController
{
[HttpPost]
[ResponseType(typeof(IHttpActionResult))]
[Route("api/v1/devices")]
[ActionName("new_device")]
[ValidateModel]
public IHttpActionResult NewDevice([System.Web.Http.FromBody] Device device )
{
...
}
[HttpGet]
[ResponseType(typeof(IHttpActionResult))]
[Route("api/v1/devices")]
[ActionName("devices_list")]
[ValidateModel]
public List<Device> GetAllDevices()
{
...
}
My expectation would be that the router would find the correct route based on the HttpMethod used since even it's using the same URI it is using a different HttpMethod.
But instead it fails with the following:
"Message": "The requested resource does not support http method 'GET'."
My guess is because it fins a match with the URI and then checks if the method if the same.
Is there a way to achieve using the same URI with different Http Method which is by the way REST guidelines? Am I missing something?
Ok , I check your whole code. I think you are trying to achieve the calls in complicated way.
Following code is for the configuration :
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/v1/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
and follwoing is your controller code :
public class DevicesController : ApiController
{
[HttpPost]
[ResponseType(typeof(IHttpActionResult))]
[ActionName("newDevice")]
public IHttpActionResult NewDevice([System.Web.Http.FromBody] Device device)
{
return null;
}
[HttpGet]
[ResponseType(typeof(IHttpActionResult))]
[ActionName("devices_list")]
public List<Device> GetAllDevices()
{
return null;
}
}
I removed ValidateModel. I think it's your custom attribute or somehow related with built in nuget package.
Anyways, execute the calls with Postman or any HTTP client tool. It should work , as it was working at my end with above mentioned code.
Example Calls:
https://localhost:44370/api/v1/devices/devices_list = > Get.
https://localhost:44370/api/v1/devices/newDevice => Post. Provide body as post call for the object.
I am trying to build out a new endpoint in API app that already has a lot of other endpoints working just fine. Not sure what I'm doing wrong. I am getting two errors:
Message: No HTTP resource was found that matches the request URI 'http://localhost:62342/api/VoiceMailStatus
and
MessageDetail: No action was found on the controller 'VoiceMailStatus' that matches the request.
Here's the controller:
public class VoiceMailStatusController : ControllerBase
{
[HttpPost]
[Route("api/VoiceMailStatus")]
public string VoiceMailStatus(string var)
{
...
}
}
And here's the route:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I'm using PostMan:
There are a LOT of threads here about both of these error messages. I've read many of them, but have yet to find a solution. One of them said to change this:
public string VoiceMailStatus(string var)
to this:
public string VoiceMailStatus(string var = "")
And while that did get the error to go away and I was able to get inside of the method while in debug, var was always just an empty string.
EDIT: GOT IT WORKING
In addition to adding [FromBody] as per Andrii Litvinov's answer, I also had to do one more thing. What had been this:
public string VoiceMailStatus(string var)
{
...
}
Is now this:
public string VoiceMailStatus([FromBody] VMStatus request)
{
...
}
And then VMStatus is just a small little class with a single string property:
public class VMStatus
{
public string var { get; set; }
}
Most likely you need to apply FromBody attribute to your parameter:
public string VoiceMailStatus([FromBody] string var)
If that does not help try to rename parameter to something else, e.g.: var1, because var is reserved work in C# and could cause some binding issues, but I doubt that's the case.
You probably want to add this to your controller since you have api in the route config:
[RoutePrefix("API/VoiceMailStatus")]
and then add this to your action:
[Route("VoiceMailStatus", Name = "VoiceMailStatus")]
This should tie the action to the url localhost/api/voicemailstatus/voicemailstatus
I have a working API with a different endpoint -- a different controller -- for each type of XML post data that I expect to receive. Now, the client wants us to use the same endpoint for everything. The only determination of direction is the content of the XML data -- basically by the name of the root element.
I'd like to be able to leverage as much of my existing work as possible, so I am trying to add a new controller "Router" which does exactly that -- redirects to the proper route based on the content received. I've tried some things but can't seem to get any traction.
If there is a better way to handle this, I would be open to hearing about it.
// WebApiConfig.cs
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
//routeTemplate: "Router",
constraints: null,
defaults: new { id = RouteParameter.Optional }
);
//In RouteConfig.cs -- is this redundant to specific MapRoute and MapHttpRoute?
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(name: "First", url: "First/{action}/{id}" );
routes.MapRoute(name: "Second", url: "Second/{action}/{id}" );
routes.MapRoute(name: "Third", url: "Third/{action}/{id}" );
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home",
action = "Index",
id = UrlParameter.Optional }
);
But at any rate, I do have my APi traffic hitting the RouterController
(and I will admit I'm not completely clear why it's working) but once
there, how do I go about rerouting based on the XML data posted?
public RouterController()
{
log.Debug("Inside RouterController");
}
public IHttpActionResult Post(object postdata)
{
// how to determine the postdata contents / XML structure to get the
// root element and then redirect to the "true" controller?
}
Several questions here: First, why the (apparent) redundancy in WebApiConfig.cs route definition and RouteConfig.cs routing? Second, what's the best way to recognize traffic by its content and route appropriately?
Thanks for your assistance. I'll admit to running fairly blind here, so any assistance is appreciated.
I think it's not a good idea to have some xml parsing logic in your routes (if it's even possible).
Based on you description I guess that you have many controller methods and models for them. You can keep them almost unchanged, maybe only making methods private.
public class First
{
public string Name { get; set; }
}
public class Second
{
public string Name { get; set; }
}
private void HandleFirst(First model)
{
// Your existing code
}
private void HandleSecond(Second second)
{
// Your existing code
}
My suggestion is to have one controller method (how your client wants) that will do the following things:
Accept raw request data
Parse XML and get know the type
Create model by deserializing XML
Call needed method
That is how it might look:
public void Post(HttpRequestMessage request)
{
using (var xmlReader = XmlReader.Create(request.Content.ReadAsStreamAsync().Result))
{
xmlReader.MoveToContent();
switch (xmlReader.Name)
{
case "First":
HandleFirst(Deserialize<First>(xmlReader));
break;
case "Second":
HandleSecond(Deserialize<Second>(xmlReader));
break;
default:
throw new NotSupportedException();
}
}
}
private T Deserialize<T>(XmlReader xmlReader)
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(xmlReader);
}
In this case you can keep you routing configuration very simple. All you need is to use default one. You might also take a closer look at attribute routing that was introduced in Web.API 2.
I've got this intresting situation where I have a baseController for webApi and have tracked the issue down to this. If I have a base controller that has an input parameter type Object it will end up with http 500 Multiple actions where found...
And this is in a default empty mvc 4.0 project for vs 2012 using IISExpress
public class BaseSecureApi : ApiController
{
public string Wtf(object ohMyGodThisIsSilly)
{
return null;
}
}
And the controller
public class Default1Controller : BaseSecureApi
{
// GET api/default1
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// POST api/default1
[HttpPost]
public string Post([FromBody]string value)
{
return "Postmyvalue:" + value;
}
}
And a route
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Now why is the routing going haywire.
IRL the base class returns a httpResponse based on who is logged on and if the result should be encrypted or not.
From the routing-and-action-selection article on the ASP.NET site:
HTTP Methods. The framework only chooses actions that match the HTTP method of the request, determined as follows:
You can specify the HTTP method with an attribute: AcceptVerbs, HttpDelete, HttpGet, HttpHead, HttpOptions, HttpPatch, HttpPost, or HttpPut.
Otherwise, if the name of the controller method starts with "Get", "Post", "Put", "Delete", "Head", "Options", or "Patch", then by convention the action supports that HTTP method.
If none of the above, the method supports POST.
This means that the Wtf method will support POST because it is a public method, with no attributes and it does not start with any of the conventional words.
If you make the Wtf method protected and it will not be called by the Web api, you can still call it from the methods of your derived classes where you need to.
I keep getting this error when I try to have 2 "Get" methods
Multiple actions were found that match the request: webapi
I been looking around at the other similar questions about this on stack but I don't get it.
I have 2 different names and using the "HttpGet" attribute
[HttpGet]
public HttpResponseMessage Summary(MyVm vm)
{
return null;
}
[HttpGet]
public HttpResponseMessage FullDetails()
{
return null;
}
Your route map is probably something like this in WebApiConfig.cs:
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
But in order to have multiple actions with the same http method you need to provide webapi with more information via the route like so:
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });
Notice that the routeTemplate now includes an action. Lots more info here: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
Update:
Alright, now that I think I understand what you are after here is another take at this:
Perhaps you don't need the action url parameter and should describe the contents that you are after in another way. Since you are saying that the methods are returning data from the same entity then just let the parameters do the describing for you.
For example your two methods could be turned into:
public HttpResponseMessage Get()
{
return null;
}
public HttpResponseMessage Get(MyVm vm)
{
return null;
}
What kind of data are you passing in the MyVm object? If you are able to just pass variables through the URI, I would suggest going that route. Otherwise, you'll need to send the object in the body of the request and that isn't very HTTP of you when doing a GET (it works though, just use [FromBody] infront of MyVm).
Hopefully this illustrates that you can have multiple GET methods in a single controller without using the action name or even the [HttpGet] attribute.
Update as of Web API 2.
With this API config in your WebApiConfig.cs file:
public static void Register(HttpConfiguration config)
{
//// Web API routes
config.MapHttpAttributeRoutes(); //Don't miss this
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
}
You can route our controller like this:
[Route("api/ControllerName/Summary")]
[HttpGet]
public HttpResponseMessage Summary(MyVm vm)
{
return null;
}
[Route("api/ControllerName/FullDetails")]
[HttpGet]
public HttpResponseMessage FullDetails()
{
return null;
}
Where ControllerName is the name of your controller (without "controller"). This will allow you to get each action with the route detailed above.
For further reading: http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
In Web API (by default) methods are chosen based on a combination of HTTP method and route values.
MyVm looks like a complex object, read by formatter from the body so you have two identical methods in terms of route data (since neither of them has any parameters from the route) - which makes it impossible for the dispatcher (IHttpActionSelector) to match the appropriate one.
You need to differ them by either querystring or route parameter to resolve ambiguity.
After a lot of searching the web and trying to find the most suitable form for routing map
if have found the following
config.Routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id =RouteParameter.Optional }, new { id = #"\d+" });
config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
These mapping applying to both action name mapping and basic http convention (GET,POST,PUT,DELETE)
This is the answer for everyone who knows everything is correct and has checked 50 times.....
Make sure you are not repeatedly looking at RouteConfig.cs.
The file you want to edit is named WebApiConfig.cs
Also, it should probably look exactly like this:
using System.Web.Http;
namespace My.Epic.Website
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// api/Country/WithStates
config.Routes.MapHttpRoute(
name: "ControllerAndActionOnly",
routeTemplate: "api/{controller}/{action}",
defaults: new { },
constraints: new { action = #"^[a-zA-Z]+([\s][a-zA-Z]+)*$" });
config.Routes.MapHttpRoute(
name: "DefaultActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
I could have saved myself about 3 hours.
It might be possible that your webmethods are being resolved to the same url. Have a look at the following link :-
http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
So, you might need to add your methodname to your routing table.
Without using actions the options would be:
move one of the methods to a different controller, so that they don't clash.
use just one method that takes the param, and if it's null call the other method from your code.
This solution worked for me.
Please place Route2 first in WebApiConfig. Also Add HttpGet and HttpPost before each method and include controller name and method name in the url.
WebApiConfig =>
config.Routes.MapHttpRoute(
name: "MapByAction",
routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
Controller =>
public class ValuesController : ApiController
{
[HttpPost]
public string GetCustomer([FromBody] RequestModel req)
{
return "Customer";
}
[HttpPost]
public string GetCustomerList([FromBody] RequestModel req)
{
return "Customer List";
}
}
Url =>
http://localhost:7050/api/Values/GetCustomer
http://localhost:7050/api/Values/GetCustomerList
I found that that when I have two Get methods, one parameterless and one with a complex type as a parameter that I got the same error. I solved this by adding a dummy parameter of type int, named Id, as my first parameter, followed by my complex type parameter. I then added the complex type parameter to the route template. The following worked for me.
First get:
public IEnumerable<SearchItem> Get()
{
...
}
Second get:
public IEnumerable<SearchItem> Get(int id, [FromUri] List<string> layers)
{
...
}
WebApiConfig:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/{layers}",
defaults: new { id = RouteParameter.Optional, layers RouteParameter.Optional }
);
It is possible due to using MVC controller instead of Web API controller.
Check the namespace in Web API controller it should be as following
using System.Net;
using System.Net.Http;
using System.Web.Http;
If the namespace are as following then it is give above error in web api controller method calling
using System.Web;
using System.Web.Mvc;
Please check you have two methods which has the different name and same parameters.
If so please delete any of the method and try.
I've stumbled upon this problem while trying to augment my WebAPI controllers with extra actions.
Assume you would have
public IEnumerable<string> Get()
{
return this.Repository.GetAll();
}
[HttpGet]
public void ReSeed()
{
// Your custom action here
}
There are now two methods that satisfy the request for /api/controller which triggers the problem described by TS.
I didn't want to add "dummy" parameters to my additional actions so I looked into default actions and came up with:
[ActionName("builtin")]
public IEnumerable<string> Get()
{
return this.Repository.GetAll();
}
for the first method in combination with the "dual" route binding:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { action = "builtin", id = RouteParameter.Optional },
constraints: new { id = #"\d+" });
config.Routes.MapHttpRoute(
name: "CustomActionApi",
routeTemplate: "api/{controller}/{action}");
Note that even though there is no "action" parameter in the first route template apparently you can still configure a default action allowing us to separate the routing of the "normal" WebAPI calls and the calls to the extra action.
In my Case Everything was right
1) Web Config was configured properly
2) Route prefix and Route attributes were proper
Still i was getting the error. In my Case "Route" attribute (by pressing F12) was point to System.Web.MVc but not System.Web.Http which caused the issue.
You can add [Route("api/[controller]/[action]")] to your controller class.
[Route("api/[controller]/[action]")]
[ApiController]
public class MySuperController : ControllerBase
{
...
}
I know it is an old question, but sometimes, when you use service resources like from AngularJS to connect to WebAPI, make sure you are using the correct route, other wise this error happens.
Make sure you do NOT decorate your Controller methods for the default GET|PUT|POST|DELETE actions with [HttpPost/Put/Get/Delete] attribute. I had added this attibute to my vanilla Post controller action and it caused a 404.
Hope this helps someone as it can be very frustrating and bring progress to a halt.
For example => TestController
[HttpGet]
public string TestMethod(int arg0)
{
return "";
}
[HttpGet]
public string TestMethod2(string arg0)
{
return "";
}
[HttpGet]
public string TestMethod3(int arg0,string arg1)
{
return "";
}
If you can only change WebApiConfig.cs file.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/",
defaults: null
);
Thats it :)
And Result :
Have you tried like:
[HttpGet("Summary")]
public HttpResponseMessage Summary(MyVm vm)
{
return null;
}
[HttpGet("FullDetails")]
public HttpResponseMessage FullDetails()
{
return null;
}