I am getting next message on my WebApi controller request:
Message: "The requested resource does not support http method 'GET'."
My action:
[HttpGet]
[System.Web.Http.AcceptVerbs("GET")]
public EntityDTO Get(long entityId)
{
return new Entity();
}
As you see, it already as AcceptVerbs and GET attributes.
My route in WebApiConfig:
config.Routes.MapHttpRoute(
name: "EntityRoute",
routeTemplate: "api/entity/{id}/{controller}",
defaults: new { id = RouteParameter.Optional, controller = "Entity" }
);
This route fires for sure, I know it because my controller's Constructor fires.
Any ideas?
PS. There are lots of similar issues on StackOverflow, but for those issues adding proper attribute usually helped.
The id parameter in your route does not match entityId in your method. They need to match.
Related
I work on my web api project.
I have two get action methods in controller.
Here the controller:
namespace Playground.Web.Controllers.API
{
[RoutePrefix("api/DamageEvent/{actionType}")]
public class DamageEventController : ApiController
{
#region API methods
[HttpGet]
public async Task<IHttpActionResult> GetDamageEvent(int damageEventId = 0)
{
//some logic
}
[HttpGet]
[Route("{ddd:int}")]
public async Task<IHttpActionResult> GetDamageEvent2(int ddd = 0)
{
//some logic
}
#endregion
}
}
Here WebApiConfig defenition:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.JsonFormatter.SerializerSettings.DateFormatString = "dd/MM/yyyy";
}
}
Here the example of URL in fiddler compose to trigger web api action:
http://localhost/playground/api/DamageEvent/GetDamageEvent2/?ddd=22
I expect that for the URL above the GetDamageEvent2 web api action will be fired. But instead GetDamageEvent action method is fired.
Why GetDamageEvent2 not fired? Any idea what do I am missing?
==============================Update================================
After I red answer from Nkosi
I made some changes to my code, I added to class WebApiConfig new route:
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
And here the changes in action type:
namespace Playground.Web.Controllers.API
{
[RoutePrefix("api/DamageEvent")]
public class DamageEventController : ApiController
{
#region API methods
[HttpGet]
[Route("GetDamageEvent/{damageEventId}")]
public async Task<IHttpActionResult> GetDamageEvent(int damageEventId = 0)
{
//some logic
}
[HttpGet]
[Route("GetDamageEvent2/{ddd}")]
public async Task<IHttpActionResult> GetDamageEvent2(int ddd = 0)
{
//some logic
}
#endregion
}
}
After I make the changes above the I tryed to fire the both actions and it worked.
But the problem now is when I try to call another actions in another controllers, For example:
http://localhost/playground/api/Contracts/1
I get 404 error.
So I guess the error occures because of the new route template.
So my question how can I fix the error above and to take the new route template into consideration only when the URI try to access to DamageEventController?
You are mixing attribute routing and convention based routing.
Nothing matches your RoutePrefix because there are no actions in the controller that has both a {actionType} and {ddd} templates.
But your stated URL...
api/DamageEvent/GetDamageEvent2/?ddd=22
...matches the DefaultApi convention based route for GetDamageEvent in the route table because it does not have a [RouteAttribute] and it defaults back the convention where...
api/{controller=DamageEvent}/{id=GetDamageEvent2/?ddd=22}
Take a look at Routing in ASP.NET Web API to understand the convention based routing.
and also Attribute Routing in ASP.NET Web API 2
Each entry in the routing table contains a route template. The default
route template for Web API is "api/{controller}/{id}". In this
template, "api" is a literal path segment, and {controller} and {id}
are placeholder variables.
When the Web API framework receives an HTTP request, it tries to match
the URI against one of the route templates in the routing table. If no
route matches, the client receives a 404 error. For example, the
following URIs match the default route:
/api/DamageEvent
/api/DamageEvent/1
/api/DamageEvent/GetDamageEvent2/?ddd=22
Once a matching route is found, Web API selects the controller and the
action:
To find the controller, Web API adds "Controller" to the value of the {controller} variable.
To find the action, Web API looks at the HTTP method, and then looks for an action whose name begins with that HTTP method name. For
example, with a GET request, Web API looks for an action that starts
with "Get...", such as "GetDamageEvent". This
convention applies only to GET, POST, PUT, and DELETE methods. You can
enable other HTTP methods by using attributes on your controller.
We’ll see an example of that later.
Other placeholder variables in the route template, such as {id}, are mapped to action parameters.
To get your stated route to work you need to update your route templates. Either the attribute route or add a new convention route to the route table
I am trying to create a WebAPI controller with multiple Get commands using the ActionName method. I successfully did this on another project, but have been having problems with this latest project and cannot see to understand why my knockout view model ajax call cannot find the specific URI.
WebApiConfig.cs:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Controller:
// GET api/lot
[ActionName("Default")]
public IEnumerable<DataObject> Get()
{
//...
}
// GET api/lot/Specific/5
[ActionName("Specific")]
public IEnumerable<DataObject> Get(int? data)
{
//...
}
// GET api/lot/5
public string Get(int id)
{
return "value";
}
My default action for GET works perfect but the Specific action continues to have this error when I attempt to call it from the view-model:
"Failed to load resource: the server responded with a status of 404 (Not Found)"
I added [HttpGet] next to the [ActionName("Specific")] and had the following error:
"GET http://localhost:57492/api/lot/Specific/1 404 (Not Found)"
I've tried several different things such as removing the int? data argument, but then when I attempt to build the project it tells me an existing function with same arguments already exists, even with different action names.
Ultimately I would like to have multiple Get(parameter) actions to call for interacting with my view-model.
I was able to solve this by adding the Route property with HttpGet and renaming all of my functions to unique names like GetAll(), GetSpecific(ind id), etc..
// GET api/lot
[HttpGet]
[Route("api/lot/GetAll")]
public IEnumerable<DataObject> GetAll()
{...}
// GET api/lot/GetSpecific/{id}
[HttpGet]
[Route("api/lot/GetSpecific/{id}")]
public IEnumerable<DataObject> GetSpecific(string id)
{
Adding a new route in config.Routes as below. Will this help? I didn't test this.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/specific/{data}",
defaults: new {
action = "Specific",
data = RouteParameter.Optional }
);
When I access to the swagger url: http://localhost:28483/swagger/ui/index, it generates this error:
500 : undefined http://localhost:28483/swagger/docs/v1
Any ideas?
UPDATED:
See this detail error in firebug:
Not supported by Swagger 2.0: Multiple operations
with path 'api/BimModel' and method 'GET'. See the config setting - \"ResolveConflictingActions\" for
a potential workaround
Swagger might be considering two actions as one operation (like in the case of this common scenario)...
GET api/Products
GET api/Products/{id}
It seems you can use attribute routing to fix this and use these attributes above your actions so swagger will recognize them separately.
[Route("api/Products")]
[Route("api/Products/{id:guid}")]
Have you tried enable this in you swagger config?
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
In the controller, it got two different GET operations and it is disallowed by Swagger.
I suggest to either have only single GET operation for each controller or modify the router in WebApiConfig
I had the same issue when mixing attribute routing with default routes.
When I removed the default route the problem went away. The downside is, without the default route defined, I had to add attribute routing to all my controllers.
So from my WebApiConfig I removed:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
And added attribute routing to my controller:
[Route("Session")] // Added this attribute
public async Task<IHttpActionResult> Get()
...
[Route("Session/{id}")] // Added this attribute
public async Task<IHttpActionResult> Get(int id)
In reality I use a [RoutePrefix("Session")] on my Controller and use [Route("")] on my methods, but the result should be the same.
I was getting this error due to the parameter names not matching between the Attribute Routing statement and the method signature.
[HttpGet("{id}")]
public IActionResult Get(string deviceNumber){
...
After changing "{id}" to "{deviceNumber}" it fixed the error.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I can't figure out how to write the method signature for a REST(ish) AJAX call in Aspnet WebAPI.
My route is recognised but I get a "No HTTP resource was found that matches the request URI..."
I try to do a REST(ish) call like
http://mysite.com/api/Project/42/Children
and my idea is to have the server return all children of project 42.
My route is:
config.Routes.MapHttpRoute(
name: "DefaultApiWithAction",
routeTemplate: "api/{controller}/{id}/Children",
defaults: new { action="Children"}
);
and my method signature is:
public class ProjectController : ApiController {
[HttpGet]
public IEnumerable<Project> Children(int projectID) {
...
Why isn't my method recognised?
I am also not sure I am doing the right "restish" thing here.
Modify your action parameter name from 'proejctID' to 'id'
public IEnumerable Children(int id)
I am setting up an ASP.NET MVC 4 Web API to accept requests from a 3rd party server, and I simply can't figure out how to set the route mappings.
Assume the 3rd party server expects to get responses for:
http://[my_server]/authorize?user=[user name]&session=[session token]&item=[item]
http://[my_server]/release?user=[user name]&session=[session token]
Alternatively, the requests can use a dedicated path, i.e.:
http://[my_server]/***api***/authorize?user=[user name]&session=[session token]&item=[item]
http://[my_server]/***api***/release?user=[user name]&session=[session token]
I would like to be able to support both alternatives.
Additional requests, following the more traditional /controller/id form, should be implemented too, but I'd like to focus on the above (I'm not even sure that Web API is the way to go here).
I have written the following controller:
public class MyController : ApiController
{
[HttpGet]
[ActionName("authorize")]
public string Authorize(string user, string session, string item)
{
...
// return "OK" or "DENY";
}
[HttpGet]
[ActionName("release")]
public string Release(string user, string session)
{
...
return "OK";
}
}
and tried everything I could find in SO and elsewhere in WebAppConfig.Register, but I keep getting a 404 error when I try the request in the browser:
http://localhost:22332/api/authorize?user=ury&session=token&item=an_item
My question is, what do I have to do - specifically in WebAppConfig.Register and in the controller - in order to serve the above requests (assuming my test URL is correct...)?
You're getting tripped up by Web API conventions for matching a controller name in a URL to a controller class.
If the name of your controller is "MyController", then the URL to request is:
http://localhost:22332/api/my/authorize?user=ury&session=token&item=an_item
To support a request w/o the "api" bit in the URL, simply add a second route definition in the Register method of WebApiConfig.cs. Since you're also using "actions" in your URL's, you'd need these two routes:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "RootWebApiRoute",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
If you want to omit the "api" prefix as well as the controller parameter, I think you need a different route definition. Something like:
public static void Register(HttpConfiguration config) {
config.Routes.MapHttpRoute(
name: "ActionOnlyRoute",
routeTemplate: "{action}",
defaults: new { controller = "My" }
);
}
Here, we'll only look for an "action" in the URL and route everything to your "MyController" class.
After a few hours working on this and with a lot of help from the Route Debugger - Thanks Phil Haack! - I've found both the problem and the solution.
The problem: Route mapping matching is ordered, and RouteTable.Routes, from which the app's route mapping is initialized, contains quite a few of them. The request pattern I was looking for also matched some of these mappings ("authorize" was matched as a controller, for example).
The solution: add "my" route mappings before the default mappings.
Yeah, right...
Since most operations are not supported on HttpRouteCollection, the resulting code is a bit ugly, but it works:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
routesCopy = new RouteBase[RouteTable.Routes.Count];
RouteTable.Routes.CopyTo(routesCopy, 0);
config.Routes.Clear();
config.Routes.MapHttpRoute(name: "AuthorizeWebApiRoute", routeTemplate: "authorize", defaults: new { controller = "My", action = "authorize" });
config.Routes.MapHttpRoute(name: "ReleaseWebApiRoute", routeTemplate: "release", defaults: new { controller = "My", action = "release" });
foreach (var route in routesCopy)
{
RouteTable.Routes.Add(route);
}
}
}
As long as "my" route mappings don't match the default mappings (I made them specific enough not to), I'm all good - I think...