问题
For the development of an API we want to create a new organization and a new user for that organization with one form submission (the registration) using a clean RESTful API design.
As we don't want to mix different resources (organizations and users) and create the them with one call (the response of that call is it an organization or an user?) we need to split the registration into two calls: create organization first and directly after create user.
But if the user and organization creation is split into two independent API calls we see the following problems:
- How to handle errors? Eg. if the create organization is successful but the create user fails due to an error (eg. user email already exists). In that case an organization without any user is created and nobody can login to change the organization resource.
- How to handle the authorization after creating the organization? Can everybody with the organization id simply create a new user without any login check (login is only possible with email and password)? Or will the create organization return a token to create the first user? (The token logic will make the client quite complicated: how to handle resubmissions etc.)
回答1:
How to handle errors? Eg. if the create organization is successful but the create user fails due to an error (eg. user email already exists). In that case an organization without any user is created and nobody can login to change the organization resource
Except for PATCH none of the standard HTTP methods really tackles a transactional behavior. PATCH
i.e. demands that all of the instruction steps defined within the patch document need to be processed atomically. Either all or none of them have to be applied. So if one instruction fails none of the modifications must be applied. The difference between PATCH and the other methods however is, that PATCH should contain a set of instructions to apply onto one or multiple resources while the other operation in a sense target a single document usually.
If viewed from a document management system, which HTTP truely is at its heart as any business rules you conclude are just a side-effect of the document management (see this great talk), the operations of HTTP make a bit more sense. I.e. PUT
replaces the current document with the one provided. If none existed so far it is similar to a document creation. The spec here mentions that PUT
is allowed to have side-effects. I.e. similar to GIT where a commit will push the head forward though the actual commit will still be accessible via its own URI. DELETE
removes the association of the URI to the document stored. Whether that leads to a document removal on the imaginary filesystem or not is an implementation detail. GET
will only return the content of that document to the invoker and POST only processes the request according to the service own semantics giving no further promises on what it does with the payload. So whether one or multiple resources are created, or even none at all, is up to the implementation. However, if you create resources you need to return a 201 Created
response containing a Location
header with the URI of the newly created resource that hints clients about the newly created resource. In the case of multiple resources being created, the spec is a bit less clear what should be returned in that case as only one Location header may appear within the respone.
The usage of a form-based creation approach is for sure a RESTful approach as here the server teaches a client on the needed inputs to perform the task. A client usually isn't that much interested in how the server is actually processing and storing the data, all it is interested in is the completion of its request, either successfully or with a hint on the problems the server had while processing the request.
Whether you create everything in one go or divide each part to its own request is some choice you have to make. I.e. on a typical Web site you may encounter a wizzard like approach were you enter some organization related information first into a form, click a submit button and then get a further form response asking you to enter user details with a further response summarizing the details and asking for confirmation. The data could be stored in some temporary resource that on confirming the summary will create everything in one go mimicking some kind of atomic processing failing the creation of the organization in case some problems were encountered with the user details and such. Such an approach is convenient if you have multiple optional data that depends on previous choices.
Of course you could also enter the data in one single form and send it to the server via a POST
request and then create the respective resources that way. Which URL to return in the Location
header is yet a different decision. If the main resource being created is the organization that I'd opt for the organization URI, especially if it allows to list its defined users. Internally you can utilize transactions to guarantee a consistent state between organization and user and in a failure to do this you simply can roll back the transaction and return an error to the user.
If you attempt to divide the creation into multiple steps you surely will have to deal with the exception case of the user resources and its effects on the organization resource. As mentioned HTTP does not give any hints on that as to HTTP these are two separate and unrelated requests. Here, not the server should be the smart thing but the client has to be. If it encounters problems while user creation it should perform the cleanup of the organization itself. The server/API is just seen as dumb storage thing in such a case.
How to handle the authorization after creating the organization? Can everybody with the organization id simply create a new user without any login check (login is only possible with email and password)? Or will the create organization return a token to create the first user? (The token logic will make the client quite complicated: how to handle resubmissions etc.)
It basically depends on your design here. Usually some kind of permission management should be added to the API. Some frameworks already contain support for such, i.e. in the Java and Spring ecosystem you can add certain annotations onto operation endpoints and business methods that check certain assigned user roles and permissions and only allow access if they are available.
In case you have a split organization and user creation approach and encountered a problem during user creation you could send a form back to the client requesting other user details as one already exist for an other organization until valid data was returned. Plenty of Web-based APIs nowadys send some confirmation links via mail to verify the correctness of the email-address and the user is only "logged-in" the first time when he clicked the activation link in that email. In pure HTTP you'd send a HTTP Authorization
header containing the user credentials. In the absence of an activated user the service would return 401 Unauthorized
as failure preventing the user from authenticating with the service. In such a case an external administrative entity (i.e. a project manager or administrator of the API) would have to create a user for that organization and send the data to the requestor. Though, such a state should be highly avoided IMO. Here, probing a user for admissible user details is sure preferrable, I guess.
You might also run some kind of cleanup routine in the back (or perform a manual cleanup task) after a given threshold amount on organizations that have no user assigned to it to free up resources and avoid carrying arround inconsitent state, according to your definition, as there has to be a user assigned to an organization.
As you see, you have a couple of design choices how to tackle such a scenario. Whether you enter the data in one form and send everything in one go to the server or use a temporary resource to collect data from n consecutive form requests until the user confirms that data and you process everything in one go atomically or you use dedicated requests per task and have some backing routines that check the consitency of the system is up to you.
A final note on a thing mentioned in your title. REST clients shouldn't consider resources to have a specific type as this leads to clients expecting certain endpoints to return certain types. This also leads to clients interpreting URIs to determine the type of that resource. As the server is free to change its URI scheme any time it wants to, chances are that clients wont be able to determine the type based on the URI automatically without a developer building in that kind of knowledge into the client. This avoids the actual benefits a REST architecture should provide, namely the freedom for evolution in future that is enabled by the decoupling of clients from servers. Instead of using typed resources clients should rely on content-type negotiation where standardized representation formats understood and supported by both, client and server, are exchanged. The media-types defining these representation formats specify the processing rules and semantics of each of the elements within the payload and allow interoperability.
来源:https://stackoverflow.com/questions/58625037/clean-way-to-create-multiple-resources-different-types-with-an-api