I have a very basic Asp.Net Core Api; my controller looks like this:
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
[HttpGet("{id}")]
public IEnumerable<Resource> Test(string id)
{
// Breakpoint here
}
I would expect the following URL to invoke the method, and fire the breakpoint:
https://localhost:5012/test/test/1
However, it doesn't. In fact, the following URL does:
https://localhost:5012/test/1
I was under the impression that the format for the URL was as follows (from startup):
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
So, unless that action is Index:
https://localhost:5012/controller/action/id
But it appears that the accepted format is:
https://localhost:5012/controller/id
My question is, why is this?
In addition to pwrigshihanomoronimo answer,
you can just change this
[HttpGet("{id}")]
public IEnumerable<Resource> Test(string id)
to
[HttpGet("[action]/{id}")]
public IEnumerable<Resource> Test(string id)
Actually it is ApiController attribute, who breaks your routing. Looks like app.UseEndpoints configures routes just for MVC. So the solution is to remove all attributes to have the following code
public class TestController : ControllerBase
{
public string Test(string id)
{
return "OK";
}
}
Or, if you want to keep ApiController attribute, you would need to adjust Route value as well. You can remove app.UseEndpoints, if you don't use MVC in your project
[ApiController]
[Route("[controller]/[action]")]
public class TestController : ControllerBase
{
[HttpGet("{id}")]
public string Test(string id)
{
return "OK";
}
}
Related
I'm really struggling with trying to hit a single end point in my ASP.NET Core Web API app
The relevant parts of the Controller are
[Route("api/[controller]")]
[ApiController]
public class PlanController : BaseApi
{
[HttpPost]
[Route("api/{controller}/Clear/")]
public async Task<ActionResult> Clear() //always returns 404
{ //some code in here
}
[HttpPost]
public async Task<ActionResult> Post([FromBody] PlanPoco model) //works as expected
{//more code
}
and the relevant part of my Startup.cs file
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "api",
pattern: "{api}/{controller}/{id?}");
endpoints.MapControllers();
endpoints.MapRazorPages();
});
In postman, I would expect to type in https://localhost:12345/api/plan/clear/ and for this to match the end point. It does not. It always returns a 404
Can anyone give me some advice here please?
Based on your code, you apply attribute routing [Route("api/[controller]")] on your controller and [Route("api/{controller}/Clear/")] on clear action, to make request to clear action method, you should use below URL.
https://localhost:port/api/plan/api/plan/clear
I would expect to type in https://localhost:12345/api/plan/clear/ and for this to match the end point.
To achieve your requirement, you can try to apply [HttpPost("Clear")] to your Clear action method.
[Route("api/[controller]")]
[ApiController]
public class PlanController : ControllerBase
{
[HttpPost("Clear")]
public async Task<ActionResult> Clear()
{ //some code in here
return Ok("Clear Action");
}
[HttpPost]
public async Task<ActionResult> Post([FromBody] PlanPoco model) //works as expected
{//more code
return Ok("Post Action");
}
}
Or modify the code like below.
[HttpPost]
[Route("Clear")]
public async Task<ActionResult> Clear() //always returns 404
{
Test Result
Could you try like and make same request ;
[Route("api/[controller]/[action]")]
[ApiController]
public class PlanController : BaseApi
{
[HttpPost]
public async Task<ActionResult> Clear() //always returns 404
{ //some code in here
}
[HttpPost]
public async Task<ActionResult> Post([FromBody] PlanPoco model) //works as expected
{//more code
}
In asp.net core 2.2 i have test api controller class and i have 2 get methods :
[Route("api/[controller]")]
[ApiController]
public class testController : Controller
{
// GET: api/test
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/test/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
....
}
How to use this route api/test?id=1 for get method by id ?
How to use this route api/test?id=1 for get method by id ?
Use Route Attribute.
Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.
[FromQuery] - Gets values from the query string.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
//test url: /api/test?id=7
[HttpGet("/api/test")] // will ignore "api/[controller]" with "/"
public int Test([FromQuery]int id)
{
return id;
}
.....
}
Test of result in .Net Core 2.2 API
I have ASP Core 2.2 app. I defined controller:
using Microsoft.AspNetCore.Mvc;
namespace Web.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok();
}
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
return Ok();
}
}
}
When I request with url /api/users/3 everything works fine, method GetById is called. But if I try to request /api/users?id=3 method Get is called and I don't know how to fix that. Moreover I would like to create two similar method different only by parameter name. For example public IActionResult GetById(int id) and public IActionResult GetByAge(int age) so I need strict routing by named parameters if possible. I don't want to implement custom middleware to resolve routes myself I wanna try to find ASP feature for that.
The url /api/users/3 : "3" is used as part of the route value .
The url /api/users?id=3: "3" is used as a query string in the url .
Attribute routing with Http[Verb] attributes is the value of which is part of the route value
You could change the Route attribute above the controller to specify action name like below :
[Route("api/[controller]/[action]")]
[ApiController]
public class UsersController : ControllerBase
{
// Get api/users/get
[HttpGet]
public IActionResult Get()
{
return Ok();
}
//Get api/users/GetById/3
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
return Ok();
}
}
Reference :https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-2.2
I have MVC and WebAPI filterattributes with parameters that should rise some actionfilters. We're switching to autofac now and I need to convert the DI-Definition.
In Ninject I have something like this:
Kernel.BindFilter<ShopAuthorizationMVCFilter>(System.Web.Mvc.FilterScope.Controller, 0)
.WhenControllerHas<ShopAuthorizationMVC>()
.InRequestScope()
.WithConstructorArgumentFromControllerAttribute<ShopAuthorizationMVC>("rechte", o => o.Rechte);
I can decorate Actions and controllers like this:
[ShopAuthorizationMVC(RightsEnum.CanAccessMycontroller)]
public class MyController : Controller {}
This works fine with Ninject, but I have absolutely no Idea how to write this in Auotofac.
What I've got so far is:
builder.Register(c =>
new ShopAuthorizationMVCFilter(c.Resolve<IAuthClass>(), default(RightsEnum[])))
.AsActionFilterFor<Controller>()
.InstancePerRequest();
But I don't know how to a) apply that rule only on Controllers (and\or Actions) with my filterattribute and b) hand over the parameters.
i think i've cracked this. initally i tried the 'WhenControllerHas' route but found that this gets applied to all controller actions... not what i wanted.
my original attribute looked like this:
public class MyAttribute : Attribute { }
public class MyFilter : ActionFilterAttribute
{
private readonly MyService _myService;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (_myService.IsSomething())
{
return;
}
filterContext.Result = new RedirectResult("/my-url/");
}
}
and was wired up in ninject as:
kernel.BindFilter<MyFilter>(FilterScope.Action, 0).WhenActionMethodHas<MyAttribute>();
after a bit of experimenting, i changed the attribute to this:
public class MyAttribute : ActionFilterAttribute
{
public MyService MyService { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (MyService.IsSomething())
{
return;
}
filterContext.Result = new RedirectResult("/my-url/");
}
}
and added this to the autofac setup:
builder.RegisterFilterProvider();
builder.RegisterType<MyAttribute>().PropertiesAutowired();
so far so good! the code now only runs on action results where decorated with the attribute:
[MyAttribute]
public ActionResult Index() {}
i've still a few things to figure out, namely the order that the attributes are applied (in ninject, it was the order that they were bound up at startup) and how to pass properties down but this feels like it's in the right direction.
The opensource Attribute Routing allows to have multiple route-prefixes.
Why does ASP.NET Web API 2.0 does not allow to have multiple RoutePrefix().
[RoutePrefix("api/v1/{abc}/Entity")]
[RoutePrefix("api/v1/{abc}/{xyz?}/Entity")]
public class MyApiController : ApiController
{
[Route("")]
public IHttpResult Get()
{
return Ok("Hello World");
}
}
You can add a route to the action method also overriding the RoutePrefix with a "~"
example:
[RoutePrefix("api/v1/{abc}/Entity")]
public class MyApiController : ApiController
{
[Route("")]
[Route("~/api/v1/{abc}/{xyz?}/Entity")]
public IHttpResult Get()
{
return Ok("Hello World");
}
}
Notice the line: [Route("~/ api/v1/{abc}/{xyz?}/Entity")]