What is the Name property on Http verbs attribute? - c#

In the docs for this property, nothing is said, so what exactly does this property do?

Reference Routing to controller actions in ASP.NET Core :
Route Name
The following code defines a route name of Products_List:
public class ProductsApiController : Controller
{
[HttpGet("/products/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id) { ... }
}
Route names can be used to generate a URL based on a specific route. Route names have no impact on the URL matching behavior of routing and are only used for URL generation. Route names must be unique application-wide.
Reference Generating URLs by route

Related

OData controller method with multiple parameters

I need to create an OData controller with this assign.
http://xxxxxx/odata/mock/Itens(NumContrato='1234',IdItem='10')/Pedidos
ItensController
public class ItensController : ODataController
{
[HttpPost]
[ODataRoute("(NumContrato={NumContrato},IdItem={IdItem})/Pedidos")]
public IQueryable<Pedido> Pedidos([FromODataUri] string NumContrato, [FromODataUri] string IdItem)
{
... do something
}
}
WebApiConfig.cs
...
config.Routes.MapODataServiceRoute(
"ODataRoute",
"odata/mock",
model: GetModel(),
new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
...
public static IEdmModel GetModel()
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
...
builder.EntitySet<Dominio.ODataSapFake.Item>("Itens");
builder.EntitySet<Dominio.ODataSapFake.LinhaDeServico>("LinhasDeServicos");
builder.EntitySet<Dominio.ODataSapFake.Pedido>("Pedidos");
var a = builder.Entity<Dominio.ODataSapFake.Item>().Collection.Action("Pedidos");
a.Parameter<string>("NumContrato");
a.Parameter<string>("IdItem");
a.ReturnsCollectionFromEntitySet<Dominio.ODataSapFake.Pedido>("Pedidos");
...
return builder.GetEdmModel();
}
An error occurs when call service.
<m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<m:code/>
<m:message xml:lang="en-US">
No HTTP resource was found that matches the request URI 'http://localhost:4492/odata/mock/Itens(NumContrato='100',IdItem='00040')/Pedidos'.
</m:message>
<m:innererror>
<m:message>No routing convention was found to select an action for the OData path with template '~/entityset/key/navigation'.
</m:message>
<m:type/>
<m:stacktrace/>
</m:innererror>
</m:error>
Either your URL is incorrect, or your configuration is incorrect, lets explore both possibilities:
Assuming that the config is correct, you have declared Action called Pedidos has 2 parameters, and it is bound to the collection, no to an item... the URL that matches your definition is actually:
~/Itens/Pedidos(NumContrato='1234',IdItem='10')
Assuming that URL is correct, then you need to configure the endpoint as an Item bound Action and we also assume that the Itens record already has the keys NumContrato and IdItem defined:
builder.EntitySet<Dominio.ODataSapFake.LinhaDeServico>("LinhasDeServicos");
builder.EntitySet<Dominio.ODataSapFake.Pedido>("Pedidos");
var itens = builder.EntitySet<Dominio.ODataSapFake.Item>("Itens");
// Key should be defined by attribute notation, but we can redefine it here for clarity
itens.EntityType.HasKey(x => x.NumContrato);
itens.EntityType.HasKey(x => x.IdItem);
// declare the Action bound to an Item
itens.Item.Action("Pedidos")
.ReturnsCollectionFromEntitySet<Dominio.ODataSapFake.Pedido>("Pedidos");
Update 1 - GET support:
Action supports HTTP Post, which OP has decorated the method with, but if you intend to support HTTP GET, then you must configure the endpoint as a Function instead. The syntax is still the same.
don't forget to remove the [HttpPost] attribute, consider replacing it with [EnableQuery] or [HttpGet]
Update 2 - Item Navigation with Composite Key
If the Item data record has a Navigation Property called Pedidos and your intention is to support the standard Item navigation down this property path then you do not need to include this information in the config all.
remove the [HttpPost], this is a GET request.
remove the [ODataRoute] attribute, the default routes should work
Make sure that you use the names of the key properties, using the same casing and order as they are declared with in the configuration.
If you are using auto configuration, then the order will match the Key attribute column specification in your data model.
For OData v3 you may need to follow this advice: Odata v3 Web Api navigation with composite key

WebAPI 2 : Default GET ALL is invoked with wrong parameter

I am using WebAPI 2 with EF and scaffolding webapi controllers from visual studio.
Each controller is created with 4 default verbs (GET,PUT,DELETE,POST) and 5 actions. while there are two versions of GET action.
IQueryable<entity> GetEntities ()
Task<IHttpActionResult> GetEntity(GUID key) // default is int id but I changed to guid.
I am using attribute routing and route prefix for the controller. just some fancy keywords for better management of url. [RoutePrefix("api/v3/Company")]
Problem :
Ideally when a wrong parameter is sent in url, it should return error, but it is not raising error, instead it fall back to the action without parameter.while if I send a wrong GUID, it shows error.
Like if I call :
http://localhost:8080/api/v3/Company/1f7dc74f-af14-428d-aa31-147628e965b2
it shows the right result.
when I call :
http://localhost:8080/api/v3/Company/1f7dc74f-af14-428d-aa31-147628e96500 (wrong key)
it set back to GetEntity() function and shows all records
when I call:
http://localhost:8080/api/v3/Company/1 (not a GUID length parameter)
it do the same and shows all records.
I am using attribute [Route("{id:guid}")]
Really appreciate if I can get some guidance on this!
It is most likely that the route is defaulting back to the convention-based mapping.
You need to explicitly make apply the route attribute on actions to let the routing know that it is the default route got GET
[RoutePrefix("api/v3/Company")]
public class CompanyController : ApiController {
//GET api/v3/Company
[HttpGet]
[Route("")] //Default Get
public IQueryable GetEntities() { ... }
//GET api/v3/Company/1f7dc74f-af14-428d-aa31-147628e965b2
[HttpGet]
[Route("{id:guid}")] // ALSO NOTE THAT THE PARAMETER NAMES HAVE TO MATCH
public Task<IHttpActionResult> GetEntity(Guid id) { ... }
//...other code removed for brevity
}
Make sure that attribute routing is enabled in the web api config
config.MapHttpAttributeRoutes();

MVC routing expect the Id when it sould not

I have troubles with my routing:
I have in my controller this method:
[HttpGet]
public JsonResult GetCase(CustomIdentity currentUser, string objectType, Guid objectId)
And in my routes
r.Match("CTRL/{id}", "CTRL", "details");
r.Match("CTRL/GetCase", "CTRL", "GetCase");
The problem is when i want to initiate a GET to my method:
I can do that only with
http://a.com/CTRL/GetCase/EE5014C2-C4AA-44E2-80F9-A23D01317790?objectType=123&objectId=1E5014C2-C4AA-44E2-80F9-A23D01317790
But i need
http://a.com/CTRL/GetCase?objectType=123&objectId=1E5014C2-C4AA-44E2-80F9-A23D01317790
What is wrong with my code?
Switch the order of the route setup. The first URI...
http://a.com/CTRL/GetCase/EE5014C2-C4AA-44E2-80F9-A23D01317790?objectType=123&objectId=1E5014C2-C4AA-44E2-80F9-A23D01317790
matches the CTRL/{id} route you set up where GetCase in the URI satisfies {id} template parameter. The convention uses the first matched route it finds when mapping routes.
You need to change the order you set up the route. From what you've shown, that would be
r.Match("CTRL/GetCase", "CTRL", "GetCase");
r.Match("CTRL/{id}", "CTRL", "details");

Route Disambiguation in ASP.NET Core (MVC 6)

When ASP.NET Core encounters ambiguously named routes, it becomes inert. That is, the application will run without exceptions thrown but, it will fail to process any requests, on any controllers. The calling client receives 500 responses.
I'll show how I got into this mess, and I'd like suggestions of how to fix it.
I have a controller that looks like this:
[Route("api/Subscribers/{id}/[controller]")]
[Route("api/Organizations/{id}/[controller]")]
public class AddressesController : Controller
{
[HttpGet("{aid}", Name = "PostalLink")]
public async Task<IActionResult> GetAddress(Guid id, Guid aid)
{
//...implementation is irrelevant for this question.
}
[HttpPost]
[SwaggerResponseRemoveDefaults]
[SwaggerResponse(HttpStatusCode.Created, Type = typeof(PostalRecord))]
public async Task<IActionResult> CreateAddress(Guid id, [FromBody] PostalAddress address)
{
address.ID = Guid.NewGuid();
await createAddress.Handle(address);
return CreatedAtRoute("PostalLink", new { id = id, aid = address.ID });
}
Why the two route prefixes on the controller? Because it fits my microservices (and Swagger documentation) strategy. Nevertheless, in this example ASP.NET Core does not know how to resolve the route name "PostalLink" because it is implicitly bound to the two prefixes:
[Route("api/Subscribers/{id}/[controller]")]
[Route("api/Organizations/{id}/[controller]")]
I can fix the problem simply by changing the HttpGet so that instead of this:
[HttpGet("{aid}", Name = "PostalLink")]
I have this:
[HttpGet("{aid}")] //the route is no longer "named"
Unfortunately, removing the route name is not a real option for me.
What is the prescribed way to fix this?
Below are some of the options I'm considering.
Possibility #1
Theoretically, ASP.NET could simply "figure it out" by itself. For example, if the current request resolved to the route containing the word "Subscribers", then the "PostalLink" name should reference that route. Seen this way, perhaps my code is exposing a bug, defect, or oversight in ASP.NET Core.
Possibility #2
I could collapse my two prefix routes into a single route like this:
[Route("api/{parent}/{id}/[controller]")]
This works, but it undermines my REST documentation strategy. I'm using Swashbuckle to publish endpoint metadata. I want a user of my API to expressly see that my "Addresses" API is serving either "Subscribers" or "Organizations". When I have two explicit route prefixes, the Swagger documentation works correctly (and I properly validate the URI used by the client).
Possibility #3
I could simply override the two prefixes like this:
[HttpGet("~/api/Subscribers/{id}/Addresses/{aid}", Name = "SubscriberLink")]
[HttpGet("~/api/Organizations/{id}/Addresses/{aid}", Name = "OrganizationLink")]
public async Task<IActionResult> GetAddress(Guid id, Guid aid)
{
//...implementation is irrelevant for this question.
}
Now my documentation and route validation works, but my implementation is forced to check which route was used to reach the endpoint. That is very doable, but very annoying.
Possibility #4
Perhaps there is a more expressive way to handle this problem without attribute-based-routing? If yes, please share!
Details
My project.json is configured as follows:
"frameworks": {
"dnx46": { }
},
I am using DNX SDK version 1.0.0-rc1-update1. Also, I posted a related SO question for those who would like more context of what I am trying to do.
If your route names are the same for all your actions, why not specify them directly on the controller ?
[Route("api/Subscribers/{id}/[controller]", Name = "SubscriberLink")]
[Route("api/Organizations/{id}/[controller]", Name = "OrganizationLink")]
public class AddressesController : Controller
{
[HttpGet("{aid}")]
public async Task<IActionResult> GetAddress(Guid id, Guid aid)
{
//...implementation is irrelevant for this question.
}
}
Have you looked into attribute routing?
E.g. Registering routes with ASP.Net 5's MVC 6 Attribute Routing
Sample from the relevant documentation:
In the following example, app.UseMvc(); is used in the Configure method and no route is passed.
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
public IActionResult Index()
{
return View();
}
[Route("Home/About")]
public IActionResult About()
{
return View();
}
[Route("Home/Contact")]
public IActionResult Contact()
{
return View();
}
}
The HomeController.Index() action will be executed for any of the URL paths /, /Home, or /Home/Index.

Changing default routes in WebAPI

Being new and transitioning from ruby on rails. I would like to understand how to change the default routes to use custom routing.
For eg the current routes in my WebApiConfig.cs is as follows:
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new {id = RouteParameter.Optional}
So the URL GET api that I have configured in my controller is:
https://localhost:44300/api/Controller?type=FirstType
The corresponding method defined in my controller is:
public HttpResponseMessage GetControllerByType(string type)
What are the changes to be made to execute the same as above when calling the WEB api using the URL:
https://localhost:44300/api/Controller/FirstType
Assuming that you're using WebAPI 2 you can create custom routes using attributes on your actions within the controllers.
Add the following to the Register method of your WebApiConfig.cs:
config.MapHttpAttributeRoutes();
The above should be added BEFORE the default routing configuration that you already posted above.
Then within your controllers you can use annotations to describe routes:
[Route("controller/{type}")]
[HttpGet]
public HttpResponseMessage GetControllerByType(string type)
Values that are inside the brackets represent variables that are bound to the parameters of the method via their names.
You can also annotate classes themselves to have prefixes that apply to all methods within that controller:
[RoutePrefix("stuff")]
public class MyController : ApiController
{
[Route("myAction/{id}")] //route to this is via /stuff/myAction/{id}
[HttpGet]
public HttpResponseMessage MyMethod(string id)
{ ... }
}
The only 'gotcha' is that query strings should NOT be within the route template, those are represented by optional parameters (those with default values) in your method signature, which don't also have an optional annotation within the route template string. (ie: "myAction/{id:string?}" must have a default value in the signature, but all other parameters that aren't id with default values can be assigned via a query string)
Anything that you do not provide a route template for using an annotation will fallback to using the default routes specified in the WebApiConfig, which is by default "/api/controller_name/action_name/"
Here's a great tutorial on all the custom routing using attributes that you can setup: Attribute Routing in ASP.NET WebAPI 2

Categories

Resources