-->

RESTful API Design and CQRS

2020-07-23 06:16发布

问题:

I was thinking of how to make a RESTFul API more intention revealing. A common patter I see around various blogs on this is that conventional REST API results in

Ban a Player -> POST /players.

But I were to change to a more intention revealing interface , I could use

Ban a Player -> POST /players/{ playerid }/banPlayer

The second one I feel is more intention revealing.

The common objection I get from the team is that the second one does not comply with start REST style.

Also currently I cannot move away from a RESTful API.

I would like to hear your thoughts on this.

回答1:

With Restful API design there are two schools of thought around how to apply actions to a resource.

  1. You describe the action to be taken against the resource in the Uri:

    Request Uri:
    POST /players/{id}/ban

    Note: Just use ban - we already know the resource is a player, its in the base Uri.

  2. You can have the action in the body of the request:

    Request Uri:
    POST /players/{id}

    Request Body:
    { 'action': 'ban' }

You can pick either way - whichever you prefer, there is lots of discussion on both but ultimately both are correct.

Note:

My assumption here that banning a player is more than just updating a part of it, but rather a system action (or state transition) relating to the player. Otherwise if it was just an update to the player resource you should handle with a PATCH or PUT as appropriate.

Some discussions for reference:

  • http://restful-api-design.readthedocs.io/en/latest/methods.html
  • https://github.com/interagent/http-api-design/issues/58
  • https://nordicapis.com/designing-a-true-rest-state-machine/
  • https://softwareengineering.stackexchange.com/questions/141410/restful-state-changing-actions

With plenty more if you do some Googling...



回答2:

Both examples you mention are not proper REST apis.

A more correct way to implement this, is say you have a 'player' resource. That player resource might have a property banned, which is a boolean.

To set the banned property to true, you would make a request such as this:

PUT /players/1234

And have the request body contain the new value for the banned property.



回答3:

According to the REST API approaches, you need to use your entities in URI, so, as banPlayer is not an entity, you cannot use it. I suggest to UPDATE your record with PUT method. Here you can read more about rules. Actually, the first section about URIs is just about your case.



回答4:

Long story short: it shouldn't be mandatory to be intention revealing but if you want to add some DDD on how this API looks like then it is nothing that prevents you from doing that

According to HATEOAS constraint of a RESTful web API (this constraint is an essential part of the "uniform interface" feature of REST, as defined in Roy Fielding's doctoral dissertation), the software clients of your API should not care about the URLs. Every possible&permitted action should be included in the response, with the corresponding link relation and URI. In this way you have to hardcode only the link relations.

This constraint does not however prevent you from making the API more intention revealing for the Human clients that try to understand the overall architecture. I recommend you to choose this path, as Human users are at least as important as the software that they write.

Roy Fielding wrote about this on his blog post.



回答5:

The common objection I get from the team is that the second one does not comply with start REST style.

The easy answer is this: consistency in the API has value, whether REST or not. So "that's not how we do it here" is going to trump "but REST says".

The spelling of URI in an API is a lot like the spelling of method names in your code. There are a lot of different arguments for different styles, but "local convention" is a strong argument by itself.

That said -- REST does not care what spelling you use for your identifiers.

This is what Fielding had to say in 2008

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.]

In band would be including the URI in the representation of the resource -- putting it into the description of a form in the HTML document. Out of band is documenting the URI, and expecting people to do the right thing with it.

Note: there's nothing wrong with human-readable URI, or documenting the URI that should be used. But notice that you can post questions to stackoverflow even though the people who wrote your browser didn't read stack overflow's API documentation -- that's REST.



回答6:

Since you asked for the RESTful way not the best way, here's my thoughts.

Your RESTful URI options include:

  • /players
  • /players/{ playerid }/banPlayer
  • /player-banning
  • /entities?action=ban_player&method=PUT
  • /banana
  • anything else, REST doesn't dictate what your URIs should look like

The RESTful way is to expose knowledge of the next available states purely through hypertext. To do REST, you must use Hypertext As The Engine Of Application State (HATEOAS). Relying on client knowledge of the URI is depending on out-of-band knowledge, which is antithetical to REST.

Your resources do not need to map directly to your business objects. If you choose, you can represent user intent itself as a resource, e.g. a banned player event resource. You can POST to it with some info on which player to ban, and subsequent GETs will provide information on the event.

Oh and just because REST doesn't care what your URIs are, doesn't mean you shouldn't. You'll just have to use a different standard to decide what's best.



标签: rest cqrs