I'm working on building a RESTful web service. I have read up on the principles of using HTTP for every mechanism as far as it will take you, and most of the time, like when fetching resources, it works pretty well.
But when I need to POST a new entry of some sort, in the interest of clarity and robustness, no matter what the client may do, I want to offer the particular validation errors that the new entry may have failed at. Additionally, there are specific errors where, say, the data for creating a new user is perfectly valid, but a nickname or an email address may be taken. Simply returning 409 Conflict
doesn't finely enough detail which of the nickname or the email address was taken.
So getting around this isn't rocket science: document a bunch of specific error codes and return an object with errors:
{ errors: [4, 8, 42] }
This means that in the case of unsuccessful requests, I'm not returning the resource or its key as I may be expected to by the REST philosophy. Similarly, when I return many resources, I have to frame them in some way in an array.
So my question is: would I still be providing a well-behaved RESTful web service if I standardized an envelope to use for every request, such that, for example, there's always an object like { errors, isSuccessful, content }
?
I have previously built RPC-style web services that used this, but I don't want to make something that's "almost REST". If there's going to be any point to being REST, I'd want to be as well-behaved as possible.
If the answer is "hell no", which I think it may be, I would like to hear if it's at least solving the validation problem correctly, and what a good reference for this sort of problem solving might be, because most guides I've found have only detailed the simple cases.
Using nouns for naming URIs is a REST API naming best practice, but when should you use singular or plural nouns? In general, using plural nouns is preferred unless the resource is clearly a singular concept (e.g. https://api.example.com/users/admin for the administrative user).
I'd say "hell yes!" (contrary to someone here who said "hell no") to an envelope! There's always some extra information that needs to be sent from some endpoints. Pagination, errorMessages, debugMessages for example. An example how facebook does it:
Response from get friends request { "data": [ { "id": "68370", "name": "Magnus" }, { "id": "726497", "name": "Leon" }, { "id": "57034", "name": "Gonçalo" } ], "paging": { "next": "https://graph.facebook.com/v2.1/723783051/friends?fields=id,name&limit=5000&offset=5000&__after_id=enc_AeyGEGXHV9pJmWq2OQeWtQ2ImrJmkezZrs6z1WXXdz14Rhr2nstGCSLs0e5ErhDbJyQ" }, "summary": { "total_count": 200 } }
Here we have pagination with the next link to request to get the next chunk of users and a summary with the total number of friends that can be fetched. However they don't always send this envelope, sometimes the data can go straight in the root of the body. Always sending the data the same way makes it much easier for clients to parse the data since they can do it the same for all endpoints. A small example of how clients can handle envelope responses:
public class Response<T> { public T data; public Paging paging; public Summary summary; } public class Paging { public String next; } public class Summary { public int totalCount; } public class WebRequest { public Response<List<User>> getFriends() { String json = FacebookApi.getFriends(); Response<List<User>> response = Parser.parse(json); return response; } }
This Response object could then be used for all endpoints by just changing List to the data that the endpoints returns.
HTTP is your envelope. You're doing the right thing by returning a 4** error code.
Having said that, there is nothing wrong with having a descriptive body on a response – in fact in the HTTP RFC, most of the HTTP Error codes advocate that you do return a description of why the error occurred. See 403 for example:
If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, it SHOULD describe the reason for the refusal in the entity.
So you're okay to continue to use the body of a response for a more detailed description of the error(s). If you're unsure of the specific HTTP error response to use (e.g. multiple errors), and you know that the user should not repeat the request as they just did it, I usually fall back to using 400.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With