Route configuration in ASP Core - c#

I Have a problem configuring routes in ASP Core.
In Startup.cs I used default configuration: services.AddMvc() in ConfigureServices() and app.UseMvc() in Configure().
Now I have a simple controller in the same assembly:
[Route("/api/[controller]")]
public class TestController: Controller
{
[HttpGet]
public string Test()
{
return "Hello";
}
}
Request /api/test/test doesn't fire
But if i add [HttpGet("test")] or [Route("test")] it works well.
However I'd like to support convention over configuration in case route attribute is not specified

Try using:
[Route("api/[controller]/[action]")]

When you add [Route("/api/[controller]")] annotation to Controller, the default routing configured in Startup.cs will ignore this Controller.
So you either need to specify URL suffix for each of your actions inside that Controller by adding [Route("")] attribute above them:
[Route("/api/[controller]")]
public class TestController: Controller
{
[Route("test")]
[HttpGet]
public string Test()
{
return "Hello";
}
}
Or specify in Controller annotaion that the routing should use action names as part of URL :
[Route("/api/[controller]/[action]")]
public class TestController: Controller
{
[HttpGet]
public string Test()
{
return "Hello";
}
}

[Route("/api/[controller]")]
public class TestController: Controller
{
[HttpGet(namOf(Test),Name=namOf(Test))]
public string Test()
{
return "Hello";
}
}

Related

Web Api Ignoring Route Attribute

I am working on a Web API project and when I run it and try to use the route for a controller, it doesn't work and instead, it throws me a 404 error. Why is the Web API ignoring the route attribute?
I'll leave the code below:
[ApiController]
[Route("api/[controller]")]
public class Controller3 : ControllerBase
{
private readonly IServiceContract3 _interest;
public Controller3(IServiceContract3 interest)
{
_interest = interest;
}
[HttpGet]
[Route("[action]")]
[Route("api/Interest/GetInterests")]
public IEnumerable<Interest> GetEmployees()
{
return _interest.GetInterests();
}
[HttpPost]
[Route("[action]")]
[Route("api/Interest/AddInterest")]
public IActionResult AddInterest(Interest interest)
{
_interest.AddInterest(interest);
return Ok();
}
[HttpPost]
[Route("[action]")]
[Route("api/Interest/UpdateInterest")]
public IActionResult UpdateInterest(Interest interest)
{
_interest.UpdateInterest(interest);
return Ok();
}
[HttpDelete]
[Route("[action]")]
[Route("api/Interest/DeleteInterest")]
public IActionResult DeleteInterest(int id)
{
var existingInterest = _interest.GetInterest(id);
if (existingInterest != null)
{
_interest.DeleteInterest(existingInterest.Id);
return Ok();
}
return NotFound($"Employee Not Found with ID : {existingInterest.Id}");
}
[HttpGet]
[Route("GetInterest")]
public Interest GetInterest(int id)
{
return _interest.GetInterest(id);
}
}
And for my Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContextPool<DatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DB")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DatabaseContext context)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
context.Database.Migrate();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
How can I fix the routing? Every time I try to do it in my browser like https://localhost:44316/api/Interest/GetInterests I get a 404 error instead. Why is this happening?
Typically controllers for a webapi are defined as so:
[ApiController]
[Route("api/[controller]")]
public sealed class InterestController : ControllerBase
{
private readonly IServiceContract3 _interest;
public InterestController(IServiceContract3 interest)
{
_interest = interest;
}
[HttpGet, Route("GetInterests")]
public IActionResult GetEmployees()
{
// this is located at: GET api/Interest/GetInterests
return Ok(_interest.GetInterests());
}
[HttpPost, Route("AddInterest")]
public IActionResult AddInterest([FromBody] Interest interest)
{
// this is located at: POST api/Interest/AddInterest
}
[HttpPost, Route("UpdateInterest")]
public IActionResult UpdateInterest([FromBody] Interest interest)
{
// this is located at: POST api/Interest/UpdateInterest
}
[HttpDelete, Route("DeleteInterest/{id}")]
public IActionResult DeleteInterest([FromRoute] int id)
{
// this is located at: DELETE api/Interest/DeleteInterest/{id}
}
[HttpGet, Route("GetInterest/{id}")]
public IActionResult GetInterest([FromRoute] int id)
{
// this is located at: GET api/Interest/GetInterest/{id}
return Ok(_interest.GetInterest(id));
}
}
First, you want to name your controller a relevant name that will carry throughout the rest of your controller. So, this this case, I changed Controller3 to InterestController. Since the route of the controller is "/api/[Controller]", it will translate to "api/Interest".
Next, remove the [Action] attributes. You do not need them in this scenario.
Then, make sure your routes are correct. If you are passing an ID, define your ID in the route using {id} and setting the [FromRoute] attribute for clarity. Also, use [FromBody] to define parameters that will be coming from the body. A lot of these are "default" (meaning you don't need to add them), but it helps with clarity.
Finally, if you are using the IActionResult pattern, then stick with that through your controller. Don't mix/match return types across endpoints because it's confusing for maintenance.
One final thing:
Your controller endpoint names are slightly redundant. For example, you could do this:
[HttpGet, Route("")]
public IActionResult GetEmployees()
{
// this is located at: GET api/Interest
return Ok(_interest.GetInterests());
}
[HttpPut, Route("")]
public IActionResult AddInterest([FromBody] Interest interest)
{
// this is located at: PUT api/Interest/
}
[HttpPost, Route("")]
public IActionResult UpdateInterest([FromBody] Interest interest)
{
// this is located at: POST api/Interest/
}
[HttpDelete, Route("{id}")]
public IActionResult DeleteInterest([FromRoute] int id)
{
// this is located at: DELETE api/Interest/{id}
}
[HttpGet, Route("{id}")]
public IActionResult GetInterest([FromRoute] int id)
{
// this is located at: GET api/Interest/{id}
return Ok(_interest.GetInterest(id));
}
i.e.: Use the "HTTP Verb" to your advantage to segregate actions.
you have to fix your route by making api a root. Replace "api" by "~/api". IMHO you should remove [action] from actions and add it to a controller. Also fix a controller name
[ApiController]
[Route("~/api/[controller]/[action]")]
public class InterestController : ControllerBase
[HttpGet("~/api/Interest/GetInterests")]
public IEnumerable<Interest> GetEmployees()
....
[HttpPost("~/api/Interest/AddInterest")]
public IActionResult AddInterest(Interest interest)
...
[HttpPost("~/api/Interest/UpdateInterest")]
public IActionResult UpdateInterest(Interest interest)
...
[HttpDelete("~/api/Interest/DeleteInterest/{id}")]
public IActionResult DeleteInterest(int id)
....
[HttpGet("~/api/Interest/GetInterest/{id}")]
public Interest GetInterest(int id)

Attribute Routing for querystring

I have below route URL:-
www.domanname.com/subroute/GetInfo?param1=somestring&param2=somestring
I have function in webapi as:-
public class HomeController : ApiController
{
public object GetInfo(string param1,string param2)
{}
}
To apply route:-
[RoutePrefix("subroute")]
public class HomeController : ApiController
{
[Route("GetInfo?param1={param1:string}&param2={param2:string}")]
public object GetInfo(string param1,string param2)
{}
}
But after applying above URL:-
www.domanname.com/subroute/GetInfo?param1=somestring&param2=somestring
It is not able to find that URL
How can I design this particular route?
You need to modify the routes a bit as query string are not normally used in attribute routes. They tend to be used for inline route parameters.
[RoutePrefix("subroute")]
public class HomeController : ApiController {
//Matches GET subroute/GetInfo?param1=somestring&param2=somestring
[HttpGet]
[Route("GetInfo")]
public IHttpActionResult GetInfo(string param1, string param2) {
//...
}
}
Also
Enabling Attribute Routing
To enable attribute routing, call MapHttpAttributeRoutes during configuration. This extension method is
defined in the System.Web.Http.HttpConfigurationExtensions class.
using System.Web.Http;
namespace WebApplication
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
// Other Web API configuration not shown.
}
}
}
Reference Attribute Routing in ASP.NET Web API 2
[RoutePrefix("subroute")]
public class HomeController : ApiController {
[HttpGet]
[Route("GetInfo/{param1}/{param2}")]
public IHttpActionResult GetInfo(string param1, string param2) {
//...
}
}
Calling
//Matches GET subroute/GetInfo/Hello/World

ASP.Net Web API: How to achieve url parameter and attribute based versioning with same Controller methods

Is it possible to achieve url parameter and attribute based versioning with same Controller methods. To explain that, suppose I have one controller like,
[RoutePrefix("api/{apiVersion:apiVersionConstraint(v2)}/values")]
public class ValuesController : ApiController
{
// GET api/v2/values
[Route("")]
public IEnumerable<string> Get()
{
return new string[] { "v2-value1", "v2-value2" };
}
// GET api/v2/values/5
[Route("{id}")]
public string Get(int id)
{
return "v2-value-" + id;
}
}
Now, I want to access the API endpoint by both of the following URL's:
http://hostname/context/api/v1/values
http://hostname/context/api/values?v=1
Is it possible?
N.B. I'm using the example at WebApiNamespaceVersion in GitHub

How to override web api route on the same method with annotations

I have this configuration in the HttpConfiguration
config.Routes.MapHttpRoute("Default", "api/{controller}");
config.Routes.MapHttpRoute("Another", "api/{controller}/{action}");
config.Routes.MapHttpRoute("WithKey", "api/{controller}/{action}/{key}");
For that reason I cannot access my controller like this
http://<host>/api/products (works)
http://<host>/api/products/1 (doesn't work)
So I added the annotation Route in the get method but it doesn't work
[RoutePrefix("products")]
public class ProductsController : ApiController
{
[HttpGet]
public IQueryable<IProduct> GetProducts()
{
return db.GetProducts();
}
//[Route("products/{productID}")] Tried. Doesn't work
//[Route("{productID:int}")] Tried. Doesn't work
[HttpGet]
public IProduct GetProduct(int productID)
{
return db.GetProduct(productID);
}
}
The only way to make it work is typing the address like this http://<host>/api/products?productID=1, but I'd really want to access with this url http://<host>/api/products/1.
I can add new routes in the http configuration but cannot modify the existing ones. And I don't want to affect existing controllers.
How can I solve this, please?
First ensure that attribute routing is enabled before convention-based routes.
config.MapHttpAttributeRoutes();
//...convention-based routes.
config.Routes.MapHttpRoute("Default", "api/{controller}");
//...other code removed for brevity
Next you want to update the attribute routes.
[RoutePrefix("api/products")]
public class ProductsController : ApiController {
//GET api/products
[HttpGet]
[Route("")]
public IQueryable<IProduct> GetProducts() {
return db.GetProducts();
}
//GET api/products/1
[HttpGet]
[Route("{productID:int}")]
public IProduct GetProduct(int productID) {
return db.GetProduct(productID);
}
}

How to put multiple GET methods in Web API2 controller?

I am doing a Web API 2 application and I have controller named NCT_ProcessSettings and already I have two GET methods as below.
1. public IEnumerable<Process_Settings> Get()
2. public HttpResponseMessage Get(int id)
Now I want to have third one as below (Same as first one but inside I will write different logic).
3. public IEnumerable<Process_Settings> Get() //Compiler will confuse which to pick?
I tried as below.
[HttpGet]
[Route("GetGlobalSettings")]
public IEnumerable<NCT_Process_Settings> GetGlobalSettings()
{
return entityObject.NCT_Process_Settings.Where(c => c.project_id == 0).ToList();
}
Below is my angularcode to call api.
var url = '/api/NCT_ProcessSettings/GetGlobalSettings';
May I have some idea how to fix this? Any help would be appreciated?
Enable attribute routing in WebApiConfig.cs before convention-based routes.
config.MapHttpAttributeRoutes();
Next update controller to use routing attributes. (note the route prefix)
[RoutePrefix("api/NCT_ProcessSettings")]
public class NCT_ProcessSettingsController : ApiController {
//GET api/NCT_ProcessSettings
[HttpGet]
[Route("")]
public IEnumerable<Process_Settings> Get() { ... }
//GET api/NCT_ProcessSettings/5
[HttpGet]
[Route("{id:int}")]
public HttpResponseMessage Get(int id) { ... }
//GET api/NCT_ProcessSettings/GetGlobalSettings
[HttpGet]
[Route("GetGlobalSettings")]
public IEnumerable<NCT_Process_Settings> GetGlobalSettings() { ... }
}
Read up more documentation here Attribute Routing in ASP.NET Web API 2
Used Action Name attribute
[ActionName("Get")]
public IEnumerable<Process_Settings> Get1()//used any name here
{
}

Categories