Get the full route to current action - c#

I have a simple API with basic routing. It was setup using the default Visual Studio 2015 ASP.NET Core API template.
I have this controller and action:
[Route("api/[controller]")]
public class DocumentController : Controller
{
[HttpGet("info/{Id}")]
public async Task<Data> Get(string Id)
{
//Logic
}
}
So to reach this method, I must call GET /api/document/info/some-id-here.
Is it possible with .NET Core, inside that method, to retrieve as a string the complete route?
So I could do for example:
var myRoute = retrieveRoute();
// myRoute = "/api/document/info/some-id-here"

You can get the complete requested url using the Request option (HttpRequest) in .Net Core.
var route = Request.Path.Value;
Your final code.
[Route("api/[controller]")]
public class DocumentController : Controller
{
[HttpGet("info/{Id}")]
public async Task<Data> Get(string Id)
{
var route = Request.Path.Value;
}
}
Result route: "/api/document/info/some-id-here" //for example

You can also ask MVC to create a new route URL based on the current route values:
[Route("api/[controller]")]
public class DocumentController : Controller
{
[HttpGet("info/{Id}")]
public async Task<Data> Get(string Id)
{
//Logic
var myRoute = Url.RouteUrl(RouteData.Values);
}
}
Url.RouteUrl is a helper method that lets you build a route URL given any route values. RouteData.Values gives you the route values for the current request.

If you want the original route template on an API controller that was specified with any HttpMethod attribute then this will do it:
var routeAttribute = Url.ActionContext.ActionDescriptor.EndpointMetadata.First(d => d is HttpMethodAttribute);
var routeTemplate = ((HttpMethodAttribute)routeAttribute).Template;
If the original route attribute was: [HttpGet("Self/{id}")]
The routeTemplate value would be: "Self/{id}"

Url.ActionContext.ActionDescriptor.AttributeRouteInfo.Template
"v{version}/{cardId}/cardsTest" {$1}

Related

Invoke ASP.Net Core MVC controller action with parameter doesn't work

I have a simple ASP.NET Core MVC controller action method defined as:
public class MyMVCController : Controller
{
public async Task<IActionResult> MyAction(String usrid)
{
// ...
}
}
When I call it using this URI:
https://localhost:6009/MyMVC/MyAction/073214df
My action gets invoked and I get return back, but the parameter usrid is always null. What am I doing wrong?
UPDATE
#Jackdaw's first solution, change parameter name from usrid to id worked for me. However, the ones with attributes don't. While the parameter does get passed in, the action will fail because the accessToken somehow returns null in the line below. This is not the case in the first solution.
[Route("MyMVC/MyAction/{usrid?}")]
public async Task<IActionResult> MyAction(String usrid)
{
var accessToken = await HttpContext.GetTokenAsync("access_token");
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// ...
}
Finally, I figured out how to get the attribute based routing solutions to work - adding another attribute [Authorize] to the action method. Don't ask me why I have to do this and why I don't in the usrid/id solution.
It`s because the default route pattern has the following definition:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Therefore, if change the method parameter name to id it will work:
public async Task<IActionResult> MyAction(String id)
Or you can apply the following route attribute to the method:
[Route("MyMVC/MyAction/{usrid?}")]
public async Task<IActionResult> MyAction(String usrid)
{
// ...
}
Test screenshot:

Getting 404 when calling a post method on Web API

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)
{
}

Cannot get LinkGenerator to create a path to API action

I am trying to create a link to an API endpoint from inside a Service - outside of a Controller.
Here is the Controller and its base class. I am using API versioning and Areas in ASP.NET Core.
[ApiController]
[Area("api")]
[Route("[area]/[controller]")]
public abstract class APIControllerBase : ControllerBase
{
}
[ApiVersion("1.0")]
public class WidgetsController : APIControllerBase
{
[HttpGet("{id}"]
[Produces("application/json")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Widget>> Get(Guid id)
{
// Action...
}
}
API Versioning configuration:
services.AddApiVersioning(options =>
{
options.ApiVersionReader = ApiVersionReader.Combine(
new QueryStringApiVersionReader
{
ParameterNames = { "api-version", "apiVersion" }
},
new HeaderApiVersionReader
{
HeaderNames = { "api-version", "apiVersion" }
});
});
And where I actually try to use LinkGenerator:
_linkGenerator.GetPathByAction(
_accessor.HttpContext,
action: "Get",
controller: "Widgets",
values: new
{
id = widget.Id,
apiVersion = "1.0"
}
)
I've tried all manner of variations for the LinkGenerator. I've used the HttpContext overload, I've used the overload without it, I've included the apiVersion parameter and omitted it, I've removed [ApiVersion] from the Controller entirely. Everything always comes back null. If I route to a normal MVC Controller like GetPathByAction("Index", "Home") I get a URL like I should though, so I'm think it must be related to my API Areas, or versioning setup.
You're not specifying the area:
_linkGenerator.GetPathByAction(
_accessor.HttpContext,
action: "Get",
controller: "Widgets",
values: new
{
area = "api",
id = widget.Id,
apiVersion = "1.0"
}
)
In case it helps someone else, I had an issue very similar to this but the action was named GetAsync and it turns out you cannot reference the action by it's full name "GetAsync", you have to use it without the suffix so just "Get".

Creating a different route to a specific action

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.

Route parameters and multiple controller types

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.

Categories

Resources