ASP.NET Core route mapping: prefix is ignored - c#

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.

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 can I use attribute routing without endpoints?

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.

Simple .Net 5.0 web app getting 404s (using mvc)

.Net novice here. I am following along with a web development bootcamp and I have had a persistent issue for the last few days. I've ran it by several TA's, other students, and a few instructors but we have all been stumped thus far.
I am getting 404s when opening the localhost:5000 page in my browser or using postman. The project was generated using the dotnet new web --no-https -o ProjName command and then edited.
Here is what my Startup looks like:
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options.EnableEndpointRouting = false);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
Here is what my controller looks like
using Microsoft.AspNetCore.Mvc;
namespace Portfolio.Controllers
{
class HomeController : Controller
{
[HttpGet("")]
public string Index() => "This is my index";
}
}
Note, I receive a response when using the default auto generated code but not using this Controller with attribute routing pattern. If you know anything about how to resolve this it would be a great help as I am falling behind.
Things I have tried:
Uninstalling everything related to .net and installing a single version (5.0.100 currently, but I tried a few others as well)
Uninstalling and reinstalling IIS Express
Trying to run the project from another computer. No luck.
Changing the port. No luck.
Using a different browser. No luck.
Sobbing on my keyboard. No luck.
Remaking the same project a dozen times.
I am happy to provide any additional information and I am generally tech savvy enough to follow up on what is suggested.
Thank you
Specifying an empty string in the HttpGet attribute is like specifying none.
You can define a default route pattern in the useMvcmethod:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
});
Or set a slash in the HttpGet attribute to set the root path for the method:
HttpGet("/")

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.

WebAPI MapHttpRoute doesn't work, but AttributeRouting does

I have a case where I can't get a default route to work via Config.Routes.MapHttpRoute(), but if I put the route as a route attribute in the controller it works fine.
Global.asax
protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
//RouteConfig.RegisterRoutes(RouteTable.Routes);
//BundleConfig.RegisterBundles(BundleTable.Bundles);
}
(removed last two as they're non-WebAPI requirements (right?) same result even if I leave them in)
WebApiConfig.cs
public static void Register(HttpConfiguration config) {
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "v1/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Controller
public class ErrorsController : ApiController
{
[HttpGet]
[Route("v1/Errors/Get")]
public IHttpActionResult Get(int id) {
return Ok();
}
[HttpPost]
[Route("v1/Errors/Submit")]
public IHttpActionResult Submit()
{
// do stuff
return Ok();
}
}
If I have the attribute routes in there, everything works fine. If I don't, I get 404s. For example, the following two report a 404 error:
localhost:myport/v1/Errors/Get?id=5
localhost:myport/v1/Errors/Submit
Yet, if I submit this:
localhost:myport/v1/Errors
I get the following response:
No HTTP resource was found that matches the request URI
'http://localhost:59498/v1/Errors/'.
No type was found that matches the controller named 'v1'.
Obviously my route configuration isn't kicking in, but for the life of my I can't tell why. I even tried changing the route name to Default instead of DefaultApi, thinking that perhaps Default had some internal significance.
"Normal" routing doesn't support this kind of routes with prefix "v1" in it. In a way or another this kind of rounting follows the old MVC routing, where the first part of the address must be the controller; so when you set the address localhost:myport/v1/Errors the system is currently looking for a controller named "v1".
If you want to use a route with a prefix before you have to stick with the attribute routing; that's why it works perfectly with attributes and it doesn't without.
May I suggest you to use a "global" prefix for the v1 thing? That would allow you to avoid repeating the same part of the URL over and over again on different resources. I suggest you to check this article for a couple of implementation details.

Categories

Resources