There's a general misconception (and misuse) associated with 403 Forbidden
: it's not supposed to give anything away about what the server thinks about the request. It's specifically designed to say,
I get what you're requesting, but I'm not going handle the request, no matter what you try. So stop trying.
Any UA or client should interpret that to mean that the request will never work, and respond appropriately.
This has implications for clients handling requests on behalf of users: if a user isn't logged in, or mistypes, the client handling the request should reply, "I'm sorry, but I can't do anything" after the first time it gets the 403
and stop handling future requests. Obviously, if you want a user to still be able to request access to their personal information after a failure, this is a user-hostile behavior.
403
is in contrast to 401 Authorization Required
, which does give away that the server will handle the request as long as you pass the correct credentials. This is usually what people think about when they hear 403
.
It's also in contrast with 404 Page Not Found
which, as others pointed out, is designed not only to say "I can't find that page" but to suggest to the client that the server makes no claims of success or failure for future requests.
With 401
and 404
, the server doesn't say anything to the client or UA about how they should proceed: they can keep trying in hopes of getting a different response.
So 404
is the appropriate way to handle a page you don't want to show to everyone, but don't want to give away anything about why you won't show it in certain situations.
Of course, this assumes the client making the request cares for petty RFC flippancy. A malicious enough client isn't going to care about the status code returned except in an incidental manner. One will know it's a hidden user page (or a potential hidden user page) by comparing it to other, known user pages.
That is, let's say your handler is users/*
. If I know users/foo
, users/bar
and users/baaz
work, the server returning a 401
, 403
, or 404
for users/quux
doesn't mean I'm not going to try it, especially if I have reason to believe there is a quux
user. A standard example scenario is Facebook: my profile is private, but my comments on public profiles are not. A malicious client knows I exist even if you return 404
on my profile page.
So status codes aren't for the malicious use cases, they're for the clients playing by the rules. And for those clients, a 401
or a 404
request is most appropriate.
Is it appropriate to mix some sort of action call with a resource URI (e.g. /collection/123?action=resendEmail
)? Would it be better to specify the action and pass the resource id to it (e.g. /collection/resendEmail?id=123
)? Is this the wrong way to be going about it? Traditionally (at least with HTTP) the action being performed is the request method (GET, POST, PUT, DELETE), but those don't really allow for custom actions with a resource.
I'd rather model that in a different way, with a collection of resources representing the emails that are to be sent; the sending will be processed by the internals of the service in due course, at which point the corresponding resource will be removed. (Or the user could DELETE the resource early, causing a canceling of the request to do the send.)
Whatever you do, don't put verbs in the resource name! That's the noun (and the query part is the set of adjectives). Nouning verbs weirds REST!
I use the querystring portion of the URL to filter the set of resources returned when querying a collection (e.g. /collection?someField=someval
). Within my API controller I then determine what kind of comparison it is going to do with that field and value. I've found this really doesn't work. I need a way to allow the API user to specify the type of comparison they want to perform.
The best idea I've come up with so far is to allow the API user to specify it as an appendage to the field name (e.g. /collection?someField:gte=someval
- to indicate that it should return resources where someField is greater than or equal to (>=
) whatever someval
is. Is this a good idea? A bad idea? If so, why? Is there a better way to allow the user to specify the type of comparison to perform with the given field and value?
I'd rather specify a general filter clause and have that as an optional query parameter on any request to fetch the contents of the collection. The client can then specify exactly how to restrict the set returned, in whatever way you desire. I'd also worry a bit about the discoverability of the filter/query language; the richer you make it, the harder it is for arbitrary clients to discover. An alternative approach which, at least theoretically, deals with that discoverability issue is to allow making restriction sub-resources of the collection, which clients obtain by POSTing a document describing the restriction to the collection resource. It's still a slight abuse, but at least it's one you can clearly make discoverable!
This sort of discoverability is one of the things that I find least strong with REST.
I often see URI's that look something like /person/123/dogs
to get the persons dogs. I generally have avoided something like that because in the end I figure that by creating a URI like that you are actually just accessing a dogs collection filtered by a specific person ID. It would be equivalent to /dogs?person=123
. Is there ever really a good reason for a REST URI to be more than two levels deep (/collection/resource_id
)?
When the nested collection is truly a sub-feature of the outer collection's member entities, it is reasonable to structure them as a sub-resource. By “sub-feature” I mean something like UML composition relation, where destroying the outer resource naturally means destroying the inner collection.
Other types of collection can be modeled as an HTTP redirect; thus /person/123/dogs
can indeed be responded to by doing a 307 that redirects to /dogs?person=123
. In this case, the collection isn't actually UML composition, but rather UML aggregation. The difference matters; it is significant!
Best Answer
I think in the most cases the most useful reaction would be to return a regular answer (HTTP 200) and send empty data. For example if you are returning JSON you could send
Another good option may be the HTTP Code '204 - No Content'.
The 4xx codes describe a client failure, so the 404 code wouldn't be a good idea, because it's a valid request, even if there are no matching data.