I currently have a service with a REST API which is pretty standard:
show: GET /users/1
update: PUT /users/1
...and some has_many relationships which follow the same convention:
show: GET /users/1/friends/1
update: PUT /users/1/friends/1
However, there is also an EAV table to handle settings (sorry, this part isn't going to change), set up to act as a has_one relationship. Internally:
user.settings # returns {:sound => true, :tutorials => true}
user.update_settings # expects {:sound => false}
It works well locally, but there's no ID to represent it the way the other routes work. Instead, the routes could be set up like this:
show: GET /users/1/settings
update: PUT /users/1/settings
Is this a normal way to handle this, or is there some other convention I'm not aware of?
Well, if you do not need an Id to access the settings, why not treat it as an attribute of the collection? Internally it's the same to you, just close the /users/:id/settings endpoint. And GET and UPDATE the user resource to change settings. The user would look somthing like this:
{ "id" : 12,
"settings": {
...
}
}
If you think of settings an attribute, the enpoint does not seem grammatically correct. Imagine you posed the same question regarding age of the user. Would you open a /user/:id/age endpoint? Is it all that the settings has more attributes? where to stop then? Again a matter of concepts and above all CONSISTENCY.
But don't be a REStafarian. Your approach is also good. For me is much more a matter of consistency so that you dont have to write tons of doc for your developers explaining exceptions. So make your choice taking into account what fits best with the rest (i'd go for my suggestion).
Be pragmatic!
Related
So lets say I have a form for submitting a new post.
The form has a hidden field which specify's the category_id. We are also on the show view for that very category.
What I'm worried about, is that someone using something like firebug, might just edit the category id in the code, and then submit the form - creating a post for a different category.
Obviously my form is more complicated and a different scenario - but the idea is the same. I also cannot define the category in the post's create controller, as the category will be different on each show view...
Any solutions?
EDIT:
Here is a better question - is it possible to grab the Category id in the create controller for the post, if its not in a hidden field?
Does your site have the concept of permissions / access control lists on the categories themselves? If the user would have access to the other category, then I'd say there's no worry here since there's nothing stopping them from going to that other category and doing the same.
If your categories are restricted in some manner, then I'd suggest nesting your Post under a category (nested resource routes) and do a before_filter to ensure you're granted access to the appropriate category.
config/routes.rb
resources :categories do
resources :posts
end
app/controllers/posts_controller
before_filter :ensure_category_access
def create
#post = #category.posts.new(params[:post])
...
end
private
def ensure_category_access
#category = Category.find(params[:category_id])
# do whatever you need to do. if you don't have to validate access, then I'm not sure I'd worry about this.
# If the user wants to change their category in their post instead of
# going to the other category and posting there, I don't think I see a concern?
end
URL would look like
GET
/categories/1/posts/new
POST
/categories/1/posts
pst is right- never trust the user. Double-check the value sent via the view in your controller and, if it does't match something valid, kick the user out (auto-logout) and send the admin an email. You may also want to lock the user's account if it keeps happening.
Never, ever trust the user, of course ;-)
Now, that being said, it is possible to with a very high degree of confidence rely on hidden fields for temporal storage/staging (although this can generally also be handled entirely on the server with the session as well): ASP.NET follows this model and it has proven to be very secure against tampering if used correctly -- so what's the secret?
Hash validation aka MAC (Message Authentication Code). The ASP.NET MAC and usage is discussed briefly this article. In short the MAC is a hash of the form data (built using a server -- and perhaps session -- secret key) which is embedded in the form as a hidden field. When the form submission occurs this MAC is re-calculated from the data and then compared with the original MAC. Because the secrets are known only to the server it is not (realistically) possible for a client to generate a valid MAC from the data itself.
However, I do not use RoR or know what modules, if any, may implement security like this. I do hope that someone can provide more insight (in their own answer ;-) if such solutions exist, because it is a very powerful construct and easily allows safe per-form data association and validation.
Happy coding.
Assume the following paths to be legitimate and resolving:
http://test.local/wizards/home
http://test.local/wizards/wizardfest2012/dates
http://test.local/dragons/
http://test.local/dragons/blog/stop-slaying-us
http://test.local/
This is (if you couldn't tell) for a CMS that includes a blog, so the slugs would be generated by the user. I have some routes to process first for reserved namespaces (admin, for example).
I assume that the user generated routes need to be routed to a Page controller - but, I don't think pragmatically adding a line to routes.rb is efficient. My question then, is how do I process the first part of the params (in this case, wizards and dragons) to get the correct information from the model?
Here's one of my ideas - split (somehow) the first part of the slug (again, wizards and dragons and pass the rest of the slug (for example, /wizardfest2012/dates) to the model to fetch the associated content.
Any thoughts on the most efficient way to do this?
I am not sure whether I understand what you want to achieve, but maybe this is what you want:
constraints :camp => /wizards|dragons/ do
match ':camp/home' => "pages#home"
match ':camp/blog/:title' => "pages#blog"
# ...and all the routes with known components
match ':camp/*other' => "pages#other"
end
You may create a before_filter which will recognize the params[:camp] and prepare the necessary models or whatever is needed.
The other action will receive the string "wizardfest2012/dates" as params[:other]. I hope that it was what you needed.
The "Rails Routing from the Outside In" guide may be worth reading, unless you have already read it.
I have a design question where I would appreciate a thoughtful response.
Let's say you have a simple application (for example's sake) that has a User, Company and Theme model. A Company has_one Theme and has_many Users.
Administrators (a User) can fully manage Companies, Users and Themes - the whole REST stack, in addition to a few other actions too. Administrators are expected to do things to all 3 of these resources that other user roles cannot do.
We also have a Company role. This role can edit their own Company, as well as select a Theme from the ones the admin-user added as nice defaults, or they can just make their own theme.
Companies can also add/edite/delete users, but only for their company. These pages will have different views and they'll have different behaviour from admins - some overlaps, but some things will be restricted while others will be added.
Now, here we have some non-trivial design choices, and I would like to know what the best-practice is.
PART 1
In Rails, it makes sense to have resources :users, :companies, :themes for the administrators and probably resource :company, :theme, :users for the Company users.
But of course, we run into some naming conflicts here - both singular and plural - so we might want to try something like resource :my_company, :my_theme, :my_users to separate them? Or is there a better solution?
Furthermore, a theme is just a component of a company, so maybe we want to nest them?
:resource :my_company do
:resource :theme
:resources :users
end
This works okay, but it could be confusing as to which UsersController we are referring to... no? This is really sticky and I would love to know how to deal with this. Do you have 1 controller, or 2? What do you name them?
So this would be an example:
http://myapp.com/my_company/theme/edit
http://myapp.com/my_company/users/1/delete
Company users also might want the list of themes via ajax, so is it correct for them to call:
http://myapp.com/themes.json
?
Is this how to approach this situation, or is there a better way?
PART 2
Also, what should your directory structure look? Should you have controllers separated by user role?
/app/controllers/admin/companies_controller.rb
/app/controllers/admin/themes_controller.rb
/app/controllers/admin/users_controller.rb
/app/controllers/company/my_company_controller.rb
/app/controllers/company/theme_controller.rb
/app/controllers/company/users_controller.rb
Or is there better ways to handle this?
It seems weird that users_controller is duplicated 2x and that there is a minor difference between Theme and Themes.
I would really appreciate a thoughtful response on this. Thanks!
I appreciate your desire to organize your codebase as I constantly have to convince myself not to take my default impulse to nest a resource or namespace a model. As there is no right answer to this question, I will just offer the reasons I use to convince myself not to.
A resource lives in once place. User.find(1) should have a single locator (URL), which I like to call user_path. I like calling it user_path because of all the times that I have made myself call it admin_company_user_path([#company, #user]) which malaise makes in me each time I write it.
That resource may render itself in different ways for different situations, like if the requester was an XHR or indicated that they would prefer German to English. Why is the header indicating that the user is an administrator any different?
If I can make it look like the simplest examples in the rails/README, shouldn't I?
At this point I would concede and end up with:
/app/controllers/companies_controller.rb
/app/controllers/users_controller.rb
/app/controllers/themes_controller.rb
And my routes.rb would have:
resources :users
resources :companies
resources :themes
I should also address how I would handle the thing that makes you want to separate them in the first place–a different view for each of the roles. In my ideal scenario, my decision would result in a themes/_form.haml that looks like:
= form.input :title if user_can_change_title?
= form.input :theme if user_can_change_theme?
And the rest of the differences would handled in CSS, with perhaps a stylesheet for each role.
In the less ideal scenario, I might be using:
= render :partial => "#{current_user.role}_form"
I'm doing a rewrite of an old Rails application and I thought I should do it in a RESTful manner, as a learning experience if nothing else.
I've reached some actions that toggles a boolean value, for example if an article is published or not.
Before I had a couple of actions: toggle_published, publish and unpublish.
They were very easy to use: i just made a link to them in the article-list.
How would you do the same thing in a RESTful manner?
Should I use the update-action, and build a mini-form to replace each link that I used before? I don't particulary like that idea.
Just a notice:
A toggle method is not RESTful, because the HTTP PUT verb is supposed to be idempotent (see a.o. http://en.wikipedia.org/wiki/Idempotence#Examples). This means that no matter how often you execute a method, it should always give the same result. A toggle method does not adhere to this principle, as it does not give the same result if you execute it once comparing to executing it twice.
If you want to make it RESTful, you should create two methods: one for setting and one for unsetting.
Making an application RESTful does not only mean that you should use the correct HTTP verb.
I'd probably solve it with PUT/DELETE or POST/DELETE on a nested "toggle resource". Perhaps not 100% completely restful but certainly easy enough to understand.
PUT or POST /articles/:id/published # Toggle published ON
DELETE /articles/:id/published # Toggle published OFF
GET /articles/:id/published # Get state RESTfully via status 200 (ON) or 404 (OFF)
Might seem a bit odd, but it is technically RESTful.
Update: A (perhaps) more natural approach might also just be:
PUT or POST /articles/:id/published Data: { state: true/false } # Toggle published ON
You could also use the PATCH verb with the actual article which I assume has a published property:
PATCH /articles/:id { published: true/false }
Because all the cool REST kids are using PATCH nowadays.
It sounds like you have two use cases:
set published state
toggle published state
You should be able to add a member route for the toggle action for:
/articles/<id>/toggle_published - calls Article.toggle(:published)
And use Article update on :published attribute via the standard REST resource route.
map.resources :articles, :member => :toggle
I like #Van der Hoorn answer
so in real life we are using in login & logout scenario
use post or put or patch
/users/login -> with some payload data
/users/logout
In Above Eg login & logout is almost acting like setting boolean Flag , Easy to read and set in db
Eg : so its no harm to use same idea in toggle context
use post or put or patch
/book/3/publish
/book/4/unpublish
Note . :
1 : use this approach if there is only 1 field to be toggled , else if there are multiple fields then general /book/4 a patch request with payload data will do
2 : use this approach if there is any security layer is implemented so it will be like
Eg :
Editor -> can access urls like `/books/:id` & `/books/:id/publish`
Senior Editor -> can access urls like `/books/:id` & `/books/:id/unpublish`
In a current project I need to support finding a User by login credentials and also by email address. I know that in RESTful design you use a GET to find resources. In Rails...
GET /users # => UsersController.index -- find all the users
GET /users/1 # => UsersController.show -- find a particular user
But I also need something akin to...
GET /users?username=joe&password=mysterio
GET /users?email=foo#bar.com
Is it conventional to add additional routes and actions beyond index and show?
Or is it more common to put conditional logic in the show action to look at the params and detect whether we're finding by one thing or another?
There's a similar issue with PUT requests. In one case I need to set a User to be "active" (user.active = true), and in another case I just need to do a general form-based editing operation.
Thanks guys. Eventually I'm going to figure out this REST stuff.
I'm new to SO, so I can't comment, but the checked green answer is not RESTful.
In a RESTful world, your controller grabs all the parameters and passes it to the model layer for processing. Typically, you shouldn't create another action.
Instead, you should do do something like this:
def show
#user = User.find_by_login_or_email(params[:user])
... #rest of your action
end
Your model can have a method like this:
class User
self.find_by_login_or_email(params)
return find_by_login(params[:login]) unless params[:login].blank?
return find_by_email(params[:email]) unless params[:email].blank?
nil #both were blank
end
end
Your view could look like this:
<%= f.text_field :user, :email %>
or
<%= f.text_field :user, :login %>
Note: untested code, so may be buggy...but the general line of thinking is usually not to create new actions for every one-off rule. Instead, look to see if you can push the logic into the models. If your controllers start to have too many non-standard actions, then it may be time to re-evaluate your domain modeling, and perhaps it's refactor the actions to some new models.
ps: you should never pass in passwords via a GET like that
I don't know how much of this is convention, but this is what I would do. I
would add another action, as long as it's specifically related to that
resource. In your example, show is a find by userid, so it makes sense as
another action on UsersController. You can turn it into a sentence that makes
sense, "get me the user with this email address"
For the other one, GET /users?username=joe&password=mysterio, I would do
that as another resource. I assume you're thinking that action would log in
the user if the password were correct. The verb GET doesn't make sense in that
context.
You probably want a 'session' resource (BTW, this is how restful_auth works).
So you would say "create me a session for this user", or something like POST
/sessions where the body of the post is the username & password for the user.
This also has the good side effect of not saving the password in the history
or letting someone capture it on the HTTP proxy.
So your controller code would look something like this:
class UsersController < ActionController::Base
def show
#user = User.find_by_id(params[:id])
# etc ...
end
def show_by_email
#user = User.find_by_email(params[:email)
end
end
class SessionsController < ActionController::Base
def create
# ... validate user credentials, set a cookie or somehow track that the
# user is logged in to be able to authenticate in other controllers
end
end
You would set up your routes like this:
map.connect "/users/byemail", :controller => "users", :action => "show_by_email", :conditions => { :method => :get }
map.resources :users
map.resources :sessions
That will get you URLs like /users/byemail?email=foo#example.com. There are
issues with encoding the email directly in the URL path, rails sees the '.com'
at the end and by default translates that into the :format. There's probably a
way around it, but this is what I had working.
Also like cletus says, there are ways to make your route match based on the format of the parts of the URL, like all numbers or alphanumeric, but I don't know off hand how to make that work with the dots in the url.
The first thing you can do is make your GETs as smart as possible. In your example, this can be handled programmatically. The argument can be processed this way:
Is a number? It's a userid;
Has a # in it? It's an email;
Otherwise? It's a username.
But I assume that you're not just talking about this example and want something to handle the general case rather than just this specific one.
There are basically two ways of dealing with this:
Add extra path information eg /users/email/me#here.com, /users/name/cletus; or
Be more specific in your "top-level" URL eg /user-by-email/me#here.com, /user-by-name/cletus.
I would handle it programmatically if you can.
Regarding the "ByEmail" request, have you considered creating a new email resource.
GET /email/foo_at_bar_dot_com
The response could contain a link to the related user.
I see so many people trying to apply RESTful design principles to their URL structure and then mapping those urls to procedural handler code. e.g. GET = Show, or is it GET = Index or ShowByEmail. By doing this you are really just pretending to do a RESTful design and then trying to create a mapping between a resource oriented URL space and procedurally oriented implementation. That is really hard to do and the procedural nature keeps leaking out into the URLs.
Resource oriented design often requires a very different way of thinking about problems that we are used to and unfortunately many of the frameworks out there keep sucking us back into the RPC model.
You might be able to set up different routes for different tasks. So for this case you could have one route to a method in UserControll dedecated to getting a user by email, and another for getting the information by credentials.