Understanding REST: Verbs, error codes, and authentication

后端 未结 10 1786
你的背包
你的背包 2020-11-22 07:39

I am looking for a way to wrap APIs around default functions in my PHP-based web applications, databases and CMSs.

I have looked around and found several \"skeleton

相关标签:
10条回答
  • 2020-11-22 07:41
    1. Use post when you don't know how the new resource URI would look like (you create new user, application would assign the new user it's id), PUT for updating or creating resources that you know how are they going to be represented (example: PUT /myfiles/thisismynewfile.txt)
    2. return the error description in message body
    3. You can use HTTP authentication (if it's enough) Web services should be stateles
    0 讨论(0)
  • 2020-11-22 07:43

    For the examples you stated I'd use the following:

    activate_login

    POST /users/1/activation

    deactivate_login

    DELETE /users/1/activation

    change_password

    PUT /passwords (this assumes the user is authenticated)

    add_credit

    POST /credits (this assumes the user is authenticated)

    For errors you'd return the error in the body in the format that you got the request in, so if you receive:

    DELETE /users/1.xml

    You'd send the response back in XML, the same would be true for JSON etc...

    For authentication you should use http authentication.

    0 讨论(0)
  • 2020-11-22 07:46

    re 1: This looks fine so far. Remember to return the URI of the newly created user in a "Location:" header as part of the response to POST, along with a "201 Created" status code.

    re 2: Activation via GET is a bad idea, and including the verb in the URI is a design smell. You might want to consider returning a form on a GET. In a Web app, this would be an HTML form with a submit button; in the API use case, you might want to return a representation that contains a URI to PUT to to activate the account. Of course you can include this URI in the response on POST to /users, too. Using PUT will ensure your request is idempotent, i.e. it can safely be sent again if the client isn't sure about success. In general, think about what resources you can turn your verbs into (sort of "nounification of verbs"). Ask yourself what method your specific action is most closely aligned with. E.g. change_password -> PUT; deactivate -> probably DELETE; add_credit -> possibly POST or PUT. Point the client to the appropriate URIs by including them in your representations.

    re 3. Don't invent new status codes, unless you believe they're so generic they merit being standardized globally. Try hard to use the most appropriate status code available (read about all of them in RFC 2616). Include additional information in the response body. If you really, really are sure you want to invent a new status code, think again; if you still believe so, make sure to at least pick the right category (1xx -> OK, 2xx -> informational, 3xx -> redirection; 4xx-> client error, 5xx -> server error). Did I mention that inventing new status codes is a bad idea?

    re 4. If in any way possible, use the authentication framework built into HTTP. Check out the way Google does authentication in GData. In general, don't put API keys in your URIs. Try to avoid sessions to enhance scalability and support caching - if the response to a request differs because of something that has happened before, you've usually tied yourself to a specific server process instance. It's much better to turn session state into either client state (e.g. make it part of subsequent requests) or make it explicit by turning it into (server) resource state, i.e. give it its own URI.

    0 讨论(0)
  • 2020-11-22 07:47

    I would suggest (as a first pass) that PUT should only be used for updating existing entities. POST should be used for creating new ones. i.e.

    /api/users     when called with PUT, creates user record
    

    doesn't feel right to me. The rest of your first section (re. verb usage) looks logical, however.

    0 讨论(0)
  • 2020-11-22 07:54

    1. You've got the right idea about how to design your resources, IMHO. I wouldn't change a thing.

    2. Rather than trying to extend HTTP with more verbs, consider what your proposed verbs can be reduced to in terms of the basic HTTP methods and resources. For example, instead of an activate_login verb, you could set up resources like: /api/users/1/login/active which is a simple boolean. To activate a login, just PUT a document there that says 'true' or 1 or whatever. To deactivate, PUT a document there that is empty or says 0 or false.

    Similarly, to change or set passwords, just do PUTs to /api/users/1/password.

    Whenever you need to add something (like a credit) think in terms of POSTs. For example, you could do a POST to a resource like /api/users/1/credits with a body containing the number of credits to add. A PUT on the same resource could be used to overwrite the value rather than add. A POST with a negative number in the body would subtract, and so on.

    3. I'd strongly advise against extending the basic HTTP status codes. If you can't find one that matches your situation exactly, pick the closest one and put the error details in the response body. Also, remember that HTTP headers are extensible; your application can define all the custom headers that you like. One application that I worked on, for example, could return a 404 Not Found under multiple circumstances. Rather than making the client parse the response body for the reason, we just added a new header, X-Status-Extended, which contained our proprietary status code extensions. So you might see a response like:

    HTTP/1.1 404 Not Found    
    X-Status-Extended: 404.3 More Specific Error Here
    

    That way a HTTP client like a web browser will still know what to do with the regular 404 code, and a more sophisticated HTTP client can choose to look at the X-Status-Extended header for more specific information.

    4. For authentication, I recommend using HTTP authentication if you can. But IMHO there's nothing wrong with using cookie-based authentication if that's easier for you.

    0 讨论(0)
  • 2020-11-22 07:55

    Simply put, you are doing this completely backward.

    You should not be approaching this from what URLs you should be using. The URLs will effectively come "for free" once you've decided upon what resources are necessary for your system AND how you will represent those resources, and the interactions between the resources and application state.

    To quote Roy Fielding

    A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types. Any effort spent describing what methods to use on what URIs of interest should be entirely defined within the scope of the processing rules for a media type (and, in most cases, already defined by existing media types). [Failure here implies that out-of-band information is driving interaction instead of hypertext.]

    Folks always start with the URIs and think this is the solution, and then they tend to miss a key concept in REST architecture, notably, as quoted above, "Failure here implies that out-of-band information is driving interaction instead of hypertext."

    To be honest, many see a bunch of URIs and some GETs and PUTs and POSTs and think REST is easy. REST is not easy. RPC over HTTP is easy, moving blobs of data back and forth proxied through HTTP payloads is easy. REST, however, goes beyond that. REST is protocol agnostic. HTTP is just very popular and apt for REST systems.

    REST lives in the media types, their definitions, and how the application drives the actions available to those resources via hypertext (links, effectively).

    There are different view about media types in REST systems. Some favor application specific payloads, while others like uplifting existing media types in to roles that are appropriate for the application. For example, on the one hand you have specific XML schemas designed suited to your application versus using something like XHTML as your representation, perhaps through microformats and other mechanisms.

    Both approaches have their place, I think, the XHTML working very well in scenarios that overlap both the human driven and machine driven web, whereas the former, more specific data types I feel better facilitate machine to machine interactions. I find the uplifting of commodity formats can make content negotiation potentially difficult. "application/xml+yourresource" is much more specific as a media type than "application/xhtml+xml", as the latter can apply to many payloads which may or may not be something a machine client is actually interested in, nor can it determine without introspection.

    However, XHTML works very well (obviously) in the human web where web browsers and rendering is very important.

    You application will guide you in those kinds of decisions.

    Part of the process of designing a REST system is discovering the first class resources in your system, along with the derivative, support resources necessary to support the operations on the primary resources. Once the resources are discovered, then the representation of those resources, as well as the state diagrams showing resource flow via hypertext within the representations because the next challenge.

    Recall that each representation of a resource, in a hypertext system, combines both the actual resource representation along with the state transitions available to the resource. Consider each resource a node in a graph, with the links being the lines leaving that node to other states. These links inform clients not only what can be done, but what is required for them to be done (as a good link combines the URI and the media type required).

    For example, you may have:

    <link href="http://example.com/users" rel="users" type="application/xml+usercollection"/>
    <link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>
    

    Your documentation will talk about the rel field named "users", and the media type of "application/xml+youruser".

    These links may seem redundant, they're all talking to the same URI, pretty much. But they're not.

    This is because for the "users" relation, that link is talking about the collection of users, and you can use the uniform interface to work with the collection (GET to retrieve all of them, DELETE to delete all of them, etc.)

    If you POST to this URL, you will need to pass a "application/xml+usercollection" document, which will probably only contain a single user instance within the document so you can add the user, or not, perhaps, to add several at once. Perhaps your documentation will suggest that you can simply pass a single user type, instead of the collection.

    You can see what the application requires in order to perform a search, as defined by the "search" link and it's mediatype. The documentation for the search media type will tell you how this behaves, and what to expect as results.

    The takeaway here, though, is the URIs themselves are basically unimportant. The application is in control of the URIs, not the clients. Beyond a few 'entry points', your clients should rely on the URIs provided by the application for its work.

    The client needs to know how to manipulate and interpret the media types, but doesn't much need to care where it goes.

    These two links are semantically identical in a clients eyes:

    <link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>
    <link href="http://example.com/AW163FH87SGV" rel="search" type="application/xml+usersearchcriteria"/>
    

    So, focus on your resources. Focus on their state transitions in the application and how that's best achieved.

    0 讨论(0)
提交回复
热议问题