How about using URI path variables for an HTTP POST? - api

I've searched a lot but I couldn't find the proper answer to my question regarding my conditions.
I'm building a REST API, and the case, which seems a border line case to me, is the following:
-I'm dealing with two entities, Users and Roles. An User can have multiple roles assigned.
-To assign a Role to a User, the Role must be already in the DataBase.
-To assign a Role to a User, the only thing needed is the 'code' of the role, that is a short string.
-The uri path template used now is:
--Users: localhost:8080/api/users
--Given User: localhost:8080/api/users/{userId}
--Roles of a given User: localhost:8080/api/users/{userId}/roles
Now, to 'link' a given User with a given Role, two options come to my mind.
-The first is the one that sounds as best practice in any scenario, sending the post data in the body, perhaps as a JSON.
-The other one, sending it through the uri and with an empty body. For example, to link User with id U001 with role R001, one would have to post to the following uri sending no data in the body: localhost:8080/api/users/U001/roles/R001
The thing is that I don't mind using the first option, and it seems to be the best and most correct one, but in this particular case, I'm not sure wether it is better to send an almost empty body (because it only holds the role id, a very short string) posting it to 'localhost:8080/api/users/U001/roles' or skipping the body and just sending the role id through the uri as a path parameter like localhost:8080/api/users/U001/roles/R001
Thank you all in advance for your help.

There is nothing wrong with putting role in the URI. Your intuition was on the right track. I'd do it this way.
PUT: locahost:8080/api/users/{userid}/role/{roleId}
And here's why.
FIRST: The PUT verb is Idempotent. In other words (taken straight from the spec)
... the side-effects of N > 0 identical requests is the same as for a single request.
Which is what I'd assume you want in this regard. You don't want multiple records in your state storage for each instance of user & role. A user should feel at ease making the same PUT request without adversely effecting (adding duplicate records) the system.
When doing the same thing with a POST I'd expect to have a new record created for every request.
SECOND: The PUT verb is supposed to identify a specific resource. (taken straight from the spec)
... PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource. If the server desires that the request be applied to a different URI,
it MUST send a 301 (Moved Permanently) response; the user agent MAY then make its own decision regarding whether or not to redirect the request.
What if role R102 becomes obsolete and R104 is preferred? Return a 301 (Moved Permanently) with a HEADER (Location : localhost:8080/api/users/{userid}/role/R104).
FINALLY: When everything works well. Return a 201 (Created) when created and a 200 (No Content) on every subsequent request to the same URI. If they provide a Role that is not in the system return a 501 (Not Implemented).

Hmm - in this case - a POST with a 302 may be a bit messy.
Why not a very simple 'PUT'/'DELETE' with indeed the URIs suggested ?
With simple; 20X meaning succeeded, possibly some 30X to indicate it was already there - and anything else a fail ?

Related

Sails.js: How to access and modify req.params in middleware

I want to implement a mechanism to obfuscate the id fields in my application . Right now all the id fields are integers. I want to use some sort of reversible hashing to create random strings corresponding to the id fields. Also, I am trying to accomplish this with minimal changes to the overall project.
One thing that came to my mind was to write a middleware to intercept every request and response object and check for the presence of id field. If the request contains id field and it is an obfuscated version, decode the string and replace the request parameter with the integer id.
If the response contains the integer id, run the encode function on it to send the obfuscated id to the client.
The problem I am facing is with modifying the req object. The id field can be present in req.body or req.params or res.query. However, in the middleware, I cannot see the id field when it is present in req.params.
I tried using policies. But the problem I am facing there is even after changing the req.params, the changes are lost when the control reaches the controller. What would be the recommended way of solving this problem ?
Here is the sample code:
In the policy:
module.exports = function (req, res, next) {
req.params.id = '12345';
req.query.pageSize = 30;
req.body = {};
sails.log.info('req.params', req.params);
sails.log.info('req.query', req.query);
sails.log.info('req.body', req.body);
return next();
};
I am just modifying values of req.params, req.query and req.body.
When I try to access these values in the controller, the values of req.query and req.body are the modified values as changed in the policy. However, req.params changes back to what was sent by the client and the changes made in the policy are lost
I think you are confusing policy and middleware? Is your code above in api/policies? If so, you still need to define which controller(s) this policy is applied to in config/policies.
So config/policies.js should look like:
modue.exports.policies = {
// If you really want this policy for every controller and action
'*': 'policyName.js',
// If you want it for a specific controller. '*' denotes every action in controller
specificController: {
'*': 'policyName.js'
},
// If you want it for a specific action on a specific controller
specificController: {
'specificAction': 'policyName.js'
}
};
Also I'd like to add. Policies are generally meant for authorization of controllers but this seems like a decent use case. Since every single request is not going to have these fields this should be a policy. Policies are great when applying something to a handful of controllers/actions. Middleware is great when you need to apply to every single action that comes into your app.
http://sailsjs.org/documentation/concepts/policies
http://sailsjs.org/documentation/concepts/middleware
Gitter response:
sgress454 #sgress454 10:45
#mandeepm91
In the policy, if I change req.body or req.query, the changes persist in the next policy or controller. However, changes made to req.params are lost.
This is one of the main use cases for req.options. This object is intended to be used to store request data that can be mutated by one handler before being passed on to the next. The req.params object is meant to provide each handler with a copy of the original request parameters.
How you approach this really depends on what your goal is. If you really need each request handler (that is, policies and controller actions) to see an encoded version of the ID, then a good approach would be to encode the ID in a policy as #S-Stephen suggested, store that value in req.options.id and refer to that in your other request handlers. If, on the other hand, you're really only concerned with the response having the encoded ID, the suggested practice is to use the res.ok() response in your handlers (rather than res.send() or res.json), and adjust the code for that response in api/responses/ok.js to encode the ID before sending. You can also use a custom response if this is only required for certain requests. See custom responses for more info.
Hi #sadlerw, you should be able to modify the code for res.ok() in your api/responses/ok.js file to have it always return JSON if that's what you want for every response. If it's something you only want for certain actions, you could create a custom response instead and use that where appropriate.

Rails cache_digests and conditionals

When we cache a partial in rails using cache digests, how does the conditional logic in the partial get handled? Does it cache the full template and later apply the conditionals so that the right json/html can be served to the right user?
Does it cache the full template and later apply the conditionals so
that the right json/html can be served to the right user?
This part of question seems a bit unclear to me, so I'll provide different options based on what "conditionals" could be.
First of all, cache digests do not care about inner conditions based on #variables's state (unless a particular state is mentioned inside of its cache key). Consider the following example:
# users.haml
.welcome_block
- if #user.admin?
%h4 Hello, admin!
- else
%h4 Hello, user!
In case you apply caching to the whole page with cache ['users_haml'], the cache would be generated just once (for the first user with whichever role). Any user who accessed this page later would see the same h4 greeting as the one which has been shown to the first user. The reason here is that digest for string users_haml, proved to cache method, is always the same regardless of any circumstances.
On the other hand, cache #user would provide slightly different behaviour. Each user who opens users.haml page would see proper greeting based on his/her role. The reason for this behaviour is that digest differs for all objects of type User, so cache_digests generates N cached pages for N users.
The last one kind of conditionals which comes to mind is the one based on conditional partials rendering, e.g.:
# users.haml
- cache [#user.month_of_birth]
- if #user.month_of_birth == 'October'
= render 'partial_one'
- else
= render 'partial_two'
So, this one renders correct cached page for users with different months of birth. But what should happen if I change the contents of partial_one? How does cache_digests understand that cache should be invalidated for those who were born in october (based on the conditional statement)?
The answer here is that it doesn't know that at all. The only thing it knows is that users.haml depends on both partial_one and partial_two, so changes to either of these inner partials gonna invalidate ALL the users.haml page caches regardless of users' month of birth.

Correct response on a request for multiple objects where one does not exist

What is the correct response on a GET request for multiple objects where one or more of them does not exist? e.g.:
http://domain.net/event-list/?ids=1&ids=5&ids=3
where object with id 5 does not exist. Should I return a list with just objects 1 and 3 or should I return some kind of error? What is the correct response?
Also I wonder If the behaviour should be any different if the request is POST. For instance:
$.post('domain.net/events/bulk-edit/?ids=1&ids=5&ids=3', { public: true });
Should I just perform operation for the objects that exist or do not perform operation at all and return an error?
I know there are some debates if non-empty querystrings are ok for POST requests. I think they are alright just for this particular case where you request a subset of objects to do something with them.
Okay, I gave it some thoughts and here is what I believe were the right thing to do.
This is a bit of a headache since you're requesting multiple objects at once which is usually a WebDAV-thing, bringing wonders such as the 207/Multistatus response with it. Let me start of with saying that I think your query string has the wrong format. I think it really should look like this:
?ids[]=1&ids[]=5&ids[]=3
Now about responses on a GET request. I believe the following response codes were the right ones:
200 if any object could be found by id
400 on a missing or empty ids query parameter (unless you think no ids should translate into get all objects)
404 if none of the given ids match any object
If you want to notify the client that the request couldn't be satisfied in parts, you are free to send a Warning header along (cf RFC 2616, sec 14.46). However, if you really want to do it absolutely rightâ„¢, here's how to deal with requests where not every id is valid:
If all ids could be used to load an object, send the 200/Ok response code
If there are any ids that could not be used to load an object, redirect via 301/Moved Permanently to a new URL sans the offending ids param(s)
As for the POST request: It is my understanding that you'd like to set multiple events as public in one go? If so, I'd really change the order: Send a POST to http://domain.net/events/publish and send the ids in the post body.

MVC user's full name in Url, how to handle duplicates

I want to setup the following url in my MVC4 website, using the user's full name in the url:
http://www.myapp.com/profile/steve-jones
I have setup the following route in Global.asax:
routeCollection.MapRoute(
"profile", "profile/{userName}",
new { controller = "myController", action = "profile", userName = string.Empty
});
And I can take the parameter 'steve-jones' and match it to a user with matching name. My only problem though is, what if there is more than one 'Steve Jones', how can I handle this?
Does anyone know of a workaround/solution to this so that I can use a user's full name as part of the url and still be able to retrieve the correct user in the controller method?
Am I forced into including the user's id with the url (something that I do not want to appear)?
The usual way of handling this is by appending a number when creating the profiles. So if "steve-jones" is already a name in the database, then make the user's display name "steve-jones2". You basically have to insist that all profile urls are unique, which includes updating any existing database and account creation code.
Alternatively (and/or additionally), if two same names are found then have the script reroute to a disambiguation page where the user is presented with links and snippet of profile info of the many existing Steve Joneseses so they can go to the full correct profile.
Another way of handling it is by giving all user profiles an additional numeric code on the end. At my university all logins are based on name, so they give everyone pseudo-random 3-digit extensions so that they are safe as long as they don't get 1000 people with the exact same names :)
Some people might be happier being steve-jones-342 if there is no steve-jones or steve-jones1, if you're concerned.

REST API: How to search for other attribute

I use node.js as REST API.
There are following actions available:
/contacts, GET, finds all contacts
/contacts, POST, creats new contact
/contacts/:id, GET, shows or gets specifiy contact by it's id
/contacts/:id, PUT, updates a specific contact
/contacts/:id, DELETE, removes a specific contact
What would now be a logic Route for searching, quering after a user?
Should I put this to the 3. route or should I create an extra route?
I'm sure you will get a lot of different opinions on this question. Personally I would see "searching" as filtering on "all contacts" giving:
GET /contacts?filter=your_filter_statement
You probably already have filtering-parameters on GET /contacts to allow pagination that works well with the filter-statement.
EDIT:
Use this for parsing your querystring:
var url = require('url');
and in your handler ('request' being your nodejs http-request object):
var parsedUrl = url.parse(request.url, true);
var filterStatement = parsedUrl.query.filter;
Interesting question. This is a discussion that I have had several times.
I don't think there is a clear answer, or maybe there is and I just don't know it or don't agree with it. I would say that you should add a new route: /contacts/_search performing an action on the contacts list, in this case a search. Clear and defined what you do then.
GET /contacts finds all contacts. You want a subset of all contacts. What delimiter in a URI represents subsets? It's not "?"; that's non-hierarchical. The "/" character is used to delimit hierarchical path segments. So for a subset of contacts, try a URI like /contacts/like/dave/ or /contacts/by_name/susan/.
This concept of subsetting data by path segments is for more than collections--it applies more broadly. Your whole site is a set, and each top-level path segment defines a subset of it: http://yoursite.example/contacts is a subset of http://yoursite.example/. It also applies more narrowly: /contacts/:id is a subset of /contacts, and /contacts/:id/firstname is a subset of /contacts/:id.