Does it violate the ideas of REST, or accepted conventions, to have different models for GET/PUT/POST at the same URL?
An example:
Consider a simple resource found at api/things
I can create a thing by:
POST api/things
with body = { Name: "New Thing" }
This returns me a thing along with location
{ Id: 500, Name: "New Thing", Links: ["api/things/500"] }
Location Header: api/things/500
I can get the thing with:
GET api/things/500
and I will get
{ Id: 500, Name: "New Thing", Links: ["api/things/500"] }
If I want to update it:
PUT api/things/500
{ Name: "Updated Thing", IsActive: false }
There are "rules" in this example that are hidden behind the different models.
When creating you can't specify the Id or IsActive setting. The Id is generated by the server always starts as Active.
You cannot update an Id, and thus the "link" which uses it, so the PUT model does not contain an Id field.
One strong criticism of this: I cannot do a POST to create a new one, change the Name field, and PUT it back to Update it. I would have to know to remove the Id and links fields. I could "be liberal in what I accept" and allow the Ids and Links to be on the PUT request, but then I need to make additional decisions like, "is it a 400 if the Id/Link they send is different?, and "is it a 400 if they don't send an Id/Link?". If the API claims to accept those fields on PUT, that could be seen as a contract that they are able to be updated.
It is perfectly valid to accept different DTO's for different methods. Quite often, a POST will create a new entity with default properties such as Id, StartDate or Active, etc. so these properties are not present on a "POST DTO". I tend to shy away from PUT's since the definition is you are replacing one entity with another, which could include an Id. I opt for PATCH in most cases where you are accepting deltas and partial objects. You can verify each property that was sent up and determine if it's a readonly property or not. In some cases, based on roles, it may be readonly for one user, and patchable by another. By following this, POST is one DTO, PATCH is partial, PUT is non-existent, and GET returns the full DTO.
I've only seen a couple places where PUT is useful. You want to change the binary of a file or you have a collection that you want to change, PUT is great. Otherwise, I love PATCH.
Related
In Postman, I can create a set of common tests that run after every endpoint in the collection/folder Tests tab, like so:
pm.test("status code is 200", function () {
pm.response.to.have.status(200);
});
But how should I do this for my schema validation on the response object? Each endpoint has a different expected schema. So I have something like this on each individual endpoint:
const schema = { type: 'array', items: ... }
pm.test('response has correct schema', function () {
const {data} = pm.response.json();
pm.expect(tv4.validate(data, schema)).to.be.true;
});
I can't extract this up to the collection level because each schema is different.
Now I find that I want to tweak that test a little bit, but I'll have to copy-and-paste it into 50 endpoints.
What's the recommended pattern here for sharing a test across endpoints?
I had the same issue some years ago, and I found two ways to solve this:
Create a folder for each structure object
You can group all your test cases that share the same structure into a new folder and create a test case for this group. The issue with this is that you will be repeating the requests in other folders. (For this solution, you will need to put your "tests cases" into the folder level)
Create a validation using regular expressions (recommended)
Specify in the name of each request a set of rules (these rules will indicate what kind of structure or call they might have). Then you create a validation in the first parent folder for each type of variation (using regex). (You will need to create documentation and some if statements in your parent folder)
E.g.: [POST] CRLTHM Create a group of homes
Where each initial is meaning to:
CR: Response must be 201
LT: The response must be a list of items
HM: The type of the response must be a home object
And the regex conditional must be something like this (this is an example, please try to make your regex accurate):
if(/CRLTHM\s/.test(pm.info.requestName))
(In this image, NA is referring just to Not Authenticated)
This a contrived example but I hope it demonstrates my question. No, I'm not storing passwords in plaintext, etc; this is just an example.
User.create(name: "John", password: "sensitive")
User.all => [{User: name: "John", password: "sensitive"}]
When I retrieve data from the DB, I do not want the password attribute returned by default, such that:
User.all => [{User: name: "John"}]
I assumed ActiveRecord would have a method in the model I could override to establish the default attributes to select, but I can't find such a method.
By default, Active Record loads all the attributes. There is no concept of "sensitive" attribute. In fact, as long as you don't dump the instance anywhere (e.g. using inspect), or you don't print out the attribute, there is really not a lot security issues associated with fetching that data (of course, assuming you have a secure connection).
Perhaps you should explain what is the issue that is bringing you to believe that not loading that attribute would be the best solution to the problem.
It's also important to remember that several Rails defaults will loop over all attributes, whenever you try to use features such as to_json. My suggestion is to avoid relying on those features, but always explicitly whitelist the attributes you want to use or export. That would prevent leakage of sensible details.
Last but not least, you can try to workaround the Rails defaults by using a method that enumerates all the attributes, without the ones you consider "sensible"
Model.select(Model.column_names - %w( password other )).all
select applies to any query method:
Model.select(Model.column_names - %w( password other )).find(54)
Model.select(Model.column_names - %w( password other )).first
I work with a system that requires that each change be audited and a reason specified for each change. In trying to keep with good REST design, we want to use the HTTP verbs correctly.
With regards to this particular case, I am unsure about best way to handle this situation. Lets say we have a simple entity:
URL: /users/100
JSON: { username: 'usr1', firstName: 'John', lastName: 'Smith' }
Now if I want to update the username from 'usr1' to 'user1', our system requires that I specify the reason for the change.
Without the change reason requirement, we could easily just PUT the JSON to the URL.
My question is what is the best way to send the change reason to the server. Here are the options I have come up with so far:
Add a changeReason attribute to the entity.
Add changeReason as a query parameter.
Add a changeReason header.
None of these options seem right to me. Has anyone dealt with this before?
First, some thoughts to your proposed solutions:
Add changeReason to the JSON object: Is bad, because (if using the same mime-type) the GET would not need that field.
As query parameter: Is wrong, because it is not a parameter for the resource (but for the request)
As header: Is wrong, because this is an out-of-band information.
So, certainly this information should be part of the request representation, and the update happens on the user, so the URL should not change (it is the same resource). I propose the PATCH method, which was created seemingly for the exact purpose you describe.
With the PATCH method, you have to describe a "change" representation, that specifically describes a change you want applied to a resource. This document format will be different from any resource formats, and could then contain a "changeReason". Looks something like this:
PATCH /users/100
Content-Type: application/vnd.company.auditedchange+json
{
"replaceFields": {
"username": "user1"
}
"changeReason": "Typo in username."
}
Introduce a new resource userchange. POST to it with the information about the change.
POST /userchange
Content-Type: application/json
{
"id": 100,
"changes": {
"username": "user1"
},
"reason": "fixed name"
}
The server would execute the change on the user and log the reason.
When new model is created and saved with RESTAdapter its Id property is undefined, because my backend is responsible for id generation. So when I return to grid there is 2 same entities: first - with empty Id (from RESTAdapter cache, I think) and second - with correct Id returned from backend.
Any ideas? Maybe it is posiible to disable RESTAdapter cache?
UPDATE
My code for entity creation.
submit:function () {
var manager = App.store.createRecord(App.Manager, {
firstName:this.get('firstName'),
lastName:this.get('lastName'),
speciality:this.get('speciality')
});
App.store.commit();
this.get('controller').transitionTo('managers');
return false;
}
NEW UPDATE
Thanks to Mike Grassotti hints. Here some details for my issue.
One antity have Id, another have no Id.
If I remove App.store.commit() code, there is no POST to server and only entity without Id will be displayed.
This entity has isLoaded=false and isError=true.
When new model is created and saved with RESTAdapter its Id property is undefined, because my backend is responsible for id generation.
Right, but there is nothing unusual about this - ember expects id generation to be done by the backend.
So when I return to grid there is 2 same entities: first - with empty Id (from RESTAdapter cache, I think) and second - with correct Id returned from backend.
OK. What do you mean by same 2 entities - surely they are different js objects. Try logging each of them to console like this:
console.log(entityOne.toString());
console.log(entityTwo.toString());
Any ideas?
There are many things that could cause this to happen. Sounds like somehow you are creating two new records and saving just one of them. Or could be the API response does not match what ember expects, causing an extra record to get created.
Try to enable logging on your records, then watch console so you can see what's going on as your model is saved. Hopefully this will give you some insight into when/how the extra record is being created.
record.set("stateManager.enableLogging", true)
Inspect browser communication with your api and compare JSON to see if it matches what the ember rest adapter expects.
Post that JSON and the rest of your source code (model definition, etc.) to Stack Overflow, maybe a second set of eyes will help.
Check this post for some other debugging tips: http://www.akshay.cc/blog/2013-02-22-debugging-ember-js-and-ember-data.html
Maybe it is posiible to disable RESTAdapter cache?
RESTAdapter does not maintain a separate cache of model objects. And since you are not trying to do anything special, there should be no need to take a step like that.
Many thanks to Mike Grassotti, I have found an answer to my question.
The good question was
With App.store.commit() back in, what does the JSON response from
server look like?
+1 for that comment.
I can't find any info in ember-data documentation for that, so some links still would be helpful for me. I change the result returned from backend and everything works fine now.
{
"manager": {
"firstName": "test",
"lastName": "test",
"speciality": "test",
"id": "acd325ac-03eb-419e-be8a-d4ac42e8c235"
}
}
I'm doing research into a web API for my company, and it's starting to look like we might implement a RESTful one. I've read a couple of books about this now (O'Reilly's "RESTful web services" seeming the most useful) and have come up with the following set of URIs and operations for an object that can be commented on, tagged, and rated.
It doesn't really matter what the object is, as this scenario applies to many things on the net, but for the sake of argument lets say it's a movie.
Some of these seem to fit quite naturally, but others seem a bit forced (rating and tagging particularly) so does anybody have any suggestions about how these could be improved? I'll list them with the URI and then the supported verbs, and what I propose they would do.
/movies
GET = List movies
/movies/5
GET = Get movie 5
/movies/5/comments
GET = List comments on movie 5
POST = Create a new comment on movie 5
/movies/5/comments/8
GET = Get comment 8 on movie 5
POST = Reply to comment 8 on movie 5
PUT = Update comment 8 on movie 5
/movies/5/comments/8/flag
GET = Check whether the movies is flagged as inappropriate (404 if not)
PUT = Flag movie as inappropriate
/movies/5/rating
GET = Get the rating of the movie
POST = Add the user rating of the movie to the overall rating
Edit: My intention is that the movie object would contain its rating as a property, so I wouldn't really expect the GET method to be used here. The URI really exists so that the rating can be an individual resource that can be updated using the POST verb. I'm not sure if this is the best way of doing it, but I can't think of a better one
/movies/5/tags/tagname
GET = Check whether the movies is tagged with tagname (404 if not; but if it is tagged with the tag name should it return the actual tag resource by redirecting to something like /tags/tagname?)
PUT = Add tag tagname to the movie, creating the tag resource /tags/tagname if required
DELETE = Remove tag tagname from the movie, deleting the tag resource tags/tagname if nothing is tagged with it after this removal
Note that these wouldn't be the entire URIs, for example the URI to list the movies would support filtering, paging and sorting. For this I was planning on something like:
/movies/action;90s/rating,desc/20-40
Where:
action;90s is a semi-colon delimited set of filter criteria
rating,desc is the sort order and direction
20-40 is the range of item indices to get
Any comments about this API scheme too?
Edit #1
This post is getting quite long now! After reading some of the answers and comments, this is the changes from above I'm planning on making:
Tags will be handled as a group rather than individually, so they will be at:
/movies/5/tags
GET = List tags
POST = Union of specified tags and existing tags
PUT = Replace any current tags with specified tags
DELETE = Delete all tags
I'm still really not sure how to handle flagging a comment though. One option is that instead of POSTing to a comment replying to it, a comment object will include its parent so it can be POSTed to the general URI, i.e.
/movie/5/comment
POST = Create a new comment (which may be a reply to a comment)
I could then use the POST to a comment to flag it. But this still doesn't feel quite right.
/movie/5/comment/8
POST = Flag comment
Most of what you have looks good. There were just a couple of strange things I saw. When I put my URLs together, I try to follow these four principles.
Peel the onion
If you make the R in REST really be a resource then the resource URL should be able to be peeled back and still be meaningful. If it doesn't make sense you should rethink how to organize the resource. So in the case below, each makes sense. I am either looking at a specific item, or a collection of items.
/movies/horror/10/
/movies/horror/
/movies/
The following seems funny to me because flag isn't a resource, it's a property of the movie.
/movies/5/comments/8/flag -> Funny
/movies/5/comments/8/ -> Gives me all properties of comment including flag
Define the View
The last peice of the URL describes how to show the resource. The URL /movies/horror/ tells me I will have a collection of movies refined by horror. But there might be different ways I want to display that collection.
/movies/horror/simple
/movies/horror/expanded
The simple view might just be the title and an image. The expanded view would give a lot more information like description, synopsis, and ratings.
Helpers
After the resource has been limited and the proper view figured out, query string parameters are used to help the UI with the little stuff. The most common query string parameters I use are
p => Page
n => number of items to display
sortby => field to sort by
asc => sort ascending
So I could end up with a URL like
/movies/horror/default?p=12&n=50&sortby=name
This will give me the list of movies limited to horror movies with the default view; starting on page 12 with 50 movies per page where the movies are sorted by name.
Actions
The last thing needed are your action on the resource. The action are either collection based or item based.
/movies/horror/
GET -> Get resources as a list
POST -> Create, Update
/movies/horror/10/
GET -> Get resource as item
POST -> Update
I hope this helps.
I disagree with the edit. Queries should be defined by querystrings as per Martijn Laarman's post. i.e.:
/movies?genre=action&timeframe=90s&lbound=20&ubound=40&order=desc
Well, the way I see it some of the information you return now as objects could simply be added to the metadata of its parent object.
For instance, rating could be part of the response of /movies/5
<movie>
<title>..</title>
..
<rating url="movies/ratings/4">4</rating>
<tags>
<tag url="movies/tags/creative">creative</tag>
...
Removing a tag simply means posting the above response without that tag.
Also queries should go in URL variables, I believe:
/movies/?startsWith=Forrest%20G&orderBy=DateAdded
Based on my understanding of ROA (I'm only on chapter five of RESTful Web Services) it looks good to me.
This is an awesome initial draft for a spec of a REST API. The next step would to specify expected return codes (like you did with "404 No Tag Available"), acceptable Content-Types, and available content-types (e.g., HTML, JSON). Doing that should expose any additional chinks you'll need to hammer out.
#Nelson LaQuet:
Using the HTTP methods as they are actually defined gives you the safety of knowing that executing a GET on anything on a web site or service won't eat your data or otherwise mangle it. As an example (pointed out in RESTful Web Services) Google's Web Accelerator expects this behaviour -- as stated in the FAQ -- and presumably other services do too.
Also it gets you idempotency for free. That is doing a GET, DELETE, HEAD or PUT on a resource more than once is the same as doing it only once. Thus if your request fails then all you have to do is run it again.
This is not REST.
A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace. Instead, allow servers to instruct clients on how to construct appropriate URIs, such as is done in HTML forms and URI templates, by defining those instructions within media types and link relations. [Failure here implies that clients are assuming a resource structure due to out-of band information, such as a domain-specific standard, which is the data-oriented equivalent to RPC's functional coupling].
http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven