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 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'm having some troubles configuring WebApi routes in an asp.net WebForms app.
In the global.asax file, I have something like this:
void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
I have another file that has this code:
using System.Web.Http;
namespace WebConfig
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}",
defaults: new { id = System.Web.Http.RouteParameter.Optional });
}
}
}
Then, in the root, I have a folder called api and in that folder I have a file that contains a class that looks somewhat like that:
public class MyController : ApiController
{
[Route("MyController")]
[HttpPost]
public HttpResponseMessage Post([FromBody]string value)
{....}
And then finally, in my client code, I have this ajax call:
$.ajax({
url: "/api/MyController",
contentType: "application/json; charset=utf-8",
type: "POST",
dataType: "json",
cache: "false",
data: someData});
However, when I run this code, all I get is a 404 error. What do I need to change in the routing mechanism to make this work?
I think others were very close. Try this:
[RoutePrefix("api")] // or maybe "api/", can't recall OTTOMH...
public class MyController : ApiController
{
[Route("MyController")]
[HttpPost]
public HttpResponseMessage Post([FromBody]string value)
and then request /api/MyController
If this doesn't work, use RouteDebugger to analyze your routes, and why it rejects a match. Amend your question with what you see in RouteDebugger so I can trace down what is not matching.
Also, you may need to call MapHttpAttributeRoutes in your Register function - but not sure about that.
Edit
Now that I'm looking at it again, I think I see more issues with it.
First, let's start with the template:
Here is what you have (from your question):
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}",
defaults: new { id = System.Web.Http.RouteParameter.Optional });
The odd part here is that your template does not have {id} segment, but is defined as optional. Sounds like it is missing from the template, and the route should be changed to:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional });
Note that you also removed default action - I'm not sure if MVC automatically uses convention method of locating a method called Post, but I suppose it does.
Second problem is that your method signature is (again from your question):
public HttpResponseMessage Post([FromBody]string value)
It defines Post that takes in a parameter named value, whereas your route defines parameter named id. Hence there is another mismatch. You can rename the variable or decorate (see below). Also, {id} is marked optional, but I believe (and here I don't remember exactly OTTOMH) you need to give default value to to value for those cases where {id} is not supplied, and so combining together:
public HttpResponseMessage Post([FromBody(Name="id")]string value = null)
This should fix it, although there may be more issues... but let's start with these.
Change url: "/api/MyController" to url: "/api/My"
Have you considered using Route attributes instead? I find them to be easier than route configs.
public class MyController : ApiController
{
[Authorize]
[Route("MyController")]
[HttpPost]
public HttpResponseMessage Post([FromBody]string value)
{....}
}
perhaps you could additionally try configuring your routes inside of a dedicated RouteConfig class that mvc4 also supports. My route config setups look like this:
/// <summary>
/// The route config class
/// </summary>
public class RouteConfig
{
/// <summary>
/// Registers the routes.
/// </summary>
/// <param name="routes">The routes.</param>
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 }
);
}
}
Use [RoutePrefix] on your Controller Class
[RoutePrefix("api/My")]
public class MyController : ApiController
{
[Route("MyController")]
[HttpPost]
public HttpResponseMessage Post([FromBody]string value)
{....}
}
Then you should be able to navigate to api/My/MyController
Change [Route("MyController")] to [Route("api/MyController")].
It will then work as you expect it to.
Your RouteAttribute overrides config settings, so you have two options:
1) remove RouteAttribute and use URL: /api/my
2) keep RouteAttribute and use URL /MyController (in this case config settings ignored)
From comments i see, that you want to navigate to: api/MyController. So third option not tested, but I think it might work:
3) [Route("api/MyController")]
Ok, after over 2 weeks of wondering on and off why something seemingly simple wasn't working, I finally figured it out!!!!!
The problem is in this sentence:
Then, in the root, I have a folder called api and in that folder I
have a file that contains ... the web API controller file
The solution turned out to be a 10-second fix:
Put the MyWebAPIController.cs file in the App_Code folder
The way I had it, the .cs file was in a root directory called /api because I had mistakenly thought that if you wanted to call the webservice from the client /api/MyController that you needed to create a directory that matched that path and then put the controller in that directory. Instead, you have to put the controller file in the App_Code directory.
LB2's answer did help me with the attributes, use both [RoutePrefix] and [Route]:
[RoutePrefix("api")]
public class MyController : ApiController
{
[Route("MyController")]
public HttpResponseMessage Post([FromBody]string value)
and then you can call it with this:
$.ajax({
url: "/api/MyController",
type:POST,
...});
Thank you all for looking into this issue!!
My challenges were slightly different. My setup is asp.net webforms project and using VS2013. I wanted to introduce Web Api. The curve ball was my web forms implementation is in VB.net.
I wanted to write my APIs in C# so I had to enable codeSubDirectories option via web.config.
<codeSubDirectories>
<add directoryName="VBCode"/>
<add directoryName="CSCode"/>
</codeSubDirectories>
I created two folders (VBCode and CSCode) under App_Code folder respectively.
I then moved all my vb.net classes to VBCode sub directory.
Created UserController.cs under CSCode and wrote regular web api code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace WebAPIDemo
{
public class UserController : ApiController
{
static IUserRepository _repo = new UserRepository();
// GET api/<controller>
public IEnumerable<Users> GetAllUsers()
{
return _repo.GetAllUsers();
}
// GET api/<controller>/5
public IHttpActionResult GetUserById(int id)
{
return Json(_repo.GetUserById(id));
}
Modified the global.asax
<%# Application Language="VB" %>
<%# Import Namespace="System.Web.Routing" %>
<%# Import Namespace="System.Web.Http" %>
<script RunAt="server">
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' Code that runs on application startup
RouteTable.Routes.MapHttpRoute(
"defaultApi",
"api/{controller}/{action}/{id}",
defaults:=New With {.id = System.Web.Http.RouteParameter.Optional})
End Sub
The api call worked with and without this decorator [RoutePrefix("api")].
Took me several hours to figure out this implementation.
Call 1:
http://localhost:60203/api/user/getuserbyid/2
Result:
{"Id":2,"FirstName":"Alan","LastName":"Wake","Company":"XYZ Corp","Email":"alan#xyz.com","PhoneNo":"64649879"}
Call 2:
http://localhost:60203/api/user/getallusers
Result:
<ArrayOfUsers xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/WebAPIDemo">
<Users>
<Company>ABC</Company>
<Email>madhur#abc.com</Email>
<FirstName>Madhur</FirstName>
<Id>1</Id>
<LastName>Kapoor</LastName>
<PhoneNo>65431546</PhoneNo>
</Users>
<Users>
<Company>XYZ Corp</Company>
<Email>alan#xyz.com</Email>
<FirstName>Alan</FirstName>
<Id>2</Id>
<LastName>Wake</LastName>
<PhoneNo>64649879</PhoneNo>
</Users>
</ArrayOfUsers>
I am creating a WebApi2 service, and one of the methods I want to implement represents an HTTP GET from an object within an internal tree structure - so the request would be along the lines of:
GET /values/path/path/to/object/in/tree
So I would want my method to receive "path/to/object/in/tree".
However, I just get a 404 when I run this, and it's interesting that I get a 404 that is different looking to the standard IIS 404. It's titled 'Server Error in '/' Application.', whereas the one for a completely invalid resource is titled 'HTTP Error 404.0 - Not Found'.
I am playing around with the default template to try and see if I can get this to work, hence the similarity.
I have this for my RouteConfig
public static void RegisterRoutes(RouteCollection routes)
{
var route = routes.MapRoute(
name: "CatchAllRoute",
url: "values/path/{*pathValue}",
defaults: new { controller = "Values", action = "GetPath" });
}
And this is my ValuesController:
[System.Web.Mvc.AuthorizeAttribute]
[RoutePrefix("values")]
public class ValuesController : ApiController
{
[Route("test/{value}")]
[HttpGet]
public string Test(string value)
{
return value;
}
[HttpGet]
public string GetPath(string pathValue)
{
return pathValue;
}
}
Interestingly, if I derive from Controller rather than ApiController it works OK, but then the normal attribute routing doesn't work.
I tried following the methodology in this post (http://www.tugberkugurlu.com/archive/asp-net-web-api-catch-all-route-parameter-binding) but I couldn't get it to work.
I'm sure I'm missing something stupidly easy, but having spent a few hours on it I thought it prudent to ask what I'm doing wrong.
Thanks
M
Web api routing is not the same as routing MVC. instead of
route.MapRoute
try
public static void Register(HttpConfiguration config) {
config.MapHttpAttributeRoutes
config.Routes.MapHttpRoute(
name: "CatchAll", routeTemplate: "values/path/{*pathvalue}",
defaults: new {id = RouteParameter.Optional });
}
The reason it works from controller is that MapRoute is the correct format for routing an MVC controller, while MapHttpRoute is designed for API controllers.
Using the new Api Controller in MVC4, and I've found a problem. If I have the following methods:
public IEnumberable<string> GetAll()
public IEnumberable<string> GetSpecific(int i)
This will work. However, if I want to retrieve some different data of a different type, it defaults to the GetAll method, even though the $.getJSON is set to the GetAllIntegers method:
public IEnumberable<int> GetAllIntergers()
(bad naming conventions)
Is it possible for me to be able to do this?
Can I only have a single GetAll method in the Web API controller?
I think it's easier to visualise what I'm trying to achieve. Here is a snippet of code to show what I'd like to be able to do, in a single ApiController:
public IEnumerable<string> GetClients()
{ // Get data
}
public IEnumerable<string> GetClient(int id)
{ // Get data
}
public IEnumerable<string> GetStaffMember(int id)
{ // Get data
}
public IEnumerable<string> GetStaffMembers()
{ // Get data
}
This is all in the routing. The default Web API route looks like this:
config.Routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
With the default routing template, Web API uses the HTTP method to select the action. In result it will map a GET request with no parameters to first GetAll it can find. To work around this you need to define a route where the action name is included:
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
After that you can star making requests with following URL's:
api/yourapicontroller/GetClients
api/yourapicontroller/GetStaffMembers
This way you can have multiple GetAll in Controller.
One more important thing here is that with this style of routing, you must use attributes to specify the allowed HTTP methods (like [HttpGet]).
There is also an option of mixing the default Web API verb based routing with traditional approach, it is very well described here:
Web API: Mixing Traditional & Verb-Based Routing
In case someone else faces this problem. Here's how I solved this. Use the [Route] attribute on your controller to route to a specific url.
[Route("api/getClient")]
public ClientViewModel GetClient(int id)
[Route("api/getAllClients")]
public IEnumerable<ClientViewModel> GetClients()