I'm using Asp.Net MVC 4, and am unsure of the best way to approach a routing problem. The site needs to support static urls which are mapped to controller/actions in the usual way:
/about
/contact
/etc
But it also needs to map to items from the database:
/clothes
/clothes/jumper
/clothes/outdoor/blue-coat
I've managed to get this working by adding a constraint in my default route listing all my static controllers:
constraints: new { controller = "About|Contact|Etc" }
Then adding a catch all route:
routes.MapRoute(
"Error",
"{*url}",
new {controller = "CatchAll", action = "Index"}
);
Then the CatchAllController handles the non-static urls, and 404s for non-existing data.
Is this the best way of doing this? Or is it better to write a custom route handler? Or is there something else? I'm aware that the constraint regular expression could get quite long, and it's also another thing to remember to add when adding a new page.
Related
I have a JobPosts/Index page with multiple GET parameter bindings to allow filtering: let's take CityId and IsRemote for example. I don't want these to be passed as query string parameters, instead I want to use friendly routes for them. So I have defined these:
options.Conventions.AddPageRoute("/JobPosts/Index", "cities/{cityId}/jobs");
options.Conventions.AddPageRoute("/JobPosts/Index", "remote-jobs");
options.Conventions.AddPageRoute("/JobPosts/Index", "jobs");
The routes work just fine when I type them in the browser and the CityId one is bound properly, but two things are missing.
First, there is no way to specify a default value for my IsRemote param, which I want to set to true ONLY when using the remote-jobs URL.
And second, when trying to generate a URL like this:
<a asp-area="" asp-page="/JobPosts/Index" asp-route-cityId="#Model.CityId"></a>
I get the following URL:
https://localhost:44391/jobs?cityId=2265885
When what I actually expect is:
https://localhost:44391/cities/2265885/jobs
So it looks like the tag helper or the part responsible for constructing the URL doesn't look at all at the different routes to try and get a best match based on the list of parameters. Actually, it will always use the last page route defined for that page.
Nor do I have the option anywhere to specify a route name for the page route and then use asp-route to explicitly say which route I want.
Any ideas how to achieve that? Or if it's something that's on the roadmap for Razor Pages?
EDIT: Hardcoding the href is not an option. I want this to go through the proper routing services as there are other things to be done as well, like generating culture-specific URL for non-english users (eg. {cultureId}/cities/{cityId}/jobs - this is done through route conventions. Hardcoding the href would obviously bypass that.
There is a easy way to set IsRemote default value.
public bool IsRemote { get; set; } = true;
This tag asp-page will link to Page /JobPosts/Index.csthml directly,
https://localhost:44391/JobPosts?cityId=2265885
= https://localhost:44391/JobPosts/Index?cityId=2265885
If you are looking forward the URL https://localhost:44391/jobs?cityId=2265885
you could try this a tag to request.
Go to JobPosts
———————————————————————————————
Using a middleware to handle /remote-jobs
app.Run(next => async context =>
{
if (context.Request.Path == "/remote-jobs")
{
return View with default IsRemote
}
});
I have in the controller folder like this:
-Controller(Main Folder)
-HomeController(Main Controller)
-HomeBasic1Controller
-HomeBasic2Controller
-HomeBasic3Controller
-HomeBasic4Controller
When I execute the program, first it´s going to HomeController, until here everything is correct, then in HomeController I can call to any controller, I can go to HomeBasic1Controller or to any other HomeBasicXController.
The problem is in the URL, it´s showing like this
http://localhost:XXXXX/HomeBasic1
but I want to be like this
http://localhost:XXXXX/Home
no matter from which controller is calling!
The easy way to do this is to put all your actions on the Basic1,2,3, and 4 controllers all on HomeController. This is the generally accepted convention. If you can't do that, then what you want is "custom routing". The straightforward way to start this approach is to add a route for each action in Basic1 with each specific URL. A single route might look something like this:
routes.MapRoute(
"Basic1SomeAction",
"home/someaction",
new { controller = "HomeBasic1", action = "SomeAction" }
);
I'm using apiResource in Route which is using (index, create, show, update, destroy) methods in exampleController. When I would like to use show method the route wont work. what shall I do? I think it is because of {fruits} but I do not how solve it?
Route::apiResource('/fruit/{fruits}/apples', 'exampleController');
My route in browser is:
localhost:8000/api/fruits/testFruitSlug/apples/testAppleSlug
difference between apiResource and resource in route: Route::apiResource() only creates routes for index, store, show, update and destroy while Route::resource() also adds a create and edit route which don't make sense in an API context.
Already peoples added answers, I am just adding the route differences as visually :
Normal Resource controller
Route::resource('users', 'UsersController');
Gives you these named routes:
Verb Path Action Route Name
GET /users index users.index
GET /users/create create users.create
POST /users store users.store
GET /users/{user} show users.show
GET /users/{user}/edit edit users.edit
PUT|PATCH /users/{user} update users.update
DELETE /users/{user} destroy users.destroy
Api Resource controller
Route::apiResource('users', 'UsersController');
Gives you these named routes:
Verb Path Action Route Name
GET /users index users.index
POST /users store users.store
GET /users/{user} show users.show
PUT|PATCH /users/{user} update users.update
DELETE /users/{user} destroy users.destroy
To quickly generate an API resource controller that does not include the create or edit methods, use the --api switch when executing the make:controller command:
php artisan make:controller API/PhotoController --api
Try using the command line to generate your controller. It will save you stress. You can then do this in your route
Route::apiResource('photos', 'PhotoController');
I solved my question in below way:
public function show(Fruits $fruits, Apples $apples){
}
I found that I should give all variables in my function however I did not use all of them.
I started using Laravel 3 last week, and then found the new 4 release and I'm trying to convert now.
I have a dozen+ routes that I want to deliver to a specific controller method. i.e., "/api/v1/owners/3/dogs/1 or /api/v1/owners/3" to run "myresourcecontroller#processRequest"
In Laravel 3 I was able to use this: (note * wildcard)
Route::any('api/v1/owners*', 'owners#processRequest'); // Process tags resource endpoints
I found this example from the documentation but it gives me an error. I get a NotFoundHttpException.
//[Pattern Based Filters](http://laravel.com/docs/routing#route-filters)
Route::filter('admin', function()
{
//
});
Route::when('admin/*', 'admin');
Not sure what I'm doing wrong? Is there another way to do this?
I don't want to use the Laravel 4 restful controllers, cause they don't seem to conform to complete restful design. i.e., no verbs in the url.
I have all of my processing written, I just need to be able to route to it.
I need to be able to create new records by POST /api/v1/owners or /api/v1/owners/3/dogs
I cannot use /api/v1/owners/create.
I'm trying to avoid having to write a route for every endpoint, i.e.,
Route::any('api/v1/owners/{owner_id}', 'owners#processRequest');
Route::any('api/v1/owners/{owner_id}/dogs/{dog_id}', 'owners#processRequest');
Thank you for any help
You should make use of resourceful controllers as they're a great asset when building an API. The endpoints you described can be achieved using resource controllers and nested resource controllers.
Route::resource('owners', 'OwnersController');
Route::resource('owners.dogs', 'OwnersDogsController');
Would allow you to create an owner with POST localhost/owners and create a dog on an owner with POST localhost/owners/3/dogs.
You can then wrap these routes in a route group to get the api/v1 prefix.
Route::group(['prefix' => 'api/v1'], function()
{
Route::resource('owners', 'OwnersController');
Route::resource('owners.dogs', 'OwnersDogsController');
});
Haven't used Laravel myself, but try any('api/v1/owners/*', (note slash before asterisk) as in the example.
I'm pretty new to Zend Framework and I'm building a website hoping to implement good SEO practices.
The URL structure will be:
example.com/language/city/controller/action
So I've created this route in my bootstrap:
$front = Zend_Controller_Front::getInstance();
$router = $front->getRouter();
$route = new Zend_Controller_Router_Route(':language/:city/:controller/:action/*',
array('language'=>'es',
'city'=>'barcelona',
'controller'=>'index',
'action'=>'index'));
$router->addRoute('language_city', $route);
Which I'm not sure is OK but seems to do the trick.
What I notice next is all these URLs point to the same content (bad SEO practice):
/
/es
/es/barcelona
/es/barcelona/index
/es/barcelona/index/index
Is there a way to get around this duplicated content problem?
Thanks in advance!
You're setting the defaults, so for exactly one page (the default page) the request will be the same. It you removed the defaults, you'll get an error (404 I believe) if the URI doesn't contain the variable.
$route = new Zend_Controller_Router_Route(
':language/:city/:controller/:action/*',
array('language'=>'es', //default when not in URI
'city'=>'barcelona', //default when not in URI
'controller'=>'index', //default when not in URI
'action'=>'index' //default when not in URI
)
);
It seems like you may want to remove the defaults for language city, since without that data I'm not sure what your controller is going to do.
If you did that, the only 'duplicated' URIs will be:
/es/barcelona
/es/barcelona/index
/es/barcelona/index/index
But you only have to use one of those URIs. If you output links using Zend's View_Helper_Url, it'll drop the index/index off - since it matches the default value.
You can always add additional routes to map other requests (say /) to the relevant controller.
It should also be noted, if you only have a single controller handling all these 'city' requests, you don't need to put it on the URI:
$route = new Zend_Controller_Router_Route(
':language/:city/:action/*',
array('language'=>'es', //default when not in URI
'city'=>'barcelona', //default when not in URI
'controller'=>'index', //all requests route here
'action'=>'index' //default when not in URI
)
);
Then the only 'duplicate' URIs are:
/es/barcelona
/es/barcelona/index