How do I combine CatchAll and EndsWith in an MVC route? - asp.net-mvc-3

The following route will match any folder structure below BasePath:
http://BasePath/{*SomeFolders}/
How do I create another route which will match any zip file below the same BasePath structure?
I tried this...
http://BasePath/{*SomeFolders}/{ZipFile}
... but it errors with
A path segment that contains more than one section, such as a literal section or a parameter, cannot contain a catch-all parameter.
Parameter name: routeUrl
How should I approach this?
*Update*
The original requirements are in fact flawed.
{ZipFile} will match the final section regardless of what it contains. (File or Folder)
In reality I believe the route pattern I'm looking to match should be:
http://BasePath/{*SomeFolders}/{ZipFile}.zip

It seems that constraints are the answer here.
routes.MapRoute( _
"ViewFile", "BasePath/{*RestOfPath}", _
New With {.controller = "File", .action = "DownloadFile"}, _
New With {.RestOfPath = ".*\.zip"}
)
routes.MapRoute( _
"ViewFolder", "BasePath/{*RestOfPath}", _
New With {.controller = "Folder", .action = "ViewFolder"} _
)
or for those of you who prefer C#...
routes.MapRoute(
"ViewFile", "BasePath/{*RestOfPath}",
new {controller = "File", action = "DownloadFile"},
new {RestOfPath = #".*\.zip"}
);
routes.MapRoute(
"ViewFolder", "BasePath/{*RestOfPath}",
new {controller = "Folder", action = "ViewFolder"}
);
(Erm I think that's right)
The same route is registered twice, with the first variation being given the additional constraint that the RestOfPath parameter should end with ".zip"
I am given to understand that Custom Constraints are also possible using derivatives of IRouteConstraint.

Catch all anywhere in the URL - exactly what you need
I've written such Route class that allows you to do exactly what you describe. It allows you to put catch-all segment as the first one in the route definition (or anywhere else actually). It will allow you to define your route as:
"BasePath/{*SomeFolders}/{ZipFile}"
The whole thing is described into great detail on my blog post where you will find the code to this Route class.
Additional info
Based on added info I would still rather use the first route definition that doesn't exclude file extension out of the route segment parameter but rather add constraint for the last segment to be
"[a-zA-Z0-9_]+\.zip"
So routing should still be defined as stated above in my answer, but contraint for the ZipFile should be defined as previously written. This would make my special route work out of the box as it is now.
To also make it work for other route delimiters (like dot in your example) code should be changed considerably, but if you know routing very well how it works, you can change it to work that way.
But I'd rather suggest you keep it simple and add a constraint.

As the error says, your catchall has to be the last param in your route.
To get the ZipFile part out, you will have to parse it from the SomeFolders catchall:
public ActionResult MyAction(string SomeFolders)
{
// parse the ZipFile out from the SomeFolders argument
}
So if you have /folder1/folder2/folder3/file.zip, you can do this:
var zipFile = SomeFolders.SubString(SomeFolders.LastIndexOf("/") + 1);

Related

Construct routes(urls) with slugs separated by dash in laravel

I am about to make more SEO-friendly URLs on my page and want a pattern looking like this for my products:
www.example.com/product-category/a-pretty-long-seo-friendly-product-name-12
So what are we looking at here?
www.example.com/{slug1}/{slug2}-{id}
The only thing I will care about from the URL in my controller is the {id}. The rest two slugs are just of SEO purpose. So to my question. How can I get the 12 from a-pretty-long-seo-friendly-product-name-12?
I have tried www.mydomain.com/{slug}/{slug}-{id} and in my controller to try and get $id. Id does not work. I am not able to able to separate it from from a-pretty-long-seo-friendly-product-name. So in my controller no matter how I do I get {slug2} and {id} concatenated.
Coming from rails it is a piece of cake there but can't seem to figure out how to do that here in laravel.
EDIT:
I am sorry I formulated my question very unclear. I am looking for a way to do this in the routes file. Like in rails.
You're on the right track, but you can't really logically separate /{slug}-{id} if you're using dash-separated strings. To handle this, you can simply explode the chunks and select the last one:
// routes/web.php
Route::get('/{primarySlug}/{secondarySlugAndId}', [ExampleController::class, 'example']);
// ExampleController.php
public function example($primarySlug, $secondarySlugAndId){
$parts = collect(explode('-', $secondarySlugAndId));
$id = $parts->last();
$secondarySlug = $parts->slice(0, -1)->implode('-');
... // Do anything else you need to do
}
Given the URL example.com/primary-slug/secondary-slug-99, you would have the following variables:
dd($primarySlug, $secondarySlug, $id);
// "primary-slug"
// "secondary-slug"
// "99"
The only case this wouldn't work for is if your id had a dash in it, but that's another layer of complexity that I hope you don't have to handle.
Route::get('/test/{slug1}/{slug2}','IndexController#index');
public function index($slug1, $slug2)
{
$id_slug = last(explode('-',$slug2));
$second_slug = str_replace('-'.$id_slug,'',$slug2);
dd($slug1, $second_slug,$id_slug);
}

How can I change name of dynamically generated URL in Codeigniter

I'm having dynamic url for my each search result in Codeigniter, but I want to know how can I change my url. For example right now I'm having the url something like this:
www.xyz.com/vendor/vendor_details?iuL80rpoEMxCi89uK6rIyTgqCGuagQ+BUoUnvyBdx09EawMiFfnaB+q3Q8YyBSFwbOVw8+32ZInJrjE2I42teA==
but I want it like this:
www.xyz.com/Delhi/Balaji-Courier-And-Cargo-Bharat-Singh-Market-Opposite-B-7-Petrol-Pump-Vasant-Kunj/011P1238505881A9D9W7_BZDET?xid=RGVsaGkgSW50ZXJuYXRpb25hbCBDb3VyaWVyIFNlcnZpY2VzIEhhbWlsdG9uIFJvYWQ=
here the example
$route['vendor/(:any)/(:any)/(:any)'] = 'vendor/vendor_details/$3';
See this Documentation here: routing
You want to add
www.example.com/city/address/related_id
This Example can help you to get that.
Here are a few routing examples:
$route['journals'] = 'blogs';
A URL containing the word “journals” in the first segment will be remapped to the “blogs” class.
$route[blog/Joe'] = 'blogs/users/34';
A URL containing the segments blog/Joe will be remapped to the “blogs” class and the “users” method. The ID will be set to “34”.
$route['product/(:any)'] = 'catalog/product_lookup';
A URL with “product” as the first segment and anything in the second will be remapped to the “catalog” class and the “product_lookup” method.
$route['product/(:num)'] = 'catalog/product_lookup_by_id/$1';
A URL with “product” as the first segment and a number in the second will be remapped to the “catalog” class and the “product_lookup_by_id” method passing in the match as a variable to the method.

Laravel pass parameters to route/action

If I want to construct this url: /categories/5/update/?hidden=1 how could I pass both {id} param and hidden param (as GET) ?
My route is:
Route::get('categories/{id}/update', 'CategoryController#update');
I don't want to make a form and put it as POST because I have a number of buttons which simply hides/shows/removes a category and dont want to make a lot of forms for simple actions, although it has nothing to do with the question
I'm just a little bit confused, because it seems like action('CategoryController#update', [$id, 'hidden' => 1]) constructs the right URL but I got no idea how it's distinguished that the first one ($id) must be in URL and the second is a GET param
You may also try this to generate the URL:
$action = action('CategoryController#update', [id => $id]) . '?hidden=1';
Also, query string could be passed with any route even without mentioning about that in Route declaration.

Custom Query Parameters without Appending Them to URL

I have 'urlFormat'=>'path' and 'showScriptName'=>false in the urlManager.
I have proxies/read as controller/action and article=>some_name as parameters.
Whenever I create a link such as:
$this->createUrl('proxies/read', array('article'=>$name));
The result is an URL of the type:
proxies/read?article=socks5_proxy_list
I would like to dump the query parameter and reformat the URL to look like this:
controller/action/param_name/param_value
In this case that would be:
proxies/read/article/socks5_proxy_list
My current 'rules' look like this:
'rules'=>array(
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>/article/<article:\w+>'=>'<controller>/<action>/article/<article>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
),
But they don't seem to be working.
Shortcut to make this work:
$this->createUrl('proxies/read/article/'.$name);
And leave the urlManager rules without your custom rule.
Another way:
// urlManager
'rules'=>array(// order of rules is also important
'<controller:\w+>/<action:\w+>/article/<article:\w+>'=>'<controller>/<action>',
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
),
In the above array, i just put the new rule in the beginning, to make sure that, that rule is applied whenever such a pattern is encountered. If you have other rules, then just make sure that this rule appears before a more general rule, that can match the pattern. Rule of thumb: more specific rule should appear before general rule.
create url in view:
$this->createUrl('proxies/read', array('article'=>$name));
Incase you don't need any of the default "user-friendly url" rules, but only need path format urls, then you only need to specify 'urlFormat'=>'path' and leave the 'rules' array empty or omitted all together.
Read the URL Management guide in the definitive guide, if you haven't already.
no need for rules..
http://yourhost.com/mycontroller/dosomething/param1/value/param2/value
class MyController extends Controller {
public function actionDosomething($param1, $param2) {
}
}
as for createUrl(). hand it a key=>value array of parameters as a second parameter
http://www.yiiframework.com/doc/api/1.1/CController#createUrl-detail
try these set of rules
'rules'=>array(
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>/*'=>'<controller>/<action>',
),

Best practice in handling invalid parameter CodeIgniter

Let's say I have a method at Controller named
book($chapter,$page);
where $chapter and $page must be integer. To access the method, the URI will look like
book/chapter/page
For example,
book/1/1
If user try to access the URI without passing all parameter, or wrong parameter, like
book/1/
or
book/abcxyz/1
I can do some if else statements to handle, like
if(!empty($page)){
//process
}else{
//redirect
}
My question is, is there any best practice to handle those invalid parameters passed by user? My ultimate goal is to redirect to the main page whenever there is an invalid parameter? How can I achieve this?
Using the CodeIgniter routing in config/routes.php is pretty useful here, something like this:
$route['book/(:num)/(:num)'] = "book/$1/$2";
$route['book/(:any)'] = "error";
$route['book'] = "error";
Should catch everything. You can have pretty much any regular expressions in the routes, so can validate that the parameters are numeric, start with a lowercase letter, etc..
The best logic here seems to be adding the default values:
book($chapter = 1, $page = 1);
and then checking if they are numeric
So it automatically opens the 1st page of the 1st chapter if there are parameter missing or non-numeric.

Resources