I am trying to create a solid example of exactly how the ASP.NET Core routing engine works and I was surprised by the results.
The premise behind this example is hitting a controllers index page, and then using AJAX requests to load data.
I created a ASP.Net Core application with MVC. Then I added the following Controller:
namespace WebApplication2.Controllers {
using Microsoft.AspNetCore.Mvc;
public class SearchController : Controller {
public IActionResult Index() {
return View();
}
[HttpGet("{company}")]
public IActionResult Get(string company) {
return Ok($"company: {company}");
}
[HttpGet("{country}/{program}")]
public IActionResult Get(string country, string program) {
return Ok($"country: {country} program: {program}");
}
}
}
I also create a simple View to go along with Index with the words "Search Page" so that you can see it get called.
The problem is that the routes that are created from this don't make sense.
Using Company: "Abc", Country: "Canada" and Program: "Plumbing" as an example:
/Search/Index
Produces: "Search Page"
/Search/Abc
Produces "company: Abc"
/Search/Canada/Plumbing
Produces: "country: Canada program: Plumbing"
However it doesn't work like this at all. Instead these are the results:
Produces: "country: Search program: Index"
Produces: "country: Search program: Abc"
Produces: 404 Not Found
Clearly the route for Index and Get string company are confused, and it is treating the Controller name as a parameter.
I can make it work with the following code, but I thought the routing engine would produce the same results:
public class SearchController : Controller {
public IActionResult Index() {
return View();
}
[HttpGet("[controller]/{company}")]
public IActionResult Get(string company) {
return Ok($"company: {company}");
}
[HttpGet("[controller]/{country}/{program}")]
public IActionResult Get(string country, string program) {
return Ok($"country: {country} program: {program}");
}
What is wrong with my understanding? It seems silly to have to specify [controller]
explicitly.
You have mixed the conventional routing and the attribute routing while you should not do that.
For your original code, it will work when you remove all the /Search
in your url.
To use the controller name, you need to set [Route("[controller]")]
on your mvc controller to make your url work as expected.
[Route("[controller]")]
public class SearchController : Controller
{
[HttpGet("[action]")]
public IActionResult Index()
{
return View();
}
[HttpGet("{company}")]
public IActionResult Get(string company)
{
return Ok($"company: {company}");
}
[HttpGet("{country}/{program}")]
public IActionResult Get(string country, string program)
{
return Ok($"country: {country} program: {program}");
}
}
Specify the root route above your class
[Route("Search")]
public class SearchController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpGet("{company}")]
public IActionResult Get(string company)
{
return Ok($"company: {company}");
}
[HttpGet("{country}/{program}")]
public IActionResult Get(string country, string program)
{
return Ok($"country: {country} program: {program}");
}
}
Routing is responsible for mapping request URL to an endpoint and it comes with two types of Conventional and Attributes routing.
And from your question, you are expecting conventional routing with default route which you can achieve in .NET CORE using below line of code.
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Search}/{action}/{id?}");
});
Note: But keep in mind that conventional routing will not work if you decorate your controller with [ApiController] attribute.
By default .NET CORE supports attribute routing so you have to prefix the route by placing the [Route] attribute on the controller level. Please see the below example
[Route("api/[controller]")]
[ApiController]
public class SearchController : ControllerBase
{
[HttpGet("{company}")]
public IActionResult Get(string company) {
return Ok($"company: {company}");
}
[HttpGet("{country}/{program}")]
public IActionResult Get(string country, string program) {
return Ok($"country: {country} program: {program}");
}
}
The above code will work as you expected.
If you are decorating your controller by [ApiController] attribute then you have to use Attribute routing and any conventional routing defined in startup class will be overridden. Please see more details here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With