WebAPI 2 custom routing - c#

I have added the custome routing to a WebAPI ASP.NET application, using framework 4.7.1, by adding the attribute on top of the method:
public class ShapeFileAnalysisController : ApiController
{
[HttpGet]
[Route("GetDataValues")]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public HttpResponseMessage GetDataValues()
{
string result = Models.ShapeFileReader.GetAdmin2();
HttpResponseMessage response = this.Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(result, Encoding.UTF8, "application/json");
return response;
}
}
but when I add [Route("...")} attribute the full URL changes from mysite.com/api/ShapeFileAnalysis to mysite.com/GetDataValues
the webapiconfig.cs is:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.EnableCors();
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
What I should do to have still mysite.com/api/ShapeFileAnalysis/GetDataValues ?

Attribute routing uses the route template placed in the attribute.
Your route has only GetDataValues so that will become the full route path.
mysite.com/GetDataValues
If you place the desired route path
//GET api/ShapeFileAnalysis/GetDataValues
[HttpGet]
[Route("api/ShapeFileAnalysis/GetDataValues")]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public HttpResponseMessage GetDataValues() {
//...
}
Then you will get the desired path
mysite.com/api/ShapeFileAnalysis/GetDataValues
You can set a common prefix for an entire controller by using the [RoutePrefix] attribute
[RoutePrefix( "api/ShapeFileAnalysis" )]
public class ShapeFileAnalysisController : ApiController {
//GET api/ShapeFileAnalysis/GetDataValues
[HttpGet]
[Route("GetDataValues")]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public HttpResponseMessage GetDataValues() {
//...omitted for brevity
}
}
Reference Attribute Routing in ASP.NET Web API 2

It's posible that you are mixing two ways to implement a WebApi in Net framework as is mention on the Microsoft Documentation - https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/routing-and-action-selection.
In the first one (the one that you are follow) the routing matching is throught the parameter of the verbs (GET, POST, PUT, DELETE), this means that when you do a Get you distinguish between method using the parameter's name (examples are aviable in Microsoft's Documentation mentioned before).
In the second one you can Add a Route and controller route on the top to tell the compiler in which route it should to expose the method.

I didn't have the time to test in VS my suggestion but I believe that you need to add one more attribute - RoutePrefixAttribute.
Your code should be be following:
[RoutePrefix( "api/ShapeFileAnalysis" )] // new line of code!
public class ShapeFileAnalysisController : ApiController
{
[HttpGet]
[Route("GetDataValues")]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public HttpResponseMessage GetDataValues()
{
}
}

For this situation "mysite.com/api/ShapeFileAnalysis/GetDataValues"
If all requests will be in this template. You don't have to use Custom Route.
The other answer is the solution of the problem, but I want to offer a different alternative. Change default route and remove all route attributes.
Route Template add action tag.
Example:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.EnableCors();
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Referance: Routing in ASP.NET Web API

Related

How do I not generate the default endpoints when using attribute routing?

I'm using attribute routing in a Web API controller (.NET Framework 4.5.2 - don't ask, I'm trying to get approval for time to move everything forward).
I have applied a [RoutePrefix] attribute to my ApiController.
I have two controller actions, both HttpGets. Each has a [Route] attribute applied.
I'm using Swagger to auto-generate docs. In the docs for this controller I see three endpoints listed - two for my controller actions, and another HttpGet with the bare controller route.
That is what I have is this:
[RoutePrefix("api/test/Tickets")]
public class TestTicketsController : ApiController
{
[HttpGet, Route("")]
public HttpResponseMessage GetTickets()
{
....
}
[HttpGet, Route("since")]
public HttpResponseMessage GetTicketsSince(string since)
{
....
}
}
And In the generated Swagger docs I see three endpoints:
GET api/test/Tickets
GET api/test/Tickets/since
GET api/TestTickets
This third endpoint, api/TestTickets, seems to be derived from the class name of the controller, ignored my routing attributes. And when I call it, I get an HTTP 200 with an empty body, despite not having defined an action for it.
Where is this coming from? And how can I stop it from being generated?
===
It was suggested that I remove the [HttpGet, Route("")] attribute. If I do, I get an error:
Multiple operations with path 'api/TestTickets' and method 'GET'.
It was also suggested that I include my WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "swagger_root",
routeTemplate: "",
defaults: null,
constraints: null,
handler: new RedirectHandler((message => message.RequestUri.ToString()), "api/docs/index"));
var mediaType = new MediaTypeHeaderValue("application/json");
var formatter = new JsonMediaTypeFormatter();
formatter.SupportedMediaTypes.Clear();
formatter.SupportedMediaTypes.Add(mediaType);
config.Formatters.Clear();
config.Formatters.Add(formatter);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
If I comment-out the config.Routes.MapHttpRoute, the extra endpoint goes away.
Now it's just a matter of determining whether we have any controllers that expect this default endpoint to be there.
Thanks.
abdulg pointed out in a comment, rather than in an answer - default routes are configured in WebApiConfig.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

Multiple actions were found that match the request But WebApiConfig is set

i'm using asp.net web api. And my problem is "Multiple actions were found that match the request" but i set route template already and in my controller i have 2 POST action
**WebApiConfig**
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.Formatters.JsonFormatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
And this is my controller
1. [Route("{InsertRoadMap}")]
[HttpPost]
public mdRoadMapCallBack InsertRoadMap(mdEvent _mdEvent){
mdRoadMapCallBack _mdRoadMapCallBack = new mdRoadMapCallBack();
return _mdRoadMapCallBack;
}
2. [Route("{UpdateRoadMap}")]
[HttpPost]
public mdRoadMapCallBack UpdateRoadMap(mdEvent _mdEvent)
{
mdRoadMapCallBack _mdRoadMapCallBack = new mdRoadMapCallBack();
return _mdRoadMapCallBack;
}
What is wrong ? Please help. Thank you so much
The Route attribute doesn't seems to be implemented correctly.
Use it like this instead -
[Route("api/mycontroller/InsertRoadMap")]
[Route("api/mycontroller/UpdateRoadMap")]
Now you can browse like this - http://server/api/mycontroller/InsertRoadMap
You can also use RoutePrefix attribute at controller level so that you do not have to repeat api for every method.
[RoutePrefix("api")]
And then you can set routes like -
[Route("mycontroller/InsertRoadMap")]
[Route("mycontroller/UpdateRoadMap")]
See here and here some more information on attribute routing

Get method not working in web api

Hi I am developing one application in web api2 and angularjs. I have some routing problems in accessing urls from angularjs.
I am trying to access below url.
var url = '/api/projects/4/processes';
My controller code looks like below.
[RoutePrefix("api/processes")]
public class processesController : ApiController
{
[ActionName("projects/{projectsId}/processes")]
public HttpResponseMessage Get(int id)
{
return Request.CreateResponse(HttpStatusCode.OK, "");
}
}
I am getting 404 error. I am not able to hit the url.
This is my webapi.config file.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
//routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Any help would be appreciated.
First ensure that attribute routing is enabled before convention-based routes.
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
if the intended url is /api/projects/4/processes Then the given action needs to update its route template to match. The controller already has a route prefix but that can be overridden by prefixing the route template with the tilde ~
Use a tilde (~) on the method attribute to override the route prefix:
//GET /api/projects/4/processes
[HttpGet]
[Route("~/api/projects/{projectsId:int}/processes")]
public HttpResponseMessage Get(int projectsId) { ... }
Source: Attribute Routing in ASP.NET Web API 2
In your case, the URL should be,
http://yourUrl/api/processes?projectsId=yourValue
Also, change your ActionName to Route attribute,
[RoutePrefix("api/processes")]
public class ProcessesController : ApiController
{
[Route("projects/{projectsId}/processes"),HttpGet]
public HttpResponseMessage Get(int projectsId)
{
return Request.CreateResponse(HttpStatusCode.OK, "");
}
}
New URL - http://yourUrl/api/processes/projects/2/processes
More info on Attribute routing
Please make sure that attribute routing should be enabled before convention-based routes.
config.MapHttpAttributeRoutes();
then you can make routing
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Define Methods like :
//GET /api/projects/4/processes ([ActionName("projects/{projectsId}/processes")])
[RoutePrefix("api/processes")]
public class processesController : ApiController
{
[HttpGet]
[Route("~/api/projects/{projectId:int}/processes")]
public HttpResponseMessage Get(int projectId) { ... }
}
You have a route prefix, which means the url of action would be RoutePrefix + ActionRoute
i.e. HTTP GET api/processes/projects/{projectsId}/processes
Ref docs
Comment out the "[Authorize]" attribute on the ValuesController class was all I needed...
All credit to DotNetTutorials
From the article:
“api/{controller}/{id}”
At this point, if you use the following URI in the browser, you will get an error stating – Authorization has been denied for this request.
http://localhost:xxxxx/api/values
To get rid of this error, comment Authorize attribute on the ValuesController class. This is related to security which we will discuss in a later article.

EnableCors failing in WebApi 5.2.3 for specific apicontrollers

So I followed the following tutorials:
enable-cors-in-webapi-2
ASPNET-WebAPI-Enable-Cors
enabling-cross-origin-requests-in-web-api
Each of them described a way to EnableCors for a controller, or you can add by global.
I followed the steps outlined in all 3 of them where in my App_Start/WebApiConfig.cs file I entered the following:
public static void Register(HttpConfiguration config)
{
config.EnableCors();
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { controller = "", action = "Get" }
);
}
And in my controller I added in:
[EnableCors(origins: "http://localhost:50247", headers: "*", methods: "*")]
public class TestController : ApiController
{ ... }
But when I went to run the program it gave me the following error:
XMLHttpRequest cannot load http://localhost:49705/api/Test/GetTestData. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:50247' is therefore not allowed access.
But when I make the following addition to the App_Start/WebApiConfig.cs to make EnableCors throughout the entire app:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var cors = new EnableCorsAttribute(
origins: "*",
headers: "*",
methods: "*");
config.EnableCors(cors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
It worked. But if makes my decorated EnableCors attribute on my api controllers useless.
When I removed the [EnableCors] attribute from my apicontroller:
public class TestController : ApiController
{ ... }
It worked, because of what I did globally. Is there a reason for why this happened? Did a bug occur when upgrading from webapi 5.2.2 to 5.2.3?
The project is new using asp.net webapi start up templates in visual studio 2013.

WebAPI 2 Attribute routing with areas not working

I'm having trouble getting WEBAPI 2 attribute routing to work. The routing scheme I'm looking for is /api/{product}/{controller}/{id-optional}, so like /api/Vision/IdCard. The controllers are in an area and are set up like this:
namespace DataServices.Controllers.Vision
{
[RoutePrefix("api/vision")]
public class IdCardController : BaseApiController
{
[System.Web.Mvc.RequireHttps]
[Route("idcard")]
public IdCardViewModel Get(HttpRequestMessage request)
{...}
Whenever I do a get to this, I get a 404. I included the namespace as the area is in it's own namespace. Are areas supported in WEBAPI attribute routing?
EDIT: WebApiConfig looks like this:
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
You need to get the HttpConfiguration instance from the GlobalConfiguration object and call the MapHttpAttributeRoutes() method from inside the RegisterArea method of the AreaRegistration.cs.
public override void RegisterArea(AreaRegistrationContext context)
{
GlobalConfiguration.Configuration.MapHttpAttributeRoutes();
//... omitted code
}
This must be done for each Area.
Finally you must in the 'WebApiConfig' remove "config.MapHttpAttributes()" method or you will get duplicate exception.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.EnableCors();
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
//config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Is the project a MVC project from start? Then I think you should remove the "ApiAreaRegistration.cs" file created when you created the area. It's found in the root of the your Api area and it will conflict with your attribute routes as it will match on a MVC (not WebApi) route like "api/{controller}/{action}/{id}" before it finds your specific routes.
Hope it helps!
Area functionality not available in Asp.Net Web API project, and its harder to maintain with custom way like Namespace based controller.
I have checked many problems with namespace based controller and routing, like single action method is accessible by namespace based routing as well as default routing.Thus custom implementation does not mitigate our requirements.
To resolve this issue we can use simple way to manage controllers routing as :
//Remove default routing registration
/*config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);*/
And use just attribute based routing only, like
[RoutePrefix("api/user/home")]
public class UserHomeController : ApiController
{
[Route]
public string Get()
{
return "Test user GET";
}
}
And for different area/module controller
[RoutePrefix("api/admin/home")]
public class AdminHomeController : ApiController
{
[Route]
public string Get()
{
return "Test admin GET";
}
}
Advantages with this approach are:
No need of custom logic like namespace based area, custom routing handler, so its better way to code.
Just need to add attribute [Route] in action to availability in API
Try play with initialization order in Application_Start
Thus:
//Config WebAPI(2) where you call config.MapHttpAttributeRoutes();
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Order is very important (if i reverse areaRegistration with WebApiConfig => WebAPI 2 attributeRouting won't work

Categories