How do I write a custom route? I'd like to write a route that returns 5 users (regardless of how many are in the database), but the available shorthands aren't cutting it.
I looked at the documentation, but I am still confused.
To write a custom route, simply pass a function in as the second argument to your route handler:
this.get('/users', function(db, request) {
return db.users.filter(i => (i < 5));
});
Related
I want to define a route like this:
Route::put('businesses/?!?/reviews/{review_id}', 'BusinessesController#editReview');
that match urls like:
businesses/-1/reviews/12or
business/abc/review/12
but calls the BusinessController::editReview method with only the review_id argument:
function edit_review($review_id) { ... }
Effectively making the ?!? part mandatory but ignoring its value.
EDIT: the point is the editReview method should have exactly one argument. Besides, I want to give the route a name and call it with exactly one argument:
route('business.review',['review_id'=>1])
EDIT: Now that I think about it, what is supposed to go in '?!?' part with the above route(...) call ?
This is typically solved using a nested controller (see the Laravel Docs, you need to scoll down a little bit)
Controller
class BusinessReviewController extends BaseController {
public function show($businessId, $reviewId)
{
//
}
}
Route
Route::resource('businesses.reviews', 'BusinessReviewController');
This enables you to call the routes like you want to (businesses/1/reviews/12 or route('business.review',['businessId' => 1, 'reviewId' => 12]))
Basically, this is a ResourceController as usual, having all the routes a normal ResourceController has. just run php artisan routes to see what you got (since your example was about edit, but mine should get you started...).
I am using AttributeRouting: http://attributerouting.net/
If I have two actions with the same name, but the route for the GET is different than the route for the POST, why is Url.Action generating a URL that matches on my GET action, even though my action method is pointing to the one that is a post?
I have tried passing the verb as an
Url.Action("CreateEdit", new { method = "POST" })
However this returns the string /enrollments/create instead of /enrollments/createedit which corresponds to the POST.
I wouldn't expect this to work but tried it as some route configurations use this technique:
Url.Action("CreateEdit", new { HttpMethodConstraint = new HttpMethodConstraint("POST") })
However both of these don't solve the problem, and the URL is still for my GET route.
I have verified that I can post to a hardcoded URL of /enrollments/createedit so the POST action does indeed have that route, just to rule out the possibility that the POST Action's default [POST] is defaulting to the expected route.
Problem is that I usually avoid hardcoded URL's and use Url.Action or Html.BeginForm (which also exhibits the same issue) so that all URLs are derived from action routes.
Here are how I have defined my actions:
[GET("create")]
[GET("edit/{id:guid?}")]
public ActionResult CreateEdit(Guid? id)
...
[POST] //default to "createedit"
public ActionResult CreateEdit()
...
Note this question is not about making the actual request, but is generating route urls.
How do you indicate to Url.Action that it should use the route from the [POST] instead of the GET. There is no overload that take constraints or HTTP Verb? Note that Html.BeginForm exhibits that same issue when generating the action URL attribute, which is clearly a POST oriented method.
It also does not work if I simplify my action routes to(although the above are my goal routes):
[GET("create")]
public ActionResult CreateEdit()
...
[POST("createedit")] //default to "createedit"
public ActionResult CreateEdit()
...
Url.Action("CreateEdit", new { method = "POST" }) returns "/enrollments/create" obviously because method = 'POST' is not the correct way to specify the desired route for Url.Action. Is there a way to tell Url.Action or Html.BeginForm to specify that the POST route is desired? The method param of BeginForm effects the method="post" html attribute, but the action attribute is still generated with the wrong /enrollments/create URL. I've seen area="Admin" used to specify Areas in route parameters. Is there some magic property that can be added to specify the verb? If there is not, then clearly there is no way to get the right URL consistently and I will probably need to rename one of my actions if I need to maintain the desired routes. My hope was there was some route value I could add analogous to new { area = "SomeArea" }. method = "POST" was just my wild guess at what might work.
The request pipeline respects route constraints, if they are both the same route, then it works fine. So if both were [POST("creatededit")] and [GET("create"] then it would work fine because the URLs for both are the same, and then when the actual request is made the distinction is made by the pipeline due to the HttpMethodContraint.
If I use parameters to make the routes distinct, it works but only if I eliminate the [GET("create")]
If Url.Action has no known/supported/documented way to take a route property then the answer may simply be that Url.Action doesn't have any way for you to tell it the desired VERB.
If someone asks: Is there a way to specify the area for Url.Action?
The answer is simply: Url.Action("SomeAction", new { area ="SomeArea" })
The answer is not: muck around with your routes so you don't have two actions of the same name in different areas(or any number of other things that don't address the question).
The answer is not: You shouldn't have actions of the same name in different areas. (It might be a valid point, but it doesn't create a useful resource for others who are in a situation where they absolutely must generate a URL for a different area).
I don't understand what is so complicated about that.
I'm not asking how to make a POST request. I know how to do that, but I don't intend to hardcode URLs into my views. That turns into a maintenance nightmare.
I could change my routes if my boss wasn't so set on SOE. So I can't eliminate the [GET("create")] route.
I can resolve by renaming an action like so:
[GET("create")]
[GET("edit/{id:guid?}")]
public ActionResult CreateEdit(Guid? id)
...
[POST("createedit")]
public ActionResult CreateEditPost()
...
That is a bit dirty, but works. It still doesn't answer the question.
If there is a way to specify the VERB for Url.Action, I think the answer would be a useful resource. Maybe not to as many people as Url.Action("SomeAction", new { area ="SomeArea" }) but it would be documenting a feature of Url.Action that for me has been elusive. Maybe it doesn't support it. Perhaps I will need to delve into the source code.
I goofed in my original answer. I wasn't thinking it through really and probably answered too quickly. Here's what it boils down to:
Url.Action is tied pretty inherently to the controller/action style routing. When there's two matching actions (overloads where one is a GET version and the other is a POST), the URL should be the same, and so the GET or really first version is returned. See, AttributeRouting just sort of layers on by letting you customize the outward facing URL, but internally, Url.Action is simply trying to find a route that will get you to the requested action. Once it finds a match, it assumes that's good enough, and especially pre-MVC5, it should have been.
MVC5 introduced attribute routing as a first-class citizen, but from what I've seen, this edge case (where the GET and POST versions of the same action have different URLs and you want the POST version in particular) has not been covered.
That said, I see a couple of potential workarounds:
Use different action names. If your POST action is named something like CreateEditPost, then you can very easily do Url.Action("CreateEditPost") and get the right URL. Since your routes are not affected directly by the action name, it doesn't really matter what it's called.
Use named routes. If you name your routes, then you can use Url.RouteUrl instead and request exactly the route your want. For example:
[POST("createedit", RouteName = "CreateEditPost")]
public ActionResult CreateEdit()
{
...
}
Then:
#Url.RouteUrl("CreateEditPost")
I'm guessing that the answer is to make multiple routes, but our route list is beginning to get long, so I'm looking to see if this can be expressed more elegantly and simply. :)
In my current example, I want to create a route that has multiple options, with some elements that are variable and some that are not.
So an example URI would be:
http://localhost/api/schedules/10/instances/5
And a matching route definition would be:
api/schedules/{id}/instances/{item}
The possible endpoints would be:
http://localhost/api/schedules/10 // returns schedule number 10
http://localhost/api/schedules/10/instances // returns all instances in schedule 10
http://localhost/api/schedules/10/instances/5 // returns 5th instance in schedule 10
What seems to cause trouble is wanting to have a fixed keyword between the two optional parameters. I've tried the following definition (and several variants):
RouteTable.Routes.MapHttpRoute(
"RouteName",
"api/schedules/{id}/instances/{item}",
new {
id = RouteParameter.Optional,
instances = RouteParameter.Optional,
item = RouteParameter.Optional,
controller = "SomeController"
}
)
With that route, just hitting /schedules/10 is not recognized, and /schedules/10/instances and schedules/10/instances/5 both return the schedules/10/instances result.
Is this possible in some way, or should I be making two routes, one for /schedules/{id} and one for /schedules/{id}/instances/{item}?
As a side note, I'm working on a Web API project, but as far as I know, that's just a specific case of an MVC solution, so I tagged both as MVC and Web API.
Yes you can see my answer here for a full explanation of how to achieve what you want.
MVC 4.5 Web API Routing not working?
Why params plugin magic function default forwarding route?
Why zf not able to get function from parameter like sample below?
public function __invoke($param = null,$type='Route', $default = null)
{
if ($param === null) {
return $this;
}
$type = 'from'.ucfirst($type);
// Need to check function exist, if not must throw some error
return $this->$type($param, $default);
}
Example use
$this->params('name','post');
$this->params('token','get');
$this->params('action'); // this will return from route like default one.
How can i extend default params plugin like this? And is it a good move?
The default params are always fetched from the Route. This is because ZF2 encourages people to do lot of manual routing. This is both for speed purposes, as well as SEO purposes.
$this->params('paramname', 'defaultValueIfNotFound');
In the cases where you need the params from specific regions of the request, you can do that, too, using the params-plugin itself. But this has already been explained greatly by #Matsemann
How to access Route/Post/Get/etc parameters in Zend Framework 2
You could extend the params plugin, but you should use the third parameter for the additional option. Bear in mind though, that the other approach is simply cleaner and ultimately requires less work by the plugin. No switch-statement just to get a param ;)
At this moment I am calling my index view in one of two ways. Using the normal http view and serializing it JSON. In order to test it I am using the code below, and it works. I want to get it with a http get call. Like (http://localhost/article,json or something similar. Any ideas.
$.getJSON("/Article", function(json) {
$.each(json, function(i, article) {
alert(article.title);
});
});
At this moment the index called to /article is being differentiated with the following IsAjaxRequest method. but my real question is if I am able to get around the .getJSON method in JQuery to test the following code.
if (Request.IsAjaxRequest()) {
return Json(articles);
} else {
return View(articles);
}
If you are trying to reuse the same action method for both a full GET (the page load) and an AJAX call (via getJSON), you'll run into issues because each action method should have a unique name. Otherwise, the MVC engine can't tell which action method should be called when a particular Url is requested.
You'll need two separate actions: one for the full page load that returns a ViewResult and the other for the AJAX call that returns a JsonResult. If you need the Urls for these actions to look the same, you can also play around with mapped routes that direct to different action methods.
So, how about:
/Article/Index
Maps to the default Index action (full page load)
/Article/Refresh
Maps to the Refresh action (asynchronous JSON call)
I'm not sure I understand the question correctly, but can't you make an optional parameter called "format", so that you pass in ?format=json to switch what reply type you get back?
if(Request.IsAjaxRequest() || (!string.IsNullOrEmpty(format) && format.Equals("json"))
{
...
}
If you're wondering how to test your action and you're talking about doing automated testing, see if this post answers your question.