I am currently writing an REST API using the Jersey Framework. I am following the HATEOAS principle and the user should only be moving through the api by the given links in my response body oder headers. On some Resources I have implemented pagination functionality. I was wondering though, what should I tell the User (HTTP Status Code), when he is not following my boundaries and just like randomly makes a request where the requested page is actually "out of bounds". Currently I just return a null Collection, but I think as a User, I wouldn't be able to make something out of such a response. I considered using the Status Code "Not Found", but I am not sure if that is the appropriate one. I really want to stay true to REST and that implicates I stay true to HTTP. So can anyone give me suggestions or even tell me if there is actually a rule for my problem?
Maybe a concrete example:
page size = 10;
Collection.size = 27;
requested page = 4;
Paging starts with page 0
, so requesting http://...../resource?page=0, returns the first page.
My question is, what should I return for the request http://...../resource?page=4? Currently I am just returning null, but I don't think that's the proper response.
Thanks in advance
EDIT:
I am only asking about the expected Response in case the requested page is "empty". I know that fixed page size design may be doomed for future change requests, but since this API is part of a Microservice, there will be none, except we have a fight inside our team :)
404 not found is the appropriate return code for an out of bound access.
But you should consider change your resource identifier (URI). Returning 404 or 200 depending on a URL parameter is not a good design.
It would be better to treat every page as single resource. That's also true for HATEOAS.
.../resource/page/0 #200 + return link in header to next resource .../resource/page/1
.../resource/page/4 #404 + return link to first resource .../resource/page/0)
Using URL parameters should be the last option if nothing else works (for example range access).
Related
I have recently read the guide on implementing RESTful API's in Spring Boot from the official Spring.io tutorials website (link to tutorial: https://spring.io/guides/tutorials/rest/)
However, something in the guide seemed to contradict my understanding of how REST API's should be built. I am now wondering if my understanding is wrong or if the guide is not of as high a quality as I expected it to be.
My problem is with this implementation of a PUT method to update the status of an order:
#PutMapping("/orders/{id}/complete")
ResponseEntity<?> complete(#PathVariable Long id) {
Order order = orderRepository.findById(id) //
.orElseThrow(() -> new OrderNotFoundException(id));
if (order.getStatus() == Status.IN_PROGRESS) {
order.setStatus(Status.COMPLETED);
return ResponseEntity.ok(assembler.toModel(orderRepository.save(order)));
}
return ResponseEntity //
.status(HttpStatus.METHOD_NOT_ALLOWED) //
.header(HttpHeaders.CONTENT_TYPE, MediaTypes.HTTP_PROBLEM_DETAILS_JSON_VALUE) //
.body(Problem.create() //
.withTitle("Method not allowed") //
.withDetail("You can't complete an order that is in the " + order.getStatus() + " status"));
}
From what I read at https://restfulapi.net/rest-put-vs-post/ a PUT method should be idempotent; meaning that you should be able to call it multiple times in a row without it causing problems. However, in this implementation only the first PUT request would have an effect and all further PUT requests to the same resource would result in an error message.
Is this okay according to RESTful API's? If not, what would be a better method to use? I don't think POST would be any better.
Also, in the same guide, they use the DELETE method in a similar way to change the status of an order to cancelled:
#DeleteMapping("/orders/{id}/cancel")
ResponseEntity<?> cancel(#PathVariable Long id) {
Order order = orderRepository.findById(id) //
.orElseThrow(() -> new OrderNotFoundException(id));
if (order.getStatus() == Status.IN_PROGRESS) {
order.setStatus(Status.CANCELLED);
return ResponseEntity.ok(assembler.toModel(orderRepository.save(order)));
}
return ResponseEntity //
.status(HttpStatus.METHOD_NOT_ALLOWED) //
.header(HttpHeaders.CONTENT_TYPE, MediaTypes.HTTP_PROBLEM_DETAILS_JSON_VALUE) //
.body(Problem.create() //
.withTitle("Method not allowed") //
.withDetail("You can't cancel an order that is in the " + order.getStatus() + " status"));
}
This looks very wrong to me. We are not deleting anything here, it is basically the same as the previous PUT method just with a different state we want to move to. Am I correct to assume that this part of the tutorial is bogus?
TL;DR: what HTTP method is right to use when you want to advance the status of a resource to the next stage without giving an option of going back to an earlier stage? Basically an update/patch that will invalidate its own pre-conditions.
something in the guide seemed to contradict my understanding of how REST API's should be built. I am now wondering if my understanding is wrong or if the guide is not of as high a quality as I expected it to be.
I wouldn't consider this guide to be a reliable authority - the described resource model has some very questionable choices.
From what I read at https://restfulapi.net/rest-put-vs-post/ a PUT method should be idempotent; meaning that you should be able to call it multiple times in a row without it causing problems. However, in this implementation only the first PUT request would have an effect and all further PUT requests to the same resource would result in an error message.
The authoritative definition of idempotent semantics in HTTP is currently RFC 7231.
A request method is considered "idempotent" if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request.
Note: "effect", not "response".
PUT /orders/12345/complete
means "please replace the current representation of /orders/12345/complete with the representation in the payload". In other words "save this file on top of your current copy". Saving the same file two or three times in row produces the same effect as saving the file once, so that's "idempotent".
HTTP does not define exactly how a PUT method affects the state of an origin server beyond what can be expressed by the intent of the user agent request and the semantics of the origin server response. It does not define what a resource might be, in any sense of that word, beyond the interface provided via HTTP. It does not define how resource state is "stored", nor how such storage might change as a result of a change in resource state, nor how the origin server translates resource state into representations. Generally speaking, all implementation details behind the resource interface are intentionally hidden by the server. -- RFC 7231
So in their CURL example
PUT /orders/4/complete HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
The meaning of this message is "replace the current representation of /orders/4/complete with an empty representation". But the origin server gets to choose how to do that, and which standardized responses to return to the client.
So this is fine.
All work is transacted by politely placing documents in in-trays, and then some side effect of placing that document in an in-tray causes some business activity to occur -- Jim Webber, 2011.
In this case, the document we are putting into the "in-tray" happens to be blank.
#DeleteMapping("/orders/{id}/cancel")
I would never approve that choice in a code review. DELETE (like PUT) has semantics in the "transfer of documents over a network domain".
The DELETE method requests that the origin server remove the association between the target resource and its current functionality. In effect, this method is similar to the rm command in UNIX: it expresses a deletion operation on the URI mapping of the origin server rather than an expectation that the previously associated information be deleted.
Trying to hijack the method because the spelling is kind of like the domain action is the wrong heuristic to use in choosing methods.
Relatively few resources allow the DELETE method -- its primary use is for remote authoring environments, where the user has some direction regarding its effect.
The point being that we have a general purpose document manipulation interface, and we are using that interface as a facade that allows us to drive business activity. So we should be using our standardized message semantics the same way every other page on the web does.
#PutMapping would be defensible, using the same justification as we did for /complete.
what HTTP method is right to use when you want to advance the status of a resource to the next stage without giving an option of going back to an earlier stage? Basically an update/patch that will invalidate its own pre-conditions.
PUT, PATCH, and POST are all appropriate methods to use when editing the representation of a resource. Use PUT or PATCH when you are sending a replacement representation for the resource, use POST when you are asking the server to calculate what the edit to the representation should be.
In our API, one of the endpoint will expect clients to provide body/payload only in certain scenario.
If the API is unable to generate a payload for given request based on the origin of the client then, we want our API to provide response with the right status code to the client, so that they know they have to provide additional information. Once the client fulfills the request with body/payload then the api will process the request as normal.
I just wanted to know is there any standard, predefined status code or procedure to implement this kind of endpoint in API design or do we have to just reject the request with some custom status code and then ask the client to implement a logic based on custom code?.
Thanks,
Vinoth
HTTP Status codes don't, nor are they intended to, map precisely against every real world error. They represent categories of error.
For example, a 404 means that the resource couldn't be found, but if your path is /customers/11/animals/5 then there are several things which could be wrong with the path. customer 11 may not have an animal 5 for example, or there may be no customer 11. There is no http response for "animal not found". Or your API may not have any calls with that pattern of URL to begin with.
You should return a status code which represents what "category" of error you have (in this case, something was not found), and the response body should contain more specific details about the error. To make things simpler, I find it helpful if the data structure is the same for a success and error (it makes parsing much easier) with a "data" field which varies per response.
Here is one example:
status code: 404 not found
body: {
"messageDetailCode" :"CustomerNotFound",
"messageDetail" : "Customer not found",
"data" : null
}
Further reading:
What's an appropriate HTTP status code to return by a REST API service for a validation failure?
I am calling a REST API for a list of resources and getting the json response as below if atleast one resource is there.
{
"value": [
"res1_id",
"res2_id",
"res3_id"
]
}
and the HTTP response code is 200.
But when no resource is there the server is returning HTTP response code as 404.
My doubt it why it is designed that way(it is an enterprise product).
I was expecting a empty list as below and HTTP response code 200:
{
"value": []
}
I am not able to understand what design decision has been taken into consideration to return 404 instead of an empty json.
Please help me to understand the reasoning behind it.
What I am reading here is that you have a 'collection' resource. This collection resource contains a list of sub-resources.
There's two possible interpretations for this case.
The collection resource is 0-length.
The collection resource doesn't exist.
Most API's will treat a 0-length collection as a resource that still exists, with a 200 OK. An empty coffee cup is still a coffee cup. The coffee cup itself did not disappear after the last drop is gone.
There are cases where it might be desirable for a collection resource to 404. For example, if you want to indicate that a collection never existed or never has been created.
One way to think about this is the difference between an empty directory or a directory not existing. Both cases can 'tell' the developer something else.
You're trying to access a resource which does not exist. That's the reason for 404.
As a simple google search says
The HTTP 404, 404 Not Found, and 404 error message is a Hypertext
Transfer Protocol (HTTP) standard response code, in computer network
communications, to indicate that the client was able to communicate
with a given server, but the server could not find what was requested.
For your case
{
"value": []
}
This can be a status code 200 which means the resource is accessed and data is returned which is an empty array or you can customize it to a 404. But for that you've mentioned best is to return 200.
A status code of 404 is the correct response for when there is no resource to return based on a request you made. You asked for data and there is no data. It returns an empty response. They're relying on you to base your interpretation of the results on the status code.
https://en.wikipedia.org/wiki/HTTP_404
Some companies prefer making their errors known instead of hiding them. I know where I work we shy away from try catch blocks because we want our applications to blow up during testing so we can fix the problem. Not hide it. So it's possible that the dev that created the API wants you to use the status code of the response as a way of telling if the service was successful.
I have to define an API that answers whether a resource with given ID can be created, like
Can I (caller) create this resource with id=resource1 ?
The possible responses could be
401 - The caller is not authenticated
403 - The caller is authenticated but not authorized to perform this check
200 - Yes, you can create a resource with id=resource1
...
Now my questions are
How can I model the API?
Will, GET /resources/resource1 be a good choice?
What HTTP codes will suite for responses like,
(a) this resource id is already taken, (b) you don't have permission to create this particular id (but only few other ids), (c) you can create this id.
An example in github may help you.
The api designed for checking if a user is following another user:
GET /user/following/:username
The deal information is presented in github's api document
For your question1, I think you can implement like this:
GET /resource/existence/:resource_id
For question2, you may also take a look at github's client errors
Would it be better to just try and create the resource with a POST? and let your implementation handle the response from there? In which case your responses could be:
a) 409: Conflict
b) 401: Unauthorized
c) 200: OK
If that's not possible, then I guess your payload response from a GET can contain the result. Something as simple as:
true: You can create the resource
false: You cannot create the resource
Since you want to check the permissions regarding the addition, you should use a different resource than the one that actually added the element. IMO something like /permissions/{elementName}?id=theid or /permissions/{elementName}/{operationName}?id=theid. Accessing it with method GET would suit.
Using the same resource would be a bit "messy" I think since I would expect the method GET on /resources/resource1 to actually return the content of the element with identifier ressource1.
Regarding the response, I would see this:
401 if the user isn't authentication and the permission resource requires an authentication.
204 if the current user is allowed to add an element with the specified identifier. I don't think that you need a response payload in this case.
Regarding the case when the user isn't allowed to add an element with the provided identifier, I think the status code 403 (Forbidden) suits. Perhaps a status code 400 could also match if you consider that the user provides a wrong content. In this case some hints about the error (identifier value not allowed) should be returned within the response payload.
For me, the status code 409 (Conflict) is more when implementing optimistic locking, i.e. concurrent accesses (updates) on the same element.
Hope it will help you,
Thierry
Let say we have only 30 products and a REST API with QueryString parameters used for pagination. In the exemple below pagesize is the number of resources per page.
api/products?page=1pagesize=10
What do you prefer to answer when the request is asking for a page over the max page like the following?
api/products?page=125&pagesize=10
400 - Bad Request?
404 - Not Found?
200 - Ok (with empty result)?
204 - No Content?
I would go with Not Found, since the request's syntax is perfectly fine. See the status code explanations
I don't think it is 400, because the request is valid, but there are REST APIs which use 400 by out of range errors.
404 is valid if we say that we don't have to bind every URL matching the pagination template to the collection resource. By range headers style pagination it won't work, because by there pages share the same URL. By query style pagination it is valid.
The 200 with empty resource is valid. The requested page does not contain items.
204 should be applied with empty message body. You probably want to send back links to the first and last pages of the collection, so this might not the solution you want.
So I think any of 200, 204 and 404 can be a valid solution.
416 Requested Range Not Satisfiable - optionally we could use this, which has the exact meaning we want, but it should be applied only with range headers. (note: should != must, English is a hard language :D)
I've been using 404 for a while, following #PhilLab's answer, and now, I find it a bit inconsistent.
My API has default pagination parameters, so
api/products
translates internally into
api/products?page=1&pagesize=10
When there are no products, what should this return?
api/products
I find it weird to return a 404 for this URL as if it was an incorrect URL.
But technically, the request is out of range, so I find it inconsistent to return a 200 in this case (totalpages=0, page=1) and not in the general out of range case (totalpages=1, page=2). It makes the "0 element" case singular.
The point here is that using 404 for an out-of-range parametrized request and a 200 for an out-of-range unparametrized request (where pagination is done with default values) establishes a difference between both requests, while in the code, the unparametrized request is just injected default values when coming in and treated equally afterwards. The other layers of code don't need to know the pagination parameters are injected defaults.
Also, if the 200 response contains pagination metadata, the client gets more information with a 200 that includes the total number of items or pages than with a plain 404.
Finally, from a practical perspective, I also find it easier (less code) to return a 200.