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 }
);
}
}
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.
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
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.
I have a VideoController and inside of it there are 2 methods like the following:
[Route("api/Video/{id:int}")]
public Video GetVideoByID(int id){ do something}
[Route("api/Video/{id}")]
public Video GetVideoByTitle(string id) {do something}
The WebApiConfig.cs is like the following:
public const string DEFAULT_ROUTE_NAME = "MyDefaultRoute";
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: DEFAULT_ROUTE_NAME,
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
So, when I comment out any of the method, the other one works, like if you totally comment out the 1st one, the 2nd method works, but both doesn't work when implemented. I used an Empty Web API template.
So any thoughts regarding why this is happening would be great.
You have to enable the attribute routing calling MapHttpAttributeRoutes during configuration.
Example:
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
...
}
I've tested it and worked for me correctly:
http://localhostapi/Video/1 // goes for the first method
http://localhostapi/Video/foo // goes for the second method
Change your routeTemplate from
routeTemplate: "api/{controller}/{id}",
to
routeTemplate: "api/{controller}/{action}/{id}",
So you'd call something like api/Video/GetVideoByTitle/x or api/Video/GetVideoByID/x
You may want to read this, under Routing Variations > Routing by Action Name