Overall:
Both PUT and POST can be used for creating.
You have to ask, "what are you performing the action upon?", to distinguish what you should be using. Let's assume you're designing an API for asking questions. If you want to use POST, then you would do that to a list of questions. If you want to use PUT, then you would do that to a particular question.
Great, both can be used, so which one should I use in my RESTful design:
You do not need to support both PUT and POST.
Which you use is up to you. But just remember to use the right one depending on what object you are referencing in the request.
Some considerations:
- Do you name the URL objects you create explicitly, or let the server decide? If you name them then use PUT. If you let the server decide then use POST.
- PUT is defined to assume idempotency, so if you PUT an object twice, it should have no additional effect. This is a nice property, so I would use PUT when possible. Just make sure that the PUT-idempotency actually is implemented correctly in the server.
- You can update or create a resource with PUT with the same object URL
- With POST you can have 2 requests coming in at the same time making modifications to a URL, and they may update different parts of the object.
An example:
I wrote the following as part of another answer on SO regarding this:
POST:
Used to modify and update a resource
POST /questions/<existing_question> HTTP/1.1
Host: www.example.com/
Note that the following is an error:
POST /questions/<new_question> HTTP/1.1
Host: www.example.com/
If the URL is not yet created, you
should not be using POST to create it
while specifying the name. This should
result in a 'resource not found' error
because <new_question>
does not exist
yet. You should PUT the <new_question>
resource on the server first.
You could though do something like
this to create a resources using POST:
POST /questions HTTP/1.1
Host: www.example.com/
Note that in this case the resource
name is not specified, the new objects
URL path would be returned to you.
PUT:
Used to create a resource, or
overwrite it. While you specify the
resources new URL.
For a new resource:
PUT /questions/<new_question> HTTP/1.1
Host: www.example.com/
To overwrite an existing resource:
PUT /questions/<existing_question> HTTP/1.1
Host: www.example.com/
Additionally, and a bit more concisely, RFC 7231 Section 4.3.4 PUT states (emphasis added),
4.3.4. PUT
The PUT method requests that the state of the target resource be
created
or replaced
with the state defined by the representation
enclosed in the request message payload.
General principles for good URI design:
- Don't use query parameters to alter state
- Don't use mixed-case paths if you can help it; lowercase is best
- Don't use implementation-specific extensions in your URIs (.php, .py, .pl, etc.)
- Don't fall into RPC with your URIs
- Do limit your URI space as much as possible
- Do keep path segments short
- Do prefer either
/resource
or /resource/
; create 301 redirects from the one you don't use
- Do use query parameters for sub-selection of a resource; i.e. pagination, search queries
- Do move stuff out of the URI that should be in an HTTP header or a body
(Note: I did not say "RESTful URI design"; URIs are essentially opaque in REST.)
General principles for HTTP method choice:
- Don't ever use GET to alter state; this is a great way to have the Googlebot ruin your day
- Don't use PUT unless you are updating an entire resource
- Don't use PUT unless you can also legitimately do a GET on the same URI
- Don't use POST to retrieve information that is long-lived or that might be reasonable to cache
- Don't perform an operation that is not idempotent with PUT
- Do use GET for as much as possible
- Do use POST in preference to PUT when in doubt
- Do use POST whenever you have to do something that feels RPC-like
- Do use PUT for classes of resources that are larger or hierarchical
- Do use DELETE in preference to POST to remove resources
- Do use GET for things like calculations, unless your input is large, in which case use POST
General principles of web service design with HTTP:
- Don't put metadata in the body of a response that should be in a header
- Don't put metadata in a separate resource unless including it would create significant overhead
- Do use the appropriate status code
201 Created
after creating a resource; resource must exist at the time the response is sent
202 Accepted
after performing an operation successfully or creating a resource asynchronously
400 Bad Request
when someone does an operation on data that's clearly bogus; for your application this could be a validation error; generally reserve 500 for uncaught exceptions
401 Unauthorized
when someone accesses your API either without supplying a necessary Authorization
header or when the credentials within the Authorization
are invalid; don't use this response code if you aren't expecting credentials via an Authorization
header.
403 Forbidden
when someone accesses your API in a way that might be malicious or if they aren't authorized
405 Method Not Allowed
when someone uses POST when they should have used PUT, etc
413 Request Entity Too Large
when someone attempts to send you an unacceptably large file
418 I'm a teapot
when attempting to brew coffee with a teapot
- Do use caching headers whenever you can
ETag
headers are good when you can easily reduce a resource to a hash value
Last-Modified
should indicate to you that keeping around a timestamp of when resources are updated is a good idea
Cache-Control
and Expires
should be given sensible values
- Do everything you can to honor caching headers in a request (
If-None-Modified
, If-Modified-Since
)
- Do use redirects when they make sense, but these should be rare for a web service
With regard to your specific question, POST should be used for #4 and #5. These operations fall under the "RPC-like" guideline above. For #5, remember that POST does not necessarily have to use Content-Type: application/x-www-form-urlencoded
. This could just as easily be a JSON or CSV payload.
Best Answer
Django-rest-framework works well even without tying it to a model. Your approach sounds ok, but I believe you can trim some of the steps to get everything working.
For example, rest framework comes with a few built-in renderers. Out of the box it can return JSON and XML to the API consumer. You can also enable YAML by just installing the required python module. Django-rest-framework will output any basic object like dict, list and tuple without any extra work on your part.
So basically you only have to create the function or class that takes in arguments, does all of the required calculations and returns its results in a tuple to the REST api view. If JSON and/or XML fits your needs, django-rest-framework will take care of the serialization for you.
You can skip steps 2 and 3 in this case, and just use one class for calculations and one for presentation to the API consumer.
Here are a few snippets may help you out:
Please note that I have not tested this. It's only meant as an example, but it should work :)
The CalcClass:
The REST view:
Your urls.py:
This code should output a list of lists when you access http://example.com/api/v1.0/resource/?format=json. If using a suffix, you can substitute
?format=json
with.json
. You may also specify the encoding you wish to get back by adding"Content-type"
or"Accept"
to the headers.Hope this helps you out.