How can I use attribute routing without endpoints? - c#

How can I use attribute routing in the Home Controller, like
[Route("")]
[Route("Home")]
[Route("Home/Index")]
public IActionResult Index()
{
return View();
}
to get to the main page and not have
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
in Startup.cs?

If you use net core your startup could be like this:
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});

Whether you do not use attribute routing, you have to use endpoint. The endpoint is a mapping to the routing template.
When the first HTTP request comes in, the Endpoint Routing middleware will map the request to an Endpoint. It will use the EndpointDataSource created when the App starts, use it to traverse to find all available Endpoints, and check the routing and metadata associated with it in order to find the most matching Endpoint.
Once an Endpoint instance is selected, it will be attached to the requested object so that it can be used by subsequent middleware.
Finally, at the end of the pipeline, when the Endpoint middleware is running, it will execute the request delegation associated with the Endpoint. This request delegate will trigger and instantiate the selected Controller and Action methods, and generate a response. Finally, the response is returned from the middleware pipeline.
Take a look at the following flow chart.
About attribute routing, it improves the freedom of routing and also provides good support for restful api.

Related

Define global route for REST API controller

I have the following case: in .NET framework there was the possibility to define a global route for API controllers and their methods. By getting the defaults and add a controller route you could leave out the [Route] attribute for the controllers. In addition to that, the route for the methods was adapted from the name of the method, so you could exclude the [Route] attribute there too.
So when you mapped for example "{controller}" and have a controller called "MyController" + have a method called "Post" and a method called "Delete" you just have to use the route "MyController" and depending on the action that is chosen either the "Post" method or the "Delete" method was called.
I want to reach the same in .NET 6 but I'm not sure how to achieve that here.
I've tried the following:
app.UseEndpoints(endpoints =>
{
IDictionary<string, object> defaults = new Dictionary<string, object>();
defaults.Add("controller", "{controller}");
endpoints.MapControllerRoute(name: "default", pattern: "{controller}", defaults: defaults);
endpoints.MapControllers();
});
Didn't work so far. As soon as I start my rest service I get the error that the [Route] attribute is missing.
Any ideas how to achieve that?
Seems like you are looking for a definition of the default conventional routing.
You can define it like in example below:
app.MapControllers();
app.UseRouting();
app.MapControllerRoute(
name: "default",
pattern: "{controller=your_default_controller}/{action=defaul_action}/{id?}");
But you should take to account, that using the conventional routing for the REST API isn't the best solution. The Microsoft documentation recommend to use attribute routing.
NOTE: If you are going to use the conventional routing like defined above you should remove the [ApiController] and the [Route("[controller]")] attributes, that usually is added by default .NET REST API template.
See the additional article:
Attribute routing for REST APIs
Set up conventional route

How to change the url of a .net 6 web api

i wanted to change the launching url of the WebAPI from https://localhost:7027/controller to https://localhost:7027/test/controller. i have tried adding app.UsePathBase("/test") but it only routes swagger. i tried it with postman and it says 404 not found. how do i go about accomplishing this please. thank you
You can do that with attribute routing for a specific controller or
[Route("test/[controller]")]
public class ValuesController : Controller
{
Or globally you can do like below
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}");
});
See below - Change the "/api" part of the url in ASP.Net Core

ASP.NET Core route mapping: prefix is ignored

I have a .NET 6 ASP.net Core web application where I want to configure all calls with a particular path prefix to map to a specific controller.
What I want is for all calls that are of the form http://myhost/ipc/some_action to invoke the action some_action of the controller LocalIpcController. Here's how I setup my route in Startup class:
//Configure routing
app.UseEndpoints(endpoints =>
{
//Local IPC endpoint
endpoints.MapControllerRoute(
name: "Ipc",
pattern: "ipc/{action}",
defaults: new { controller = "LocalIpc" }
);
}
However, it is not working. Specifically:
If I make a call to http://myhost/ipc/some_action I get a 404 error
If I make a call to http://myhost/some_action it works correctly
So it looks like the /ipc prefix in the path is completely ignored. Why is this happening?
PS: I know I can also use the [Route] attribute on the controller to do this, but I want to why it isn't working via MapControllerRoute() and what I am doing wrong.
I tested it out and it is working for me. Here is the pipeline configuration and the test controller:
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// Don't forget to use the UseRouting middleware.
app.UseRouting();
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseEndpoints( endpoints => {
//Local IPC endpoint
endpoints.MapControllerRoute(
name: "Ipc",
pattern: "ipc/{action}",
defaults: new { controller = "LocalIpc" }
);
});
Note: do not forget to include the app.UseRouting();
public class LocalIpcController : ControllerBase
{
public LocalIpcController()
{
}
[HttpGet]
public IActionResult Get()
{
return Content("I'm here.");
}
}
Also I would like to add, that defining the URL routes in endpoints middleware options is not in my opinion the best approach from readability perspective and I would use it only in some edge cases. It is much more clear to specify it in Route attribute (or you can even put it into HTTP method attribute constructor e.g. [HttpGet("ipc/some_action")]).
It turns out, I didn't understand properly how the routing attributes work in ASP.net.
In my controller, the action methods were marked like this:
[HttpGet("some_action")]
public IActionResult MyMethod()
{
}
I was under the impression that "some_action" would be appended to the path indicated in the configuration phase, to obtain "ipc/some_action", but apparently that is not the case, the route specified in the attribute seems to override the one specified in the MapControllerRoute() method.

My Post form is not reaching the action method inside my asp.net mvc core web application

I am working on an asp.net MVC core web application, and i have the following routing rules:-
app.UseEndpoints(endpoints => {
endpoints.MapControllerRoute(
name: "IP",
pattern: "IP/{controller=Home}/{action=Index}/{id?}");
});
app.UseEndpoints(endpoints => {
endpoints.MapControllerRoute(
name: "Info",
pattern: "Info/{controller=Home}/{action=Index}/{id?}");
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
then inside my create form i define the following to use different url when posting back the form depending on the current url:-
<form method="post" action="#(!Context.Request.Path.Value.ToString().ToLower().Contains("/info") ? "/IP/submissions/create/": "/info/submissions/create/")" >
and here is my post action method:-
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Submission,SubmissionQuestionSubmission")] SubmissionCreate sc )
{
but currently when i post back the form the action method will not be called... any idea? and the browser console will raise this error:-
HTTP400: BAD REQUEST - The request could not be processed by the server due to invalid syntax.
POST - https://localhost:44363/info/Submissions/Create/
It's a "Bad Request" because you aren't using the form tag helper. Which should build the action url for you and add an anti forgery token.
However, your routes and controller actions should have a 1-1 mapping. Right now you have 3 different routes to the same actions. How are those actions supposed to know which route was used and therefore what action to take? Are you planning on checking the raw url in each action?
Are you attempting to split your controllers into areas? In which case adding the asp-controller, asp-action, asp-route-... attributes will cause the form tag helper to build the right url for you.
Or would you prefer to add an extra argument to each action, indicating which url was used. Which you could then add to your model so your view can provide it as a route parameter.

Using attribute routing and convention based routing with Web API and MVC simultaneously

I have an Asp.net MVC web application that uses convention based routing. I recently added some Web Api 2 controllers, for which I used attribute routing. Despite the documentation claiming that you can use both, I can either get the (attribute routed) API methods to work, or the (convention routed) web application methods.
This is RouteConfig.RegisterRoutes():
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Tables", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "Foo.Cms.Controllers" }
);
}
This is WebApiConfig.Register():
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
// The models currently only serialize succesfully to xml, so we'll remove the json formatter.
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.JsonFormatter);
}
And this is Application_Start():
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
}
This way, only the routing to the web api controllers works. If I switch GlobalConfiguration.Register() and RouteConfig.RegisterRoutes(), like so:
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configure(WebApiConfig.Register);
...only the convention-based routing works.
I'm at a loss. What's going on here?
Edit:
What I'm trying to achieve:
The application currently uses the basic {controller}/{action}/parameters convention. So I have a controller called ElementsController that has, for instance, an Index() method that is routed to /Elements or a ListPublic() method that is routed to /Elements/ListPublic. I achieved this with the convention based routing mentioned above.
I also have a bunch of Web Api controllers (for instance, TablesController) that I want to route to using a /api/v0/tables route. I tried to achieve this like so:
[RoutePrefix("api/v0/tables")]
public class TablesController : ApiController
{
[Route()]
public string Get()
{
// ...
}
}
As you can see, it's not the same route pattern: api calls are all prefixed with api/v0/. For some reason though, it still appears to treat them as the default {controller}/{action} routes.
What's occurring is that the "first registered" route is taking effect. If I have a MVC route defined as
{controller}/{action}/{id}
and a Web API route defined as
{controller}/{action}/{id}
The first registered route will take effect.
Why is this the case? Imagine you send a request to the server
foo/bar/1
Which route does this match?
Both!
It will choose the first result that matches the route regardless of the type of routing used. If you want some examples as to how to make these routings work, check out this link
If an URL matches two different route templates, it doesn't matter if they refer to MVC or Web API, the only rule is that the first registered route will be used to select the action to execute.
In your particular case, if you make a GET request to this URL: /api/v0/tables, the action selector starts checking the registered routes.
The first route that you're registering is an MVC route like this: /{controller}/{action}/{id}. So, the template matches with this values:
controller = "api"
action = "v0"
id = "tables"
If you register the attribute routes before the MVC route, the first matching route template will be your route attribute template, so the Web API controller action will be correctly selected.
In other words, if you need to route Web API and MVC in the same application you have to use routes which match different URLs for each action, and be careful with the order in which they are registered: register first the more specific templates, and then the more generic ones, so that the generic templates doesn't swallow the URIs which should be matched with the specific templates.
This is not the only option. You can also use route constraints, for example, if all your API has a particular naming convention which can be checked with a constraint (for example a Regex constraint), you can still use convention routing for Web API.
NOTE: apart from the route matching, it's also necessary that the HTTP method (like POST, GET, etc.) is supported by the action. So you can have two similar actions in the same route that accept different methods, and the Action selector would know which one to choose, but this doesn't solve your problem

Categories

Resources