What to do when you need more verbs in REST

前端 未结 4 668
无人共我
无人共我 2021-01-30 11:32

There is another similar question to mine, but the discussion veered away from the problem I\'m encounting.

Say I have a system that deals with expense reports (ER). Yo

相关标签:
4条回答
  • 2021-01-30 11:41

    I think you are making it more complicated then it needs to be. Treat your expense report as a complete resource and any edits to it are simply a matter of PUTing a new representation to the URI where the resource lives. No need to have custom actions to change status, just GET the resource - make your edits - then PUT it back. Done.

    0 讨论(0)
  • 2021-01-30 11:48

    For manipulating the status of resources I often like to use "status buckets". The idea is that when you "add" an object into that bucket, it gets that status. It is like having in and out boxes on your desk. The location of the document defines its status.

    So, you could do something simple like:

    POST /Expenses/Approved
    { .. Expense document ... }
    

    or for the more complex case that you hinted at in your document where multiple people have to approve the document.

    POST /ExpenseApprover/John/ApprovedExpenses
    { .. Expense document ... }
    

    If you need to submit an expense report for approval you can do

    POST /ExpenseApprover/John/Pending
    { .. Expense document ... }
    

    And don't forget hypermedia can make this process workflow enabled. Imagine someone creates an initial expense report, the server could response with the following JSON.

    { "id" : "234",
      "title": "Trip to NY", "totalcost": "400 USD",
      "submit_url": "/ExpenseApprover/John/Pending"
    }
    

    The client can POST to the submit_url to move the expense onto it's next step. Then when John retrieves the expense, he gets

    { "id" : "234",
      "title": "Trip to NY", "totalcost": "400 USD",
      "approve_url": "/ExpenseApprover/Finance/Pending",
      "denied_url": "/ExpenseApprover/John/Denied",
    }
    

    When the finance department do a

    GET /ExpenseApprover/Finance/Pending
    

    they could get a list of Pending Expenses,

    { PendingExpense: [
        { "id" : "234",
          "title": "Trip to NY", "totalcost": "400 USD",
         "approve_url": "/Expense/Approved",
         "denied_url": "/ExpenseApprover/Finance/Denied",
        }
       ]
    }
    

    Forgive my horrible JSON, but I hope you get the idea that including the link in the response you can guide the flow of your application. You can also stop worrying so much about what the url looks like because the client doesn't really care. The client reads the url from the response based on the property name and dereferences it. You can change your mind a million times on what the best url structure is and your clients will not be affected. Just don't change the property name!

    These "status bucket" urls are used to hold a set of resources that have a similar status. The idea is that you POST a document into the collection:

    POST /ExpenseApprover/Finance/Denied
    
    {"id" : "234", "title": "Trip to NY", "totalcost": "400 USD"}
    

    It is not necessary to uniquely define the particular expense that you are adding in the URL because the body document should contain some kind of identifying key value.
    This technique is just as valid for flagging expenses has having discrepancies. You simply create a new resource that holds expenses with discrepancies and post your expense report into to it.

    POST /Discrepancies
    {"id" : "234", "title": "Trip to NY", "totalcost": "400 USD"}
    
    0 讨论(0)
  • 2021-01-30 12:03

    What to do when you need more verbs in REST

    You have 3 options:

    • create a new resource and use the available HTTP methods with it to describe what you want
    • check the standard, maybe you are missing an already existing method
    • send a new RFC about your desired method to ietf maybe they accept it

    In your case you just missed the RFC of the PATCH method by a month or two.

    0 讨论(0)
  • 2021-01-30 12:05

    The REST architecture says that a resource is managed by the server and identified by a URL.

    In that light /er/1/approval is not a reasonable URL or model to use, unless you have an approval object or entity that you manage and manipulate on the server side. Seems to me the entity is the expense report itself, which means, /er/1 is your URL path.

    Now, as for verbs... you can send (POST) any message you like to that resource.

    set data:

    { action: "modify", data: { purpose : "Club hopping" } }
    

    approve:

    { action: "approve" }
    

    add item:

    { action:"additem", data: { amount:72.13, category:113, note:"client dinner" }}
    

    etc.


    From Fielding's Ch5, which defined REST,

    The in-parameters (of a request) consist of request control data, a resource identifier indicating the target of the request, and an optional representation.

    ...and...

    Control data defines the purpose of a message between components, such as the action being requested or the meaning of a response. It is also used to parameterize requests and override the default behavior of some connecting elements. For example, cache behavior can be modified by control data included in the request or response message.


    Therefore if you'd like to perform multiple actions on a resource, then you should embed in the "control data" multiple messages or action requests. In my example, the posted data would be something like:

    { action: "modify", data: { purpose : "Club hopping" } }
    { action: "approve" }
    

    But you'd probably want to generalize that so that it is:

    { actions: [ {action:"modify", data: {...} }, { action:"approve"} ] } 
    

    The messages or actions your server can handle on each particular type of entity are up to you to define.

    ps: sometimes REST implementations use HTTP PUT to create a resource and POST to modify or act on an existing resource.

    and: I liked the article, How to GET a cup of coffee.

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