Rails routing, deep nesting for external resources - ruby-on-rails

I understand that it's a good practice to only include in the URL the parameters needed to determine the object of the model.
If I have 2 models, Post and Comment... a Post has many comments and a comment belongs to one post. A URL for a comment can be
/comment/:comment_id
and from associations I can determine which Post it belongs to but
Some rails apps need to access external resources(Via APIs for example). If the rails app needs to replicate a part of another external source, what is the right way to handle URLs and routing?
If for example a post has some comments, The URL for a comment can be
/post/:post_id/comment/:comment_id
or
/comment/:comment_id
The latter has one disadvantage which is that I can't determine which post it belongs to if the API of the external source doesn't determine that and this would cause some problems with navigation through the app but it's a short URL and allows the user to easily manipulate the URL to get another comment(which I see as an advantage). At the same time using the first(long) link would make the URL so long but I can know which post it belongs to.
The only solution I can think of is to make both possible but the user would never know that the short one exists if I make the long one the default. What do you think?

I always use the longer / spelled-out version myself. I don't mind that it's long and I can only see good things come from it as you're discovering here. I also think it's an advantage because then you can do things like this:
post = Post.find_by_id(params[:post_id])
comment = post.comments.find_by_id(params[:id])
The point being that you can't go "comment fishing" this way. You have to have the right post context in order to get at a specific comment. This may not matter a whole lot if comments aren't at all sensitive, but there are many thing in a web app that may be. So scoping finds by a root object (like post here) allows a quick permissions check that can be reused and without having to check on parent objects.
Anyway, that's my 2 cents. I never understood why people take offense to the longer urls. If they work for you then don't be afraid to use them!

Related

Do paths like /profile make proper URIs? Do they violate REST? What are the implications?

I'm developing an app on which regular users should have read and write permissions on their own data, while admins have read permission on everybody's.
In my design, admins can:
GET /users
GET /users/:id
But for regular users, two routing schemas came to mind. The first one being just a continuation of the first:
GET /users/:id
GET /users/:id/edit
PATCH /users/:id
and the second being another resource that is dependent on the user that's logged in:
GET /profile
GET /profile/edit
PATCH /profile
The advantage I see on the second approach is that the design itself doesn't allow users to change the URL and try to edit other people's records.
However, Wikipedia says:
A Uniform Resource Identifier (URI) is a string of characters that unambiguously identifies a particular resource.
and as I understand it, /profile doesn't fit that description since different users will see and update different records.
So, the questions are:
Does /profile make a proper URI?
Does it violate REST?
What might be other implications of such design?
Thanks <3
PS: probably URN is a more accurate term than URI in this situation.
As best I can tell, it isn't really a good idea, but you will probably get away with it if you go that route.
First, it's important to recognize that one of the very powerful implications of URI that identify a resource is that you can easily share that URI (for example, pasting it into a message), and the recipient can just use it. In the usual case, the identifier means the same thing no matter who is using it, which is to say that both clients and the server all agree what the URI refers to.
You lose some of that semantic agreement when you start experimenting with providing personalized representations of resources depending on the identify associated with the query.
A second issue is that the target-uri is an important element in HTTPs caching story; there are other condition in play, but a primary condition is whether the target-uri in the request matches the target-uri of the stored response.
So it's easy to image: Alice asks for a representation of some resource, but instead of seeing her own view of the resource, she sees a representation of Bob's view of the resource, because his was available in some public cache.
Which would be pretty awful.
That doesn't actually happen though; how do we tell Alice from Bob? The standard answer is that we have that information in the Authorization header field. HTTP caching, however, has special rules that take effect for shared caches when the request includes an authorization header.
So these rules are going to protect you unless you go out of your way to make a mess of it (for example, by using the public cache control directive).
In summary: can you? Yes, absolutely. Should you...? I eventually decided that I shouldn't. If I need to be clever with a pronoun URI then I will use it to redirect to the appropriate resource, rather than leaning upon content negotiation via the authorization header.
As with most questions, the answer is "it depends" - in this case it depends on who is the primary consumer of those URIs. If it's a user then /profile is perfectly acceptable since there's the additional requirement of user experience. Together with the state provided by the session cookie it uniquely represents a user. To give another example - which would be better on an e-commerce website /basket or /baskets/:id? Obviously it's the former since it allows a user to navigate directly to a URI without having to remember what their basket id is (which is likely to change over time).
Conversely, if the primary user is an API client then the format /users/:id may be more appropriate since that allows for a more consistent approach to coding. Though even here it may still be worthwhile providing some affordance with a URI like /users/current. Even if you follow the principle of HATEOAS in an API you'll still need to get the relevant URIs to call from some singleton resource like the root path.
In general the thing to remember is that these are guiding principles and not hard and fast rules - what makes sense for your application and context may not be the same for other people's applications.
I think the question is: "Should my route be called /profile based on the context of my program?" I don't think it should. I think you should have a base user and run something like permission levels. Like is_admin or is_moderator.

Does Delicious use GET requests for creation instead of POST, and why shouldn't I do the same?

I'm looking at the Delicious API and see the following is the operation to create a new bookmark:
https://api.del.icio.us/v1/posts/add?&url={URL}&description={description}
It looks like they're using a GET request to create server-side database entries, which I've read elsewhere shouldn't be done with GET requests, only with POST requests.
I'm writing my own API right now and I think that it's fabulous to let users interact with the API directly from the URL. But you can't do this unless you allow CRUD operations over GET.
So, is Delicious really doing CRUD operations over GET? Is there an important reason I shouldn't do the same thing in my API, or is POST just mandated for CRUD to prevent accidental invocation?
Accidental invocation is part of it; that's what the HTTP spec means when it talks about "idempotent" methods. But you could argue that what Delicious is doing is actually idempotent as long as the URL only gets added once no matter how many times you GET. But more importantly is that GET is safe:
The important distinction here is that the user
did not request the side-effects, so therefore
cannot be held accountable for them.
From an interface design standpoint, you want user-agents to make POST and PUT and DELETE more difficult than GET, or at least distinctly different, so that users can rely on that difference to hint when their actions might cause a change in the resource state, because they are responsible for those changes. Using GET to make changes, even if idempotent, blurs that line of accountability, especially when prefetchers are widely deployed.
That depends if you follow the REST principles GET for changing things is forbidden. Therefore most people say with REST use POST for changes.
However there is a difference between GET and POST. According to the RFC GET requests have always a followup RESPONSE. And if you use POST you need to follow the Redirect-After-Post pattern.
Another limitation is that URLs may have a limited size. So GET will only work as long as your input data is short enough. So the delicious API has there a bug. You will not be able to add every possible url via a GET parameter.

How can I strip request values out of my Rails url?

In my Rails 3 application, I list many items on the homepage. Some of them are obscure, and I would like to limit my list to only popular items unless the user clicks a specific link that basically "zeroes out" the limiter.
What I have now works, but when the user chooses to "Show all items", I end up with a ugly url:
http://myapp.com/?limiter=0
Is there any way that I can strip that out so that the user does not see the ugly attribute at the end of the url?
No, don't use POST. POST is only supposed to be used when you are making a state change on the server. Use an AJAX GET if you really need to do this.
Better yet, get used to seeing GET parameters like this. It's normal. And, it's like that for a reason: it allows bookmarking a resource, including whatever settings are needed to reproduce the request later.
Read up on REST. Learn it. Live it. Love it.
There's a number of approaches you could take. Probably the most obvious one is to have a separate page for your show_all. It sounds like you're trying to do too much with your homepage.
If you must have these on the homepage, and your link is also on the homepage, you could use an ajax call to load up your items without having to redirect to that url.
Finally I suppose you could try making a route just for this situation. I don't really have any experience with Rails3 routes, though, so I can't suggest any syntax.
Really, though, this smells like an application design problem, not a technical problem. I strongly encourage you to rethink how you are trying to do this. This doesn't sound like a feature that is appropriate to put on your homepage. Make a separate show_all action.

Using GET instead of POST to delete data behind authenticated pages [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I know you should use POST whenever data will be modified on a public website. There are several reasons including the fact that search engines will follow all the links and modify the data.
My question is do you think it is OK to use GET behind authenticated pages in something like an admin interface?
One example would be a list of products with a delete link on each row. Since the only way to get to the page is if you are logged in, is there any harm in just using a link with the product ID in the query string?
Elaboration for comments:
I personally don't have any issues or difficulties in implementing the deletes with POST. I have just seen several examples of code in ASP.NET and ASP.NET MVC for "admin like" pages that use GET instead of POST. I am curious about peoples' opinion on the matter.
The temptation of using GETs is that you can create a bunch of delete links without creating dozens of forms per page, or resorting to JavaScript. Yet for various reasons that have already been mentioned, the web depends on GETs not being destructive.
The best practice, if generating one tiny form per delete link in on server is impractical, is to use a GET link to load up a confirmation page from the server which has a POST form that performs the delete. Then do some progressive enhancement:
Delete
If the server gets a GET to /controller/delete/x then serve up a confirmation page with a POST form. If the server gets a POST (or maybe a DELETE) request then do the deletion.
Some people learned some time ago that it's a very bad idea.
Google launched a new app to "speed up browsing" (Google Web Accelerator) that prefetched the linked pages in the browser (no attacks, no third party...), and when someone logged to such protected pages, well the app looked at all those links and said: "Hey, I'll prefetch those ones also because that way I have the page ready when the user requests it"
They have changed the behavior, but anyone can do anything similar any day.
It is still bad practice to use GET for destructive operations - even if it is hidden behind authentication - as it makes it possible (easier?) for someone with knowledge of that URL to exploit it (for example, using XSS). And of course, it is a bad design/coding practice as well, especially if you are trying to create a RESTful service.
There are probably many other reasons as well...
GET ought to be used to retrieve data idempotently and POST ought to be used to update data non-idempotently. That's all. It's certainly not a "best practice" to interchange the methods.
As to XSS and CSRF risks, to prevent the one just HTML-escape any user-controlled input during (re)displaying and to prevent the other just make use of request based tokens and/or captchas.
Yes.
Code may rely (correctly) on GET not being destructive. That code could run in the browser, and thus will be authenticated (link prefetching comes to mind).
It would be a bad practice to delete data based on a GET request. Technically, you can do it, but you'll be out of sync with most well written websites. You are basically creating a new set of rules for your user interface if your use GET requets for deletes. I consider the URLs of your website part of the user interface. If you sent somebody a link like http://www.fakesite.site/posts/delete?ID=1, they would expect to be displayed a page asking if they want to delete post with ID #1, not perform the actual delete.
I do this on pages where I know someone is logged in and I can verify the users right to delete something based on other data which I keep in my session. I would suggest adding a confirmation step: "are you sure you want to delete this thingy?"
GET and POST are very, very similar except for the fact that GETs have a limit on the length of the HTTP action because they are all URL based.
Since you won't be providing access to people who haven't authenticated I don't believe using gets is problematic.

Is it worth using "pretty URLs" if you don't care about SEO/SEM

I'm designing a hosted software-as-a-service application that's like a highly specialized version of 37Signal's Highrise product. In that context, where SEO is a non-issue, is it worth implementing "pretty URLs" instead of going with numeric IDs (e.g. customers/john-smith instead of customers/1234)? I notice that a lot of web applications don't bother with them unless they provide a real value (e.g. e-commerce apps, blogs - things that need SEO to be found via search engines)
Depends on how often URLs are transmitted verbally by its users. People tend to find it relatively difficult to pronounce something like
http://www.domain.com/?id=4535&f=234&r=s%39fu__
and like
http://www.domain.com/john-doe
much better ;)
In addition to readability, another thing to keep in mind is that by exposing an auto-incrementing numeric key you also allow someone to guess the URLs for other resources and could give away certain details about your data. For instance, if someone signs up for your app and sees that their account is at /customer/12, it may effect their confidence in your application knowing that you only have 11 other customers. This wouldn't be an issue if they had a url of /customer/some-company.
It's always worth it if you just have the time to do it right.
Friendly-urls look a lot nicer and they give a better idea where the link will lead. This is useful if the link is shared eg. via instant message.
If you're searching for a specific page from browser history, human readable url helps.
Friendly url is a lot easier to remember (useful in some cases).
Like said earlier, it is also a lot easier to communicate verbally (needed more often than you'd think).
It hides unnecessary technical details from the user. In one case where user id was visible in the url, several users asked why their user id is higher than total amount of users. No damage done, but why have a confused user if you can avoid it.
I sure am a lot more likely to click on a link when I mouseover it, and it has http://www.example.com/something-i-am-interested-in.html.
Rather than seeing http://www.example.com/23847ozjo8uflidsa.asp.
It's quite annoying clicking links on MSDN because I never know what to expect I will get.
When I create applications I try my best to hide its structure from prying eyes - while it's subjective on how much "SEO" you get out of it - Pretty URLs tend to help people navigate and understand where they are while protecting your code from possible injections.
I notice you're using Rails app - so you probably wouldn't have a huge query string like in ASP, PHP, or those other languages - but in my opinion the added cleanliness and overall appearance is a plus for customer interaction. When sharing links it's nicer for customers to be able to copy the url: customer/john_doe than have to hunt for a "link me" or a random /customer/
Marco
I typically go with a combination -- keeping the ease of using Rails RESTful routing while still providing some extended information in URLs.
My app URLs look something like this:
http://example.com/discussions/123-is-it-worth-using-pretty-urls/
http://example.com/discussions/123-is-it-worth-using-pretty-urls/comments
http://example.com/discussions/123-is-it-worth-using-pretty-urls/comments/34567
You don't have to add ANY custom routes to pull this off, you just need to add the following method to your model:
def to_param
[ id, permalink ].join("-")
end
And ensure any find calling params[:id] in your controller is converted to an integer by setting params[:id].to_i.
Just a note, you'll need to set a permalink attribute when your record is saved...
If your application is restful, the URLs that rails gives you are SEO-friendly by default.
In your example, customers/1234 will probably return something like
<h1>Customer</h1>
<p><strong>Name:</strong> John Smith</p>
etc etc
Any current SEO spider will be smart enough to parse the destination page and extract that "John Smith" from there anyway.
So, in that sense, customers/1234 is already a "nice" URL (as opposed to other systems, in which you would have something like resource/123123/1234 for customer 1234 resource/23232/321 for client 321).
Now, if you want your users to be regularly using urls (like in delicious, etc) you might want to start using logins and readable fields instead of ids.
But for SEO, ids are just fine.

Resources