Multiple Conventional Routes order Precedence not working as expected for (Current LTS version ASP.NET CORE 3.1 ) - asp.net-core

This question is related to ASP.NET Core Routing. I am doing the hands-on implementation (ASP.NET CORE 3.1 LTS) of Concept Multiple Conventional Routes.
MS documentation MultipleConventional Routes
According to documentation, conventional routing is order-dependent. what that means is there are consequences for not ordering my routes correctly.
here is the code snippet of the app.UseEndpoint method with a list of configured routes.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action}/{id?}");
endpoints.MapControllerRoute(
name: "CustomerHomepage",
defaults:new { controller = "Customer", action = "Index" },
pattern: "customer/{name}/index"); });
For this request https://localhost:44341/customer/sean/details
At first look at the set of route templates and order especially the first route it is a perfect match
with
controller name = customer
action name = sean
id = details.
what I have in the project.
I do have a controller name Customer but no action name as sean instead I have action name as details inside the Customer Controller.
Question
The point I am trying to make is this path customer/sean/details overall should be invalid and should not navigate anywhere based on the order of the routing template.
Instead, it does navigate to the action method Details in the customer controller. The question is why it is working instead it should not be based on the concept that conventional routing is order-dependent and this request URL customer/sean/details match to the first route. Also, what would be the best example for this claim that conventional routing is order-dependent.
The code for the Customer Controller is listed down
public class CustomerController: Controller
{
public IActionResult Index(string name)
{
return View();
}
public IActionResult Details(string name) {
ViewBag.CustomerName = name;
return View();
}
}

Convention based routing in ASP.NET Core 3.x+ is not fundamentally order based. Instead, the routing system builds an acyclic graph which is used to match the incoming URL to the best route. In your example, the literal match to customer makes the send route the best match.
In this series I describe how you can visualize all the routes in your ASP.NET Core applications, which may help you to understand how routes are combined.

From this doc about "Routing in ASP.NET Core", you would find the process of matching an incoming request to an endpoint, like below.
URL matching operates in a configurable set of phases. In each phase, the output is a set of matches. The set of matches can be narrowed down further by the next phase. The routing implementation does not guarantee a processing order for matching endpoints. All possible matches are processed at once. The URL matching phases occur in the following order. ASP.NET Core:
Processes the URL path against the set of endpoints and their route templates, collecting all of the matches.
Takes the preceding list and removes matches that fail with route constraints applied.
Takes the preceding list and removes matches that fail the set of MatcherPolicy instances.
Uses the EndpointSelector to make a final decision from the preceding list.
The list of endpoints is prioritized according to:
The RouteEndpoint.Order
The route template precedence
All matching endpoints are processed in each phase until the EndpointSelector is reached. The EndpointSelector is the final phase. It chooses the highest priority endpoint from the matches as the best match. If there are other matches with the same priority as the best match, an ambiguous match exception is thrown.

Related

ASP.NET Core OData Action With Two Parameters

I have this action method available to OData:
[HttpPost]
[ODataRoute("({id})/Replace")]
public Blog Replace([FromODataUri] int id, Blog blog)
This responds for POST requests on /odata/Blogs(1)/Replace.
It is working except for the part that the blog parameter is never bound from the POST, that is, it is not null, but has default values for all properties. If I add [FromBody] to the parameter, it becomes null. I also tried with a parameter of type ODataActionParameters, but it is always null.
This is the relevant part of my model:
var replace = builder.EntitySet<Blog>("Blogs").EntityType.Action("Replace");
//replace.Parameter<int>("id").Required(); //this can be added or not, it doesn't matter
replace.EntityParameter<Blog>("blog").Required();
replace.Returns<int>();
I read somewhere that an OData action cannot have two parameters, but I am not sure of that. I need the id parameter to be present, so I need to find a solution for this.
I am using ASP.NET Core 3.1 and the latest versions of all packages.
What am I doing wrong?
The solution turned out to be simple:
The parameter declaration was wrong: the name of the entityset is “blogs”, not “blog”
I was posting the JSON for the “Blog” entity, but I had to modify it so as to be included in a parameter “blog”, as this:
{
“blog”: {
“BlogId”: 1,
<< rest of the Blog properties >>
}
}
This solved my problem.

how to do pagination in RESTFUL API in a effective way?

I want to support pagination in my RESTful API.
My API method should return a JSON list of product via http://localhost/products/v1/getproductsbycategory, there are potentially thousands of products, and I want to page through them, so my request should look something like this:
public function getProductsByCategory($product_id,$page){
$perPage=5;
$start=($page-1)*$perPage;
$stmt=$this->conn->prepare("SELECT id,product,description,destination_url,expiry_type,savings,expiry,title,last_updated_on FROM products WHERE product_id=? ORDER BY last_updted_on DESC LIMIT $start ,$perPage");
$stmt->bind_param('i',$category_id);
$stmt->execute();
$productbycategory=$stmt->get_result();
$stmt->close();
return $productbycategory;
}
}
Firstly, in a RESTful call, the URL should ideally be noun-based and not verbs. We are using HTTP verbs (GET, PUT, POST, etc) to do an action on a noun - product in your case.
So, the URL should be http://localhost/products/v1/category
This effectively means you are GETting product of type v1 based on category. To get a given page number, simply add it as a query parameter -
http://localhost/products/v1/category?page=1
and handle it accordingly in your GET implementation corresponding to localhost/products/v1/category
Hope this helps.
Pagination has nothing to do with the JSON format per se - it's all about the query string in the URL and how the server interprets that.
Expanding on #Sampada's answer, you can have a URL like
http://localhost/products/v1/category?pageSize=5&pageNumber=2
and then you'll simply pick the corresponding elements on the server side (consider whether you'll want 0 or 1-based index for the pageNumber), and return them.
Additionally you can wrap this collection in an object that also provides links as to navigate to the previous/next/specific page - see HATEOAS & Richardson's Maturity Model level 3.

MVC Routing with wildcard

I am developing a website on Asp MVC4
I have a link like that www.myhostname/category/{some arbitrary text}_ProductId
(e.g: www.myhostname.com/electronics/the-new-ipad_12345, in which 12345 is the ProductId that I need to extract)
Is the any way to register one route that give my directly the Id ?
I tried
routes.MapRoute(
name: "productRoute",
url: "{category}/*_{ProductId}",
defaults: new { Controller = "Home", action = "Product" }
);
But of course, it doesn't work
Note : as a work around, I used "{category}/{ProductLink}", to get the whole segment (e.g: the-new-ipad_12345) and extracted this ProductId on my action
Thanks & regards,
You are better off using attribute based routing. You can add regex to [Route] attribute and parse the product id.
It is part of Web API 2 now. If you are using older version of Web API, you can add reference to - http://attributerouting.net/
The final solution will look something like..
[Route("electronics/{productId:int:regex(_([0-9]+))}")]
public ProductDto GetProduct(int productId)
{
// ...
}

Web API 2 Routing based on Parameter's Value

Is it possible to setup Web Api 2 route based on a parameter's value in the query string.
I have the following requirement:
/api/controller?data=param.data1
should go to controller's action 1
/api/controller?data=param.data2
should go to controller's action 2
any other value of data must go to action 3.
I know there's an option to set a constraint with a regex, but the examples I've found are for generic scenarios and not as specific as mine.
This is what I've tried
config.Routes.MapHttpRoute(
name: "test",
routeTemplate: "api/Hub/{data2}",
defaults: new { action = "Test" },
constraints: new { data2 = #"^(param\.data2)$" }
);
Is there a way to do it? Maybe there's a better way?
Important to note, I cannot change the URI of the service. It must have ?data=[value]
This is a fallback for a legacy system :(
You can use Attribute Routing, new in Web API 2.
Let's say you have the following actions, where the data param is, let's say, a string:
public Stuff GetStuffForData1(string data) { ... }
public Stuff GetStuffForData2(string data) { ... }
public Stuff GetStuffForData(string data) { ... }
Since you mentioned regex, you can specify route constraints for each of the above actions using a regex like the one you mentioned in your question1, for example:
[Route("controller/{data:regex(#"^(param\.data1)$")]
public Stuff GetStuffForData1(string data) { ... }
[Route("controller/{data:regex(#"^(param\.data2)$")]
public Stuff GetStuffForData2(string data) { ... }
// No need for a route constraint for other data params.
public Stuff GetStuffForData(string data) { ... }
The general syntax is {parameterName:constraint(params)} (params is optional and is not used for all constraints). In the above example, the first route will only be selected if the data segment of the URI matches the data1 regex. Similarly, the second route will be selected if the data segment of the URI matches the data2 regex. Otherwise, the last route will be chosen.
In general, the total ordering is determined as follows:
Compare the RouteOrder property of the route attribute. Lower values are evaluated first. The default order value is zero.
Look at each URI segment in the route template. For each segment, order as follows:
Literal segments.
Route parameters with constraints.
Route parameters without constraints.
Wildcard parameter segments with constraints.
Wildcard parameter segments without constraints.
In the case of a tie, routes are ordered by a case-insensitive ordinal string comparison (OrdinalIgnoreCase) of the route template.
You can even create your own custom route constraints by implementing the IHttpRouteConstraint interface and registering it in the Register method of your WebApiConfig class, assuming you're hosting on IIS, or in the Configuration method of your Startup class if self-hosting using OWIN.
Note I haven't personally tried any of the above, but it should all work; at the very least it should give you some ideas. For more details, including very nice examples, you should start by taking a look at the following article (which I shamelessly used extensively in my answer):
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#constraints
1 I'm really not an expert on writing regexes, so unfortunately I can't advise you on the specific ones you'll need.

What is the proper RESTful way to "like" something in Rails 3?

Let's say I have a Rails 3 app that displays videos. The user can "Like" or "Dislike" the videos. Also, they can like/dislike other things like games. I need some help in the overall design and how to handle the RESTful routes.
Currently, I have a Like Class that uses polymorphic design so that objects are "likeable" (likeable_id, likeable_type)
I want to do this via AJAX (jQuery 1.5). So I was thinking something like:
javascript
// these are toggle buttons
$("likeVideo").click( function() {
$.ajax({
url: "/likes/video/" + video_id,
method: "POST",
....
});
} );
$("likeGame").click( function() {
$.ajax({
url: "/likes/game/" + game_id,
method: "POST",
....
});
} );
rails controller
Class Likes < ApplicationController
def video
# so that if you liked it before, you now DON'T LIKE it so change to -1
# or if you DIDN'T like it before, you now LIKE IT so change to 1
# do a "find_or_create_by..." and return JSON
# the JSON returned will notify JS if you now like or dislike so that the
# button can be changed to match
end
def game
# same logic as above
end
end
Routes
match "/likes/video/:id" => "likes#video", :as => :likes_video
match "/likes/game/:id" => "likes#game", :as => :likes_game
Does this logic seem correct? I am doing a POST via AJAX. Technically, shouldn't I be doing a PUT? Or am I being too picky over that?
Also, my controller uses non-standard verbs. Like video and game. Should I worry about that? Sometimes I get confused on how to match up the "correct" verbs.
An alternative would be to post to something like /likes/:id with a data structure that contains the type (game or video). Then I could wrap that in one verb in the controller...maybe even Update (PUT).
Any suggestions would be appreciated.
Rest architectural style does not specify which "verb" you should be using for what. It simply says that one can use HTTP if they want to for connectors.
What you are looking for is HTTP specifications for method definitions. In particular POST is intended for:
- Annotation of existing resources;
- Posting a message to a bulletin board, newsgroup, mailing list,
or similar group of articles;
- Providing a block of data, such as the result of submitting a
form, to a data-handling process;
- Extending a database through an append operation.
while PUT:
requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server.
Which category your functionality falls into is up to you - as long as you are consistent with yourself about it.