MVC Routing with wildcard - asp.net-mvc-4

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

Related

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

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.

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.

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.

How can I pass a List<string> to an api url?

Suppose I have an api method like this inside MyAPIS controller:
public List<string> GetList(List<string> myList)
{
var newList = myList;
return newList;
}
The uri bind to this action should be something like mysite/api/myapis/getlist/[id]. Usually the id is a single value and not a complex item.
But how'd you manage to arrage an url like this?
You would have to be more specific about what framework you are using to get a specific answer. However, in essence what you want to accomplish is the use of slugs. Depending on what framework you are using you can just search for : "framework" slug, "framework" slugs, "framework" slugify.
For example this explains how to use slugs on a controller with the Spring MVC framework:
https://github.com/resthub/resthub.org/blob/master/spring-stack.rst#sluggable-controller
Hope this helps.

typo3 extbase permissions in extensions

I have written one extension for making service order.
The issue I am facing here is,
There are FE users belong to three FE user groups namely "client", "Admin" and "Employee".
Here the client can make order and he should be able to see only his orders.
And the admin can see all orders done by different clients.
And the employee should be able to see only some clients orders.
Currently I made a order table with N:1 relation with FE user table. So every order should relate with any one client.
So in controller, I am checking the login user and using custom query in repository, I am accessing order related to loggedin client (FE user)
In file OrdersController.php
public function listAction() {
$orders = $this->ordersRepository->orderForLoginUsr();
$this->view->assign('orders', $orders);
}
In file OrdersRepository.php
public function orderForLoginUsr(){
$loggedInUserId = $GLOBALS ['TSFE']->fe_user->user['uid'];
$query = $this->createQuery();
$query->matching(
$query->equals('user', $loggedInUserId)
);
$query->setOrderings(array('crdate' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING));
return $query->execute();
}
But here my question is how to make admin user able to see all the orders done by all clients?
I have to write different template and action that calling function findAll() ?
$orders = $this->ordersRepository->findAll();
And how to set for usergroup Employee ?
Thanks in Advance
I think that the easiest way is to actually implement 3 actions with 3 different plugins, something like: listClientAction, listAdminAction and listEmployeeAction
In each of those action, you implement a method in your repository that fetch the right list of order with the good ordering:
orderForLoginClient(), orderForLoginEmployee(), orderForLoginAdmin()
What does the trick actually is that there will be 3 plugins on your page, one for each action. In each instance of your plugin, you set the access for the right be_group.
Don't forget to add the actions and plugin in the localconf and ext_table files.
I hope it will help!
Olivier
If your view is almost the same for client, admin, employee you should simply add a method like getOrderWithPermissionsForUser($currentUser);
In the method itself you should check for the usergroup and call different queries on your Repo.
If your view is different from usergroup to usergroup, you should use different templates with partials for the same parts.
If the data of the views is the same, just change the template for each usergroup in the action. If not use different actions.
Here is a helper method for easily changing your templatefile.
/**
* This method can change the used template file in an action method.
*
* #param string $templateName Something like "List" or "Foldername/Actionname".
* #param string $templateExtension Default is "html", but for other output types this may be changed as well.
* #param string $controllerName Optionally uses another subfolder of the Templates/ directory
* By default, the current controller name is used. Example value: "JobOffer"
* #param \TYPO3\CMS\Fluid\View\AbstractTemplateView $viewObject The view to set this template to. Default is $this->view
*/
protected function changeTemplateFile($templateName, $templateExtension = 'html', $controllerName = null, AbstractTemplateView $viewObject = null)
{
if (is_null($viewObject)) {
$viewObject = $this->view;
}
if (is_null($controllerName)) {
$controllerName = $this->getControllerContext()->getRequest()->getControllerName();
}
$templatePathAndFilename = $this->getTemplateRootpathForView($controllerName . '/' . $templateName . '.' . $templateExtension);
$viewObject->setTemplatePathAndFilename($templatePathAndFilename);
}