0

Let's say you have a resource that you can do normal PUT/POST/GET operations on. It represents a BLOB of data and the methods retrieve representations of the data, be they metadata about the BLOB or the BLOB itself.

The resource is something that can be processed by the server on request. In this instance a file that can be parsed multiple times.

How do I initiate that processing? It's a bit RPC like. Is there best practice around this?

I've read this: RESTFul: state changing actions but it doesn't really answer the question.

(First time on programmers. This is the right place for this sort of question, right?)

  • 1
    What is the result of the processing? Sounds like that may be another resource? In that case POST to the new resource (collection) with the resource and identifier of the stuff to be processed in the body of the post request. Use POST to /resources if the server determines the identification of the new resource, use PUT to /resources/myidentifier if the caller determines the identification of the new resource. – Marjan Venema Oct 29 '13 at 20:10
  • Now theres an interesting idea. It does create other resources, lots of them. I also need to be able to delete them and could do it this way too. I really like this answer. There is one problem here for me, but it's a technical one. I will have to make the resource authorise depending on the Method. All users can GET, only some can POST/DELETE – tom Oct 30 '13 at 18:39
  • What did you end up deciding on? – bstempi Nov 16 '13 at 2:52
  • I've gone with the processor-as-a-resource approach. You POST a Blob ID to a resource like /processor/{id} and it returns a job representation. You can query the job resource to get the status and results of the processing. – tom Nov 18 '13 at 14:24
1

The processor for this resource could be its own resource. Consider for a minute that the name of the BLOB resources is /data/. By making the processor its own resource, you allow yourself to make different processors. This also prevents you from having to modify the data resource to facilitate processing. It could work like this:

POST

A post might take the id of the data that you're trying to process. This would return some sort of id that represents the process or job.

GET

Given an Id, this would get the result of the processing. If it's not done, you could return an empty response or a 204 NO CONTENT.

HEAD

Given an Id, this could return just the status without returning the result of the processing. The "job metadata," if you will. If you really wanted to, you could return the parameters, such as the data Id that is being processed, the start date, etc.

DELETE

Given an Id, this would cancel the request.

PUT

I'm not sure that PUT is applicable here.

  • I'd not thought of processors-as-resources. I like it. Definitely something to consider here. – tom Oct 31 '13 at 9:42
  • I'm definitely not the first to have that idea. I just finished reading "RESTful Web Services", and they present this idea. The argument that they make is that the job itself is a resource and is independent of the data that it's processing. Book link: shop.oreilly.com/product/9780596529260.do – bstempi Oct 31 '13 at 14:12
  • Cheers. Book on its way. – tom Oct 31 '13 at 17:00
1

The HTTP protocol has built-in support for the concept of background or batch operations in the form of status code 202:

10.2.3 202 Accepted

The request has been accepted for processing, but the processing has not been completed. The request might or might not eventually be acted upon, as it might be disallowed when processing actually takes place. There is no facility for re-sending a status code from an asynchronous operation such as this.

The 202 response is intentionally non-committal. Its purpose is to allow a server to accept a request for some other process (perhaps a batch-oriented process that is only run once per day) without requiring that the user agent's connection to the server persist until the process is completed. The entity returned with this response SHOULD include an indication of the request's current status and either a pointer to a status monitor or some estimate of when the user can expect the request to be fulfilled.

Clients will appreciate if your 202 response also includes a Location header where they may check the status. This is especially important for any automated end-to-end tests.

I'm not sure if this is really your question, because a large chunk of it actually seems to be referring to Content Negotiation, which is how you store or retrieve "different representations" of something. Of course you can just have different URLs, such as /widgets/1/info and widgets/1/blob, but the preferred way of doing this is through conneg. There are two headers that are relevant here:

  1. Accept, which specifies what the client wants to receive;
  2. Content-Type, which specifies what the client is actually sending.

So, for example, let's say you have a widgets API that is supposed to be able to support widgets in either the "ACME" or "OMNI" format. The client can send in either format via the Content-Type header:

  • PUT /widgets/123
    Content-Type: application/vnd.foo.acme-widget+json

  • PUT /widgets/456
    Content-Type: application/vnd.foo.omni-widget+json

The server should understand both of these requests and choose the correct parsing method based on the Content-Type header. On the receiving end, the client specifies with Accept:

  • GET /widgets/123
    Accept: application/vnd.foo.acme-widget+json

  • GET /widgets/123
    Accept: application/vnd.foo.omni-widget+json

Both requests are for the same resource, and they use the same method (GET), but when the server sees the first one, it should provide the data using the ACME scheme, and when it sees the second one, it should provide the same data in OMNI scheme.

One considerable benefit of this approach is that every resource still has a canonical URI which you can use in Location headers and so on. It's not required, but generally ideal in REST for each resource to "live" at exactly one URI and no more.

If the difference is truly between data and metadata then you probably should have an actual metadata resource, like /widgets/123/info, but if you are dealing with different representations of the same data then you should use Accept and Content-Type.

I think that covers all aspects of your question, but if you think something is missing - please clarify.

  • Thanks for the detailed answer. I guess I'm wondering how do I model actions that /transform/ or otherwise operate/monidfy/process a given resource. For example a parse operation or a decryption process that generates persistent results. For reference this is in the context of moving a GWT RPC service to a restful one. I've previously modeled the Accept as you mentioned above when uploading the BLOBs and see how I could use this again here. – tom Oct 30 '13 at 18:33
  • @tom: Actions that store resources should be PUT or POST. Broadly speaking, that's PUT if it's idempotent, POST if it's not. Most of the time, though, the input for these operations should come from the client. I'd say that something smells a little funny if you have to POST to complete processing on a resource that already exists. I can certainly think of valid cases, but without knowing the details of your situation, I'd say you should make sure that you're actually solving the right problem, and that it wouldn't be more appropriate to use something like a cron job or pub/sub. – Aaronaught Oct 31 '13 at 0:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.