Global route for all ApiControllers in .Net 6 - c#

In all of my projects i put this code in top my controllers :
[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
public IActionResult GetTest1()
{
return Ok();
}
[HttpGet]
public IActionResult GetTest2()
{
return Ok();
}
[HttpPost]
public IActionResult PostTest1(string Request)
{
return Ok("value was " + Request);
}
}
So i can call my APIs with action name without specify each action route, Like below picture from my swagger :
This work fine but i need to put this route top of all my ApiControllers in my project, When i decide to change all of my route to new one, i need to replace all RouteAttributes in my project.
I look for global solution, for Example something like this in my program.cs file :
app.MapControllerRoute(
name: "default",
pattern: "api/{controller}/{action}");
Problem is i can't make this code work when i delete RouteAttribute from my APIs.

Just followed this simple tutorial on Global route Prefix You will as well have to remove the route prefix on top of the controller after adding the global route prefix to your program.cs file.
my swagger :
my progam.cs
builder.Services.AddMvc(opt =>
{
opt.UseCentralRoutePrefix(new RouteAttribute("core/v1/[controller]/[action]"));
}
).AddControllersAsServices().AddNewtonsoftJson();

I search few hours for this and after all i didn't find any simple solution for creating good, simple, fast global route for APIs, Then i tried on my own.
Solution 1 :
You can create a base class for your APIs and put your route in that file, Then you only inherit from that class in all of your APIs.
APIBase.cs file :
[ApiController]
[Route("api/[controller]/[action]")]
public class APIBase: ControllerBase
{
}
Next step : If you need have id (or any other parameter) in your URL so you should add RouteAttribute only for that method.
ValuesController.cs file :
public class ValuesController : APIBase
{
[HttpGet]
public IActionResult GetTest1()
{
return Ok();
}
[HttpGet]
[Route("{id?}")]
public IActionResult GetTest2(int id)
{
return Ok("value was " + id);
}
[HttpPost]
public IActionResult PostTest1(string Request)
{
return Ok("value was " + Request);
}
}
Edit :
Solution 2 : You can create a string const in your project and use it in all APIs (Suggested by #Serge)
HelperStatic.cs file :
public static class HelperStatic
{
public const string BaseAPIRoute = "api/[controller]/[action]";
}
ValuesController.cs file :
[ApiController]
[Route(HelperStatic.BaseAPIRoute)]
public class ValuesController : ControllerBase
{
[HttpGet]
public IActionResult GetTest1()
{
return Ok();
}
[HttpGet]
[Route("{id?}")]
public IActionResult GetTest2(int id)
{
return Ok("value was " + id);
}
[HttpPost]
public IActionResult PostTest1(string Request)
{
return Ok("value was " + Request);
}
}
And your swagger will be like this (on both solution) :

Related

.NET 6 best way to differentiate a route in the same controller class

I have an API controller say TestController.
I have the following route definition: api/[controller] on class level and I can call the default get method as follows: .../api/test
I want to have another method in the same controller, I also want to call it with Get and the link should be as follows: .../api/test-abc
What is the best way to differentiate this second method within the same controller class.
For test-abc, you can configure the Route attribute as ~/api/test-abc to override the controller base route. See this link for details.
[Route("api/[controller]")]
public class TestController : ControllerBase
{
public IActionResult Get()
{
// ...
}
[HttpGet]
[Route("~/api/test-abc")]
public IActionResult GetAbc()
{
// ...
}
}
This approach changes only the URL of the "test-abc"-action; all other actions of the controller use the base URL configured on class level.
In .Net 6 by following Minimal API's
app.MapGet("api/test", () =>
{
return "";
});
app.MapGet("api/test-abc", () =>
{
return "";
});
By following Traditional Method
[Route("api")]
public class TestController : ControllerBase
{
[HttpGet("test")]
public IActionResult Get()
{
// ...
return Ok();
}
[HttpGet("test-abc")]
public IActionResult GetAbc()
{
// ...
return Ok();
}
}
You can by defining the controllers route template like as below:
[Route("api/[controller]")]
public class TestController : Controller
{
...
}
So you can define a method like:
[HttpGet]
[Route("~/api/Test/Index")]
public IActionResult Index()
{
return Foo.Bar();
}
[HttpGet]
[Route("~/api/Test/Index2")]
public IActionResult Index2()
{
return Foo.Bar2();
}
[HttpGet]
public IActionResult Index3()
{
return Foo.Bar3();
}
You can call it by ../api/test/index2 to use the method Index2 or ../api/test to use Index3.
This is for the standardized way to do, in your case you would use - instead of / at the Route("...") attribute.
If you use - you need to call ../api/test-index2
For further information take a look at documentation.

Controller route attribute only relevant for root function?

I have a question about .NET Core controller routing. Recently I discovered that the controller route attribute (which you place just above the controller) only works for the root method, or at least it seems that way.
My code:
using KrabbelMicroservice.Models;
using KrabbelMicroservice.Services.Interfaces;
using Microsoft.AspNetCore.Mvc;
namespace KrabbelMicroservice.Controllers;
[ApiController]
[Route("/profile")] // <-- This is the controller routing attribute I am talking about
public class ProfileKrabbelController : Controller
{
private readonly IProfileKrabbelService _krabbelService;
public ProfileKrabbelController(IProfileKrabbelService krabbelService)
{
_krabbelService = krabbelService;
}
[HttpGet]
public IActionResult Index()
{
// not relevant
}
[HttpGet]
[Route("/id/{krabbelId}")]
public IActionResult GetKrabbelById(long krabbelId)
{
// not relevant
}
[HttpGet]
[Route("/pid/to/{profileId}")]
public IActionResult GetKrabbelsToProfileId(long profileId)
{
// not relevant
}
[HttpGet]
[Route("/pid/from/{profileId}")]
public IActionResult GetKrabbelsFromProfileId(long profileId)
{
// not relevant
}
[HttpGet]
[Route("/pid/with/{profileId}")]
public IActionResult GetKrabbelsWithProfileId(long profileId)
{
// not relevant
}
[HttpPost]
[Route("/new")]
public IActionResult AddKrabbel(ProfileKrabbel krabbel)
{
// not relevant
}
[HttpPut]
[Route("/update")]
public IActionResult UpdateKrabbel(ProfileKrabbel krabbel)
{
// not relevant
}
[HttpDelete]
[Route("/delete")]
public IActionResult DeleteKrabbel(ProfileKrabbel krabbel)
{
// not relevant
}
}
In my swagger launch the requests look like this:
I expected that all paths would be prefixed by /profile/ but it seems like only the root function (which did not have its own route attribute) implemented the prefix.
I am not only trying to get a fix for this, but also looking for an explanation as to why my controller route attribute is ignored for the other requests. The only possibility I can think of is the specific route attributes for each endpoint overriding the controller route attribute but I would like to hear it from an expert.
Secondly I would of course also like to find a solution to this problem, preferrably not adding /profile before every seperate route but if that is the only solution so be it.
Thanks in advance!
you should be remove "/" if you have root route
ex:
[Route("test")]
[ApiController]
public class TestController3 : Controller
{
[HttpGet]
[Route("testobj")]
public TestObj Test()
{
return "test";
}
}
the even shorter in httpget
[HttpGet("testobj")]
the both output:
test/testobj

Multiple controller types were found that match the URL but different http verbs

We are getting this error,
Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.\r\n\r\nThe request has found the following matching controller types:
NameSpace.AController
NameSpace.BController
[AllowAnonymous]
public class AController : Controller
{
[Route("a1"), HttpGet]
public ActionResult A1()
{
return Content("A1");
}
}
[AllowAnonymous]
public class BController : Controller
{
[Route("a1"), HttpPost]
public ActionResult A1()
{
return Content("B1");
}
}
I have to allow different route names with http verb distinction.
There are two possible solutions for this:
De URL matched because the route for the two methods matched. (base/A1 and base/A1.) Change you method A1 to B1. Then you have two different endpoints.
[AllowAnonymous]
public class AController : Controller
{
[Route("a1"), HttpGet]
public ActionResult A1()
{
return Content("A1");
}
}
[AllowAnonymous]
public class BController : Controller
{
[Route("b1"), HttpPost]
public ActionResult B1()
{
return Content("B1");
}
}
With this example your URL become: base/A1 and base/B1
It is also possible to set the controllername in the url:
[AllowAnonymous]
[Route("[controller]")]
public class AController : Controller
{
[Route("a1"), HttpGet]
public ActionResult A1()
{
return Content("A1");
}
}
[AllowAnonymous]
[Route("[controller]")]
public class BController : Controller
{
[Route("A1"), HttpPost]
public ActionResult A1()
{
return Content("B1");
}
}
With this example your URL become: base/A/A1 and base/B/A1

ASP.NET Core routing engine confusion

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.
Expected Results
/Search/Index
/Search/{company}
/Search/{country}/{program}
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"
Actual Results
However it doesn't work like this at all. Instead these are the results:
/Search/Index
Produces: "country: Search program: Index"
/Search/Abc
Produces: "country: Search program: Abc"
/Search/Canada/Plumbing
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.

Route Prefix VS Controller Name ( Web api )

I was wondering that if we use RoutePrefix attribute in our web api controller with a different name from controller's actual name. So would it work or not?
As far as i did
[RouterPrefix("quotation")]
public class SaleOrderController : ApiController { ... }
if we define RoutePrefix like above we can't access it via /quotation but we can access it using saleorder.
So what is RoutePrefix for or am i doing something wrong ?
To use default route use Route("")
[RoutePrefix("quotation")]
public class SaleOrderController : ApiController {
//GET quotation
[Route("")]
[HttpGet]
public IHttpActionResult GetAll() { ... }
}
Source: Attribute Routing in ASP.NET Web API 2 : Route Prefix
In order for it to work, you need to call the code below inside your WebApiConfig.Register() method:
config.MapHttpAttributeRoutes();
So your RoutePrefix works as exptected:
[RoutePrefix("quotation")]
public class SaleOrderController : ApiController
{
[Route("example")]
[HttpGet]
public IHttpActionResult Example()
{
return Ok();
}
[Route("another")]
[HttpGet]
public IHttpActionResult Another()
{
return Ok();
}
}
So your could access your apis like this:
quotation/example
quotation/another

Categories

Resources