I am using routing in my WebApi Katana application. I have the following two route mappings that work fine. My question is, can I combine these into a single route mapping using optional parameters? I can’t see an obvious way to do it and keep the required functionality. I am new to this and may have missed a technique that my help me achieve this. If the routes have to stay this way then this isn’t a problem.
config.Routes.MapHttpRoute(
name: "UnRegister",
routeTemplate: "api/services/{serviceName}/{location}",
defaults: new {controller = "MyController", location = RouteParameter.Optional});
config.Routes.MapHttpRoute(
name: "UnRegister2",
routeTemplate: "api/services/{serviceName}/{instanceId}",
defaults: new { controller = "MyController" });
The required functionality is to unregister a service by supplying the following details:
Servicename
Servicename and location
Servicename and instanceId
In ASP.NET Web API 2 you can use attribute routing and you don't have to define all your routes that way with MapHttpRoute.
The explanation can be found here.
In your Owin Startup you have to enable the attribute routing using MapHttpAttributeRoutes:
public class Startup
{
public static void Configuration(IAppBuilder app)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
// Enable attribute based routing
config.MapHttpAttributeRoutes();
app.UseWebApi(config);
}
}
and your controller should look something like this:
[RoutePrefix("api/service")]
public class ServicesController : ApiController
{
[HttpGet]
[Route("{location}")]
public IHttpActionResult GetByLocation(string location)
{
return Ok();
}
[HttpGet]
[Route("{instanceId:int}")]
public IHttpActionResult GetByInstanceId(int instanceId)
{
return Ok();
}
}
As you can see I've used RoutePrefix to define the endpoint and route constraints to restrict parameters, as suggested in the article.
You can even create your own custom constraints.
The article suggest that you have to install the NuGet package Microsoft.AspNet.WebApi.WebHost.
That's not necessary aymore.
Related
I have a controller which works perfectly fine:
[Authorize]
[Route("api/Version")]
public class VersionController : ApiController
{
However if I omit the Route attribute in other controllers it doesnt work, when I go to: url/api/User or Users, I get a 404
[Authorize]
public class UserController : ApiController
{
my webappi config
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}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Services.Add(typeof(IExceptionLogger), new AiExceptionLogger());
}
}
my routeconfig
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
User Controller GetUsers
[HttpGet]
public async Task<IHttpActionResult> GetUsers()
{
You seem to be defining two different configuration classes that specify different route schemes in their methods:
In WebApiConfig.Register(...), you have routeTemplate: "api/{controller}/{action}/{id}";
In RouteConfig.RegisterRoutes(...), you specified url: "{controller}/{action}/{id}".
Please note that these routes overlap each other, so you have to be careful when employing these configurations in your application.
Regarding the VersionController and UserController, it seems that it is in fact the Route attribute that is defining your route.
In VersionController, if you specify [Route("api/Version")], you are correctly able to access /api/version. If you remove this, you may be able to access /version instead of /api/version, or are you not? (This may help understanding what configuration - WebApiConfig, RouteConfig or any - is used.
Likewise, in UserController, given that you don't specify [Route("api/User")], you may be able to access /user (without the /api prefix). Can you confirm this, please? On the other hand, if you were defining the Route attribute, then you should be able to access api/user.
I am assuming that you are already mapping your controllers to endpoints, since I understood that you are able to access api/version.
This documentation is pretty good on explaining Routing in MVC projects (in this case, for .NET Core), and it explians the multiple routes approach that perhaps you are trying to achieve with WebApiConfig and RouteConfig.
VS 2017, New, Project, C#, ASP.NET Web app, ASP.NET 4.5.2 Empty Templates.
Unchecked folder and reference for Webforms, MVC and WebAPI. Later added MS WebApi v5.4.2 via Nuget.
Manually added "Controllers" folder.
xController.cs:
namespace v1.MyApiCallTheirApi.api
{
public class xController : ApiController
{
// GET api/<controller>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
}
index.html:
<body>
<input type="button" value="Go" onclick="go()"/>
<script type="text/javascript">
function go()
{
alert("lafdla");
$.ajax({
type: "GET",
url: "/api/x",
success: alert("ok")
});
}
</script>
</body>
$.ajax call always returns 404. Already check this, that, and many others. So far my only suspect is it may need a Global.asax for routing config, but I assume after adding API it should auto-add some hidden routing for me.
Right now your application doesn't knows how routes are created. MVC cannot know automatically unless you provide the pointers as to where each type of code is located.
Hence two ways out here (using GlobalConfiguration instance):-
a) Convention based, use map route method. No other place requires any further change.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
b) Use attributes on controller methods to define actual routes. Then add the route attribute on each action method
config.MapHttpAttributeRoutes();
Over action
[Route('api/myentity/get')
public entity GetEntity() { }
As for adding the package, it only provides you the relevant dlls required for WebAPI, doesn't makes any more changes
You can define the default action of the controller also by the Action attribute [HttpGet("")].
You need to update your action:
// GET api/<controller>
[HttpGet("")]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
As a tip Postman is a nice tool to test your api requests, which is also recommendet by the .NET documentation, see here.
Summarize the WHY and SOLUTION of my problem:
Global.asax
API Routing registration via App_Start
Attribute and Convention routings can't mixed together
Once going Attribute you have to declare attribute for each method.
Although Api controller can be placed anywhere/folder and can be named anything without "Controller" suffix, it must have the "Controller" suffix for class, i.e. public class v1Controller : ApiController, just public class v1 : ApiController won't work.
Global.asax is optional but it's like _init() of a project, so is a must here because API needs routing. Whether this Api is invoked by in-project or out-of-project, as soon as it's hit the built-in App_Start init everything of the project.
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
API routing registration defined in a class typically in App_Start, convention name is WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Either this Attribute Routing
config.MapHttpAttributeRoutes();
// Or this conventional Routing, but not together.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Adding a new package from Nuget won't auto-config/add necessary items
to project.
MS WebApi is developed based on MVC routing, it's confused if MVC is needed, here is a good reading. Answer is No, you don't need MVC in order to do WebApi.
Question is regarding defining custom routes with the Route attribute.
I know that in the WebApiConfig class you always define the default route,
configuration.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
What I cannot get working is when I want to pass another parameter. I know I can do this (code below is defined underneath the default route listed above):
//configuration.Routes.MapHttpRoute(
// name: "GetBrandImagePaths",
// routeTemplate: "api/{controller}/{id}/{type}");
But I'd rather, instead of defining all these routes in the WebApiConfig file, use custom routing. However, if I do not have the commented out code above in the file, I get a 404. Thus leading me to believe the custom Route is not even being looked at.
public class HelperApiController : ApiController
{
[HttpGet]
[Route("api/helperapi/{id}/{type}")]
public string GetBrandImages(int id, string type)
{
.....
}
}
How can I have it so I can use routes defined in the WebApiConfig file, AND defining custom routes inside individual API controllers.
Note that this project is also a MVC project (not just WebApi). Is there something I'm missing, doing incorrectly etc? I know there's numerous posts out there defining how to pass multiple params, but I think my question is a little more specific on to why one works and not the other.
You need to call config.MapHttpAttributeRoutes().
This will parse all the Controller classes and derive the routes from the attributes.
I would not mix this with the standard routing.
Attribute Routing in ASP.NET Web API 2
Enabling Attribute Routing
To enable attribute routing, call MapHttpAttributeRoutes during
configuration. This extension method is defined in the
System.Web.Http.HttpConfigurationExtensions class.
using System.Web.Http;
namespace WebApplication
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
// Other Web API configuration not shown.
}
}
}
Attribute routing can be combined with convention-based routing. To
define convention-based routes, call the MapHttpRoute method.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Attribute routing.
config.MapHttpAttributeRoutes();
// Convention-based routing.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
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;
}
I've the following problem, my route attribute is not working.
I have the following action:
[HttpGet]
[Route("~api/admin/template/{fileName}")]
public HttpResponseMessage Template(string fileName)
{
return CreateHtmlResponse(fileName);
}
and i want to access the action like .../api/admin/template/login.html, so that Template get login.html passed as the file name.
But i alsways get: No HTTP resource was found that matches the request URI 'http://localhost:50121/api/admin/template/login.html'.
The following request works: /api/admin/template?fileName=login.html
Does anyone know, what i am doing wrong with my routing?
EDIT:
My route configuration
config.Routes.MapHttpRoute(
"API Default", "api/{controller}/{action}",
new { id = RouteParameter.Optional });
You have to call MapHttpAttributeRoutes() so that the Framework will be able to walk through your attributes and register the appropriate routes upon application start:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// you can add manual routes as well
//config.Routes.MapHttpRoute(...
}
}
See MSDN
Check your Route attribute's namespace. It should be System.Web.Http instead of System.Web.Mvc.
try adding a forward slash after the tilde
[HttpGet]
[Route("~/api/admin/template/{fileName}")]
public HttpResponseMessage Template(string fileName)
{
return CreateHtmlResponse(fileName);
}
Verify that you are using System.Web.Http.RouteAttribute and not System.Web.Mvc.RouteAttribute
Try this routing in your WebApiConfig
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
You have to add RoutePrefix.
With my Web API 2 project I had to add a [RoutePrefix("events")] to the controller for it to pickup the action route attribute.
In my case, I added Route("api/dashboard") to api controller.
Changed it to RoutePrefix("api/dashboard") . And it works perfectly.
Also you need config.MapHttpAttributeRoutes(); in webapiconfig.cs