If I have the following controller:
public class RetrievalController : ApiController
{
[Route("api/User")]
public HttpResponseMessage RetrieveSomethingKthx()
{
if (true)
{
return Request.CreateResponse(System.Net.HttpStatusCode.OK);
}
return Request.CreateResponse(System.Net.HttpStatusCode.NotFound);
}
}
Is there a way early on (such as in OWIN) to read the path. E.G.: Request.Path == "http://www.website.com/api/User" and therefore get Reflection Information about that method?
Note that I am not relying on the Route to have the Controller Name or "Action" Name necessarily. I assume something lives in the Web API Routing to pull method info based on the Route.
Related
I have an API controller which have standard GET,POST and Delete actions.
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
//Get
[HttpPost]
public async Task Post([FromBody] TestUser testUser, string tempPassword, role = "Guest")
{
}
}
Now I am adding a new action using:
[HttpPost]
[Route("api/[controller]/UpdateRole")]
public async Task Post(string email, List<string> roles)
{
}
When I am trying to call the API using postman ,
Type : POST
Endpoint : http://localhost/api/users/UpdateRole
Request body:
{
"email":"something#mail.com",
"roles":["S1","s3"]
}
But I am getting a 404 as response back. On server I can see ,
the application completed without reading the entire request body.
It seems that your overall route is /api/Users/api/Users/UpdateRoute because of how RouteAttribute works.
[Route("a")]
public class MyController
{
[Route("a/b")]
public IActionResult MyAction()
{
}
}
The above will have a route of /a/a/b because the action route is appended to the controller route in this case.
Your options are:
Change the controller route to [Route("[controller]/[action]")] and remove the action route, in which case the example above would become /MyController/MyAction
Change the action route to simply [Route("b")], in which case the full route would be a/b
Use an absolute path for the action route [Route("/a/b")], in which case the controller route would be ignored and the full route will simply be /a/b.
See here for more information about routing.
As for your issue with null values, ASP.NET Core is currently expecting email and roles as querystring parameters. Instead, you should create a model for your request body:
public class MyModel
{
public string Email { get; set; }
public List<string> Roles { get; set; }
}
And then change your action to accept it:
[HttpPost]
[Route("api/[controller]/UpdateRole")]
public async Task Post([FromBody]MyModel model)
{
}
I'm trying create an additional Get method on a web api but the return is 404 ( method not found ).
At my APIs before Core I was creating such methods like :
[HttpGet]
[Route("api/MyNewMethodName")]
public object MyNewMethodName(string parameter1)
{}
And for call :
myURL/api/MyNewMethodName?parameter1=somestring
At my controller definition I have :
[Produces("application/json")]
[Route("api/MyController")]
public class MyController : Controller
For the exactly some code I receive the 404 error.
What is wrong please ?
Your controller has a route defined. So for your action method, it will be the route prefix defined for the controller + the route pattern for the action method. That means, with your current code, it will work for the below request
yourBaseUrl/api/MyController/api/MyNewMethodName?parameter1=somestring
Here api/MyController part is from the route definition on the controller level and the api/MyNewMethodName part is from the action method level.
Fix the route prefix at controller or method level as needed. For instance if you want your action method to respond to /api/MyNewMethodName?parameter1=somestring. Just remove the Route decorator on the controller level.
[Produces("application/json")]
public class MyController : Controller
{
[HttpGet]
[Route("api/MyNewMethodName")]
public object MyNewMethodName(string parameter1)
{
return "Sample dummy response : "+parameter1;
}
}
Keep in mind that, removing the controller level routing might break routes to other action methods in that controller. If you want to keep the existing routes as it is (with the controller level route attributes), You may update your action method level route pattern to start with a /
[Produces("application/json")]
[Route("api/MyController")]
public class MyController : Controller
{
[HttpGet]
[Route("/api/MyNewMethodName")]
public object MyNewMethodName(string parameter1)
{
return "Some test"+parameter1;
}
[HttpGet]
[Route("SecondMethod")]
public object SecondMethod(string parameter1)
{
return "SecondMethod : "+parameter1;
}
}
[HttpGet]
[Route("api/[controller]/MyNewMethodName")]
public object MyNewMethodName(string parameter1)
{
}
Try declaring the above format, this worked for me.
I am working on an asp.net 5 mvc api, and I am currently working on the Accounts Controller.
since I saw in many different places that there is a convention of using /api/Tokenrouting to a login in a web api. I would like to route to that specific method without the accounts prefix, I would prefer not using a different controller, and I would prefer using Attributes over routing in Startup.cs to avoid confusion in the future.
this is what I have currently
[Route("api/[controller]")]
public class AccountsController : Controller
{
[HttpPost("login")]
public async Task<JwtToken> Token([FromBody]Credentials credentials)
{
...
}
[HttpPost]
public async Task CreateUser([FromBody] userDto)
{
...
}
}
With attribute routing you can use a tilde (~) on the Action's route attribute to override the default route of the Controller if needed:
[Route("api/[controller]")]
public class AccountsController : Controller {
[HttpPost]
[Route("~/api/token")] //routes to `/api/token`
public async Task<JwtToken> Token([FromBody]Credentials credentials) {
...
}
[HttpPost]
[Route("users")] // routes to `/api/accounts/users`
public async Task CreateUser([FromBody] userDto) {
...
}
}
For ASP.NET Core it seems that the tilde ~ symbol (see accepted answer) is not needed anymore to override the controller's route prefix – instead, the following rule applies:
Route templates applied to an action that begin with a / don't get combined with route templates applied to the controller. This example matches a set of URL paths similar to the default route.
Here is an example:
[Route("foo")]
public class FooController : Controller
{
[Route("bar")] // combined with "foo" to map to route "/foo/bar"
public IActionResult Bar()
{
// ...
}
[Route("/hello/world")] // not combined; maps to route "/hello/world"
public IActionResult HelloWorld()
{
}
}
from https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing
[Route("[controller]/[action]")]
public class HomeController : Controller
{
[Route("~/")]
[Route("/Home")]
[Route("~/Home/Index")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
In the preceding code, the Index method templates must prepend / or ~/ to the route templates. Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.
I wanted to provide some URL separation for my public/anonymous controllers and views from the admin/authenticated controllers and views. So I ended up using entirely Attribute Routing in order to take more control of my URLs. I wanted my public URLs to start with "~/Admin/etc." while my public URLs would not have any such prefix.
Public Controller (one of several)
[RoutePrefix("Home")]
public class HomeController : Controller
{
[Route("Index")]
public ActionResult Index()
{ //etc. }
}
Admin Controller (one of several)
[RoutePrefix("Admin/People")]
public class PeopleController : Controller
{
[Route("Index")]
public ActionResult Index()
{ //etc. }
}
This allows me to have public URLs such as:
http://myapp/home/someaction
...and admin/authenticated URLs such as:
http://myapp/admin/people/someaction
But now I want to do some dynamic stuff in the views based on whether the user is in the Admin section or the Public section of the site. How can I access this programmatically, properly?
I know I could do something like
if (Request.Url.LocalPath.StartsWith("/Admin"))
...but it feels "hacky." I know I can access the controller and action names via
HttpContext.Current.Request.RequestContext.RouteData.Values
...but the "admin" piece isn't reflected in there, because it's just a route prefix, not an actual controller name.
So, the basic question is, how do I programmatically determine whether the currently loaded view is under the "admin" section or not?
You just need to reflect the RoutePrefixAttribute from the Controller type, and then get its Prefix value. The Controller instance is available on the ViewContext.
This example creates a handy HTML helper that wraps all of the steps into a single call.
using System;
using System.Web.Mvc;
public static class RouteHtmlHelpers
{
public static string GetRoutePrefix(this HtmlHelper helper)
{
// Get the controller type
var controllerType = helper.ViewContext.Controller.GetType();
// Get the RoutePrefix Attribute
var routePrefixAttribute = (RoutePrefixAttribute)Attribute.GetCustomAttribute(
controllerType, typeof(RoutePrefixAttribute));
// Return the prefix that is defined
return routePrefixAttribute.Prefix;
}
}
Then in your view, you just need to call the extension method to get the value of the RoutePrefixAttribute.
#Html.GetRoutePrefix() // Returns "Admin/People"
I have a asp.net web api, using attributes for routing on the controllers. There are no route attriutes on the action level. The route for accessing a resource is:
[Route("{id}"]
public MyApiController: ApiController
{
public HttpResponseMessage Get(Guid id)
{
// ...
}
}
My problem is that when I want to create a search controller, I'd like the URL to be
[Route("search")]
But this results in an error: Multiple controller types were found that match the URL. Is it possible to make sure the exact matching route is selected before the generic one?
Technically, the phrase search could be a valid ID for the first controller, but as {id} is a guid, this will never be the case, thus I'd like to select the controller with the exact matching route.
You can use Route constraints to do the job. For example you could constraint your ID route to accept only valid GUID's.
Here is an ID controller that accepts only GUID strings in the URL:
[System.Web.Http.Route("{id:guid}")]
public class MyApiController: ApiController
{
public HttpResponseMessage Get(Guid id)
{
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
The Search controller would match to an url like "/search". Here is the Search controller:
[System.Web.Http.Route("search")]
public class SearchController : ApiController
{
public HttpResponseMessage Get()
{
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
Constraints will prevent matching conflicts in the router.