Symfony2: Why weren't query string parameters included in the routing component? - symfony

I am porting a legacy application to Symfony2 and I am struggling because routing doesn't include query string parameters. Some quick examples: Suppose you have a search page for books where you can filter results based on criteria:
http://www.bookstore.com/books?author=Stephen+King&maxPrice=20
The nice thing about query string parameters in a case like this is you can have any number of filters, use the ones you want for any given request, and not crowd the URL with filters you're not using.
Let's say we rewrote the routing for the above query using the Symfony2 routing component. It might look like this:
http://www.mybookstore.com/book/any_title/stephen%20king/any_release_date/max_price_20/any_min_price/any_format/any_category
Even not taking into account how arbitrarily long an unclean that URL is I still don't think it is as intuitive because each 'segment' of that route is not a key value pair but instead just a value (e.g. author=Stephen+King > /stephen%20king/).
You can of course access query string parameters in the controller by passing the Request object into the action method (e.g. indexAction(Request $request) {) but then validating them and passing them into other parts of the application becomes a hassle (i.e. where I find myself now). What if you are using the Knp Menu Bundle to build your sidebar and you want parts to be marked as .current based on query string parameters? There is no functionality for that, just functionality to integrate with Symfony2 routes.
And how to validate that your query string parameters are acceptable? I am currently looking at validating them like a form to then pass them into the database to generate a query. Maybe this is the way the Symfony2 team envisioned handling them? If so I'd just really like to know why. It feels like I'm fighting the application.

I ended up actually asking Fabien this question at Symfony Live San Francisco 2012. There was another talk at the same conference in regards to this question and I will share with you the slides here:
http://www.slideshare.net/Wombert/phpjp-urls-rest#btnNext
Basically in the slides you can see that the author agrees with me in that query string parameters should be used for filtering. What they should not be used for is determining a content source. So a route should point to a product (or whatever) and query string parameters should then be used in your controller to apply whatever logic you require to filter that content source (as per Fabien).
I ended up creating an entity in my application that I bind all my query string parameters to and then manipulate, much the same way forms are handled. In fact when you think about it it's basically the same thing.

Like in Symfony1, query strings are independent from the route parameters.
If you have a path defined as #Route("/page/{id}", name="single_page"), you can create a path in your view like this:
{{ path('single_page', { id: 3, foo: "bar" }) }}
The resulting URL will be /page/3?foo=bar.

Related

Handling Singular and Plural Controllers/Routes

I'm a little confused on how I should handle singular and plural routes and controllers in my web application.
The website is a simple quotes site - think Einstein, Shakespeare etc. not Insurance. Within the project I have a controller called `QuoteController'. The controller name is singular, so does this mean that the controller should only handle the display of single quotes? I.E.
/quote/love-is-a-battlefield-1
Do I then need another controller for the display of multiple quotes (plural)? For example:
/quotes/ (would default to most recent)
/quotes/new
/quotes/shakespeare
/quotes/popular
Is it convention, or good practice, to have separate controllers for singular and plural routes? I hope that makes sense.
Just because the default controllers of asp-mvc have singular names, it doesn't mean you should implement singular form for all your controllers.
The correct answer is: it depends on the quantity of the entity that your controller represents.
Singular, example the AccountController is singular because it represents actions (action method) pertaining to a single account only.
Plural If your controller contains at least one action method that handles multiple entities at a single transaction.
Example plural format
users/update/3
The route above makes you think you are editing all users, which does not makes sense if you read it like a sentence. However, if you read your route like query, it will make much more sense.
If we think about it, a route IS a query: {entities}/{action}/{parameter} looks like a query to me.
users/ shorthand of users/all reads "select all users table"
users/123 reads "select single entity from users table"
users/update/123 reads "update single entity from users table"
Major sites use plural format, see sample below
stackoverflow.com/questions <- list of questions (multiple)
stackoverflow.com/questions/18570158 <- individual question (single)
stackoverflow.com/questions/ask <- new question (single)
stackoverflow.com/users <- display list of users (multple)
stackoverflow.com/users/114403 <- individual user (single)
asp.net/mvc/tutorials <- display list of tutorials (multiple)
asp.net/mvc/tutorials/mvc-5 <- individual tutorial (single)
facebook.com/messages/ <- Display list of messages (multiple)
facebook.com/messages/new <- Create a single message (single)
facebook.com/messages/john <- view individual messages (multiple)
I believe that English grammar should be strictly incorporated in every programming aspect. It reads more natural, and leads to good code hygiene.
Answer
Here's a question asked in programmers StackExchange that suggests keeping class names singular. I especially like the logic in one of the answers, "The tool to turn screws with is called "screw driver" not "screws driver"." Which I agree with. The names should be kept to nouns and adjectives.
As far as routing goes, best practices seems to favor that routes be pluralized nouns, and to avoid using verbs. This Apigee blog says, "avoid a mixed model in which you use singular for some resources, plural for others. Being consistent allows developers to predict and guess the method calls as they learn to work with your API." and suggests using singular or plural based off what popular websites have done. Their only example for using singular nouns in the route is the Zappos site, which has http://www.zappos.com/Product as a route; but if you examine the site, it's not exactly a route to a product resource but is instead a query, probably for all their products. You can enter http://www.zappos.com/anything and get a results page. So I wouldn't put too much stock into that.
Other blogs like this one from mwaysolutions (random find) say to "use nouns but no verbs" and "use plural nouns". Other points aside, most blogs/articles tend to say the same thing. Plural nouns and no verbs.
TL;DR: Use singular nouns for controllers and plural nouns for routes.
Controllers
Controllers and routes represent two different concepts. The controller is a class, or a blueprint. Similar to a stamper, I wouldn't say I had a UnicornsStamper I'd say I had a UnicornStamper that makes a stamp of a unicorn with which I could make a collection of unicorn stamps with. Collections, Enum types that are bit fields, static class that is a collection of properties (acts like a collection), and probably a few other edge cases are where I'd use a plural name.
Routes
A(n) URL is an address to a resource, thus the name Uniform Resource Locator. I disagree that "a route IS a query". Routes can contain queries (query string) to narrow down returned resources but the route is the location or the resource(s) that's being requested.
With the advent of attribute routing, pluralizing routes for controllers is as easy as adding a [RoutePrefix("quotes")] attribute to the QuoteController declaration. This also allows for easier design for associative routes. Looking at the sample routes provided in the original question:
/quotes
GET: Gets all quotes
POST: Creates a new quote
/authors/shakespeare/quotes
Associative route in the QuoteController
GET: Gets all Shakespeare quotes
/quotes/new
This is a bad route IMO. Avoid verbs.
Make a POST request to '/api/quotes' to create a new quote
/quotes/shakespeare
/quotes/popular
Although these would be nice to have, they're not practical for routing (see below)
The problem with the last two routes is that you have no simple way of differentiating between popular quotes and quotes by authors, not to mention that routes that link to quotes by name or Id. You would need actions decorated with [Route("popular")] followed by [Route("{author}")] (in that order) in order for the routing table to pick the routes up in the appropriate order. But the author route kills the possibility of having routes [Route("{quoteName}")] or [Route("{quoteId}")] (assuming quoteId is a string). Naturally, you'll want to have the ability to route to quotes by name and/or ID.
Slightly Off Topic
Getting quotes by author would best be done by an associative route. You could still probably get away with the popular route as a static route name, but at this point you're increasing route complexity which would be better suited for a query string. Off the top of my head, this is how I might design these routes if I really wanted to have the popular search term in the route:
a:/authors/{authorName}/quotes
b:/authors/{authorName}/quotes/popular
c:/authors/{authorName}/quotes?popular=true
d:/quotes/popular
e:/quotes/popular?author={authorName}
f:/quotes?author={authorName}&popular=true
g:/quotes/{quoteName|quoteId}
These could be spread across the QuoteController's actions. Assuming the controller has a [RoutePrefix("quotes")] attribute:
[HttpGet]
[Route("popular")]
[Route("~/authors/{authorName}/quotes/popular")]
// Handles routes b, d, & e
public ViewResult GetPopularQuotes(string authorName)
return GetQuotes(authorName, true);
[HttpGet]
[Route("")]
[Route("~/authors/{authorName}/quotes")
// Handles routes a, c, & f
public ViewResult GetQuotes(string authorName, bool popular = false)
// TODO: Write logic to get quotes filtered by authorName (if provided)
// If popular flag, only include popular quotes
[HttpGet]
[Route("{quoteId}")]
// Handles route g
public ViewResult GetQuoteById(string quoteId)
// TODO: Write logic to get a single quote by ID
Disclaimer: I wrote these attributes off the top of my head, there may be minor discrepancies that would need to be ironed out, but hopefully the gist how to get these routes to work comes through.
Hopefully this helps to clear up some confusion on the topic of controller and routing best practices on naming conventions. Ultimately the decision to use plural or singular controllers or routes is up to the developer. Regardless, be consistent once you pick a best practice to follow.
Name of controller can be plural or singular based on the logic it executes. Most likely we keep controller name as singular because ProductController sounds little better than ProductsController.
/product/list or /products/list
/product/add or /products/add
You can use both. But you must keep consistency and you should not mix them. Either all URL should be plural for every entity types or all should be singular.
In ASP.NET sample, they have used Singular controller names Such as HomeController, AccountController. In case of HomeController you can't use HomesController because that no longer represents current site Home.
With regards to logic, mostly we create Controller per database entity, in which we infer that Controller represents Actions to be performed on "Entity" So it is good to use Singular controller name and there is no harm.
Only if you want to create set of Controller representing collection of something that should look like or map to plural database table names then you can name that as plural.
Good question. Some contributors to this site would recommend you try Programmers website, but I'm willing to attempt an answer to your question.
Routing mechanism in ASP.NET MVC ,conceptually, is based on Resource-oriented architecture; the common guideline in ROA is
Applications should expose many URIs (possibly an infinite number of them), one for each Resource (any resources in your applications should be unambiguously accessible via a unique URI)
So, it's up to you to decide whether quote and quotes are two different resources or not.

Pass data in DurandalJS to other view

I'm developing a SPA webapplication with DurandalJS, but I don't understand something.
Let's say I have page 1 and 2. On page 1 there is a modal that creates a entity in the database with BreezeJS and gives back a key (that was generated in the database).
I want to pass this fetched key and then go to my second page, page 2. I know how I can do this when I put it in the url
http://localhost:60312/#/page2?orderid=2&repairorder=2
But this is not the way for Durandal, or is it? A user can enter a number of his own with al consequences!
How can I solve this?
I would like to add something to the very good #Joseph Gabriel answer. In durandal you can do this:
router.map( url: 'page2/:orderid/:repairorder',
name: 'Page2',
moduleId: 'viewModels/page2');
http://localhost:60312/#/page/2/2
But also, you can do this:
router.map( url: 'page2',
name: 'Page2',
moduleId: 'viewModels/page2');
http://localhost:60312/#/page2?orderid=2&repairorder=2
You don't nedd to specify all the parameters that you are pasing. Yo can get this parameters from the input of the active method.
var activate = function(context) {
console.log(context.orderid);
console.log(context.repairorder);
}
Sometimes the second solution can be more appropriated if you want that the user can store the url to repeat a query only copping that in the browser.
As far as I know, the only seamless way to pass parameters between view models in a Durandal app is to use the normal hash-based url parameters.
In you example, you can define a router that takes a orderId and repairOrder parameters, then your url would look something like this: http://localhost:60312/#/page/2/2
This is best option, if your data isn't too sensitive. Leave the navigation parameters in the open, and handle the values with care in the second view model. In other words, design your view model so that it can properly handle any values - even if a user directly modifies a parameter value, your view model should be able to handle it correctly.
It can be advantageous to be able to set url parameters directly, but you do have to be careful to ensure the integrity of the values before consuming them.
However, if you need to hide the parameters, you do have some different options:
Encryption: Use an encryption library like tea.js, and encrypt the value before adding it as a parameter value for navigation. Then, of course, decrypt it on the second page before using it. This allows Durandal's router navigation to work normally, while preventing users from supply their own values.
If you really need to prevent users from entering their own values, and you can bear the overhead of a few extra KB's, this is a good approach as long as you only need to pass a few parameters.
Shared data model: Use a singleton model which is shared between the two view models. This model can be manually required() as needed, and essentially serves as a repository for the application state that is shared between one or more view models.
This works fine, but it's kind of like having a big global variable - it can get messy if it's overused.
Modify VM directly: (Only for singleton view models)
Manually require() the second view model and set its properties before navigating to it.

Authorize request in ASP.NET Web API based on specific user

I followed this tutorial http://www.tugberkugurlu.com/archive/api-key-authorization-through-query-string-in-asp-net-web-api-authorizationfilterattribute
to create custom Authorization filter.
I have CarController with my custom Authorize Attribute:
[ApiKeyAuth("apiKey", typeof(ApiKeyAuthorizer))]
I send two parameters in the url .. host/Car/4?username=xxx&pass=xxx
It works basically fine, however I want to allow only car owners to see information about their cars.
E.g. user ABC can see only host/Car/5 and user DEF can see host/Car/6 and host/Car/10
how can I solve this scenario?
How can I access the id of the car used in query (host/Car/ID) in my ApiKeyAuthorizer.
Greetings
If you look at his code, https://github.com/tugberkugurlu/ASPNETWebAPISamples/tree/master/TugberkUg.Web.Http/src/samples and https://github.com/tugberkugurlu/ASPNETWebAPISamples/tree/master/TugberkUg.Web.Http/src/TugberkUg.Web.Http, I think you'll find that he's pulling the data directly from the query string. It should simply be a matter of extending that method to pull in the id parameter. You might also want to look at the RequestContentKeyValueModel on the HttpActionContext parameter passed into the OnAuthorization method. The documentation is sketchy and I haven't played with it yet, but that seems like a likely candidate to me. However, the route data is available indirectly through the HttpRequestMessage via an extension method, specifically:
message.GetRouteData();

How to create a unique web page address in ASP.NET

Is there a standard way to create unique web page address in ASP.NET? I'm sending surveys to customers and will be including a link to the web page. For example:
http://www.mysurveypages.foo/survey/UniqueID
I would then customize the survey based on who I sent it to. I know it can be done by passing in a unique parameter to a page but I curious about doing it this way.
You can use Routing to map your url structure onto a particular page and parameter collection. Essentially, it allows you to convert parts of the url into url parameters for your page. This works in standard WebForms and is the basis upon which MVC chooses the appropriate controller/action signature to invoke.
Yup like dcp said GUID is a reasonable solution http://mywebsite.com/survey.aspx?ID=GUID
Suppose you are going to sent the survey to a list of users.
List<User> users = GetSurveyUsers();
foreach(User u in users)
{
Guid guid = Guid.NewGuid();
//Then store the user-guid pair into DB or XML...
}
The simplest solution would seem to be making UniqueID an incrementing field that can be used to get the appropriate user information out of the database. You could use numbers, or a Guid, or some alpha-numeric value, it really doesn't matter.
If you go with ASP.Net MVC then it is quite easy to map the Url (like the one you specified) to a controller action which gets the ID passed in as a parameter. Otherwise you will probably want to look into some sort of Url rewriting to make sure the Url can be nice and pretty.
A GUID approach is safest, if you're able to store it somewhere. If not you could use some existing unique data (e.g. customer id or email address) either directly (but be careful about leaking sensitive data) or hashed (but need to consider collisions).

ASP.NET MVC routing based on data store values

How would you tackle this problem:
I have data in my data store. Each item has information about:
URL = an arbitrary number of first route segments that will be used with requests
some item type = display will be related to this type (read on)
title = used for example in navigation around my application
etc.
Since each item can have an arbitrary number of segments, I created a custom route, that allows me to handle these kind of requests without using the default route and having a single greedy route parameter.
Item type will actually define in what way should content of a particular item be displayed to the client. I was thinking of creating just as many controllers to not have too much code in a single controller action.
So how would you do this in ASP.NET MVC or what would you suggest would be the most feasible way of doing this?
Edit: A few more details
My items are stored in a database. Since they can have very different types (not inheritable) I thought of creating just as many controllers. But questions arise:
How should I create these controllers on each request since they are related to some dynamic data? I could create my own Controller factory or Route handler or possibly some other extension points as well, but which one would be best?
I want to use MVC basic functionality of using things like Html.ActionLink(action, controller, linkText) or make my own extension like Html.ActionLink(itemType, linkText) to make it even more flexible, so Action link should create correct routes based on Route data (because that's what's going on in the background - it goes through routes top down and see which one returns a resulting URL).
I was thinking of having a configuration of relation between itemType and route values (controller, action, defaults). Defaults setting may be tricky since defaults should be deserialized from a configuration string into an object (that may as well be complex). So I thought of maybe even having a configurable relation between itemType and class type that implements a certain interface like written in the example below.
My routes can be changed (or some new ones added) in the data store. But new types should not be added. Configuration would provide these scenarios, because they would link types with route defaults.
Example:
Interface definition:
public interface IRouteDefaults
{
object GetRouteDefaults();
}
Interface implementation example:
public class DefaultType : IRouteDefaults
{
public object GetRouteDefaults()
{
return new {
controller = "Default",
action = "Show",
itemComplex = new Person {
Name = "John Doe",
IsAdmin = true
}
}
}
Configuration example:
<customRoutes>
<route name="Cars" type="TypeEnum.Car" defaults="MyApp.Routing.Defaults.Car, MyApp.Routing" />
<route name="Fruits" type="TypeEnum.Fruit" defaults="MyApp.Routing.Defaults.Fruit, MyApp.Routing" />
<route name="Shoes" type="TypeEnum.Shoe" defaults="MyApp.Routing.Defaults.Shoe, MyApp.Routing" />
...
<route name="Others" type="TypeEnum.Other" defaults="MyApp.Routing.Defaults.DefaultType, MyApp.Routing" />
</customRoutes>
To address performance hit I can cache my items and work with in-memory data and avoid accessing the database on each request. These items tend to not change too often. I could cache them for like 60 minutes without degrading application experience.
There is no significant performance issue if you define a complex routing dictionary, or just have one generic routing entry and handle all the cases yourself. Code is code
Even if your data types are not inheritable, most likely you have common display patterns. e.g.
List of titles and summary text
item display, with title, image, description
etc
If you can breakdown your site into a finite number of display patterns, then you only need to make those finite controllers and views
You them provide a services layer which is selected by the routing parameter than uses a data transfer object (DTO) pattern to take the case data and move it into the standard data structure for the view
The general concept you mention is not at all uncommon and there are a few things to consider:
The moment I hear about URL routing taking a dependency on data coming from a database, the first thing I think about is performance. One way to alleviate potentialy performance concerns is to use the built in Route class and have a very generic pattern, such as "somethingStatic/{*everythingElse}". This way if the URL doesn't start with "somethingStatic" it will immediately fail to match and routing will continue to the next route. Then you'll get all the interesting data as the catch-all "everythingElse" parameter.
You can then associate this route with a custom route handler that derives from MvcRouteHandler and overrides GetHttpHandler to go to the database, make sense of the "everythingElse" value, and perhaps dynamically determine which controller and action should be used to handle this request. You can get/set the routing values by accessing requestContext.RouteData.Values.
Whether to use one controller and one action or many of one or many of each is a discussion unto itself. The question boils down to how many different types of data do you have? Are they mostly similar (they're all books, but some are hardcover and some are softcover)? Completely different (some are cars, some are books, and some are houses)? The answer to this should be the same answer you'd have if this were a computer programming class and you had to decide from an OOP perspective whether they all have a base class and their own derives types, or whether they could be easily represented by one common type. If they're all different types then I'd recommend different controllers - especially if each requires a distinct set of actions. For example, for a house you might want to see an inspection report. But for a book you might want to preview the first five pages and read a book review. These items have nothing in common: The actions for one would never be used for the other.
The problem described in #3 can also occur in reverse, though: What if you have 1,000 different object types? Do you want 1,000 different controllers? Without having any more information, I'd say for this scenario 1,000 controllers is a bit too much.
Hopefully these thoughts help guide you to the right solution. If you can provide more information about some of the specific scenarios you have (such as what kind of objects these are and what actions can apply to them) then the answer can be refined as well.

Resources