What is the "Restful" way to command a server? - rest

I have a REST endpoint to create an application configuration as such
POST /applications
With a body
{
"appName" : "my-new-app"
}
I returns a newly created application configuration:
{
"appName": "my-new-app",
"appId": "2ed17ff700664dad9bb32e400d39dc68",
"apiKey": "$2a$10$XVDH9F3Ix4lx2LdxeJ4ZOe7H.bw/Me5qAmaIGF.95lUgkerfTG7NW",
"masterKey": "$2a$10$XVDH9F3Ix4lx2LdxeJ4ZOeSZLR1hVSXk2We/DqQahyOFFY6nOfbHS",
"dateCreated": "2021-03-28T11:00:07.340+00:00",
"dateUpdated": "2021-03-28T11:00:07.340+00:00"
}
Note: The keys are auto-generated in the server and not passed from the client.
My question here is, what's the RESTful way to command the server to reset the keys for example:
PUT /applications/my-new-app/update_keys is not noun-based and thus, not restful, also passing a command as query parameter does not also seem to be restful since this is not a GET method rather it's a PUT (update) method.

Here's one way to send a command that is as much as possible RESTful:
Endpoint:
POST /application/:appName/actions
Example Payload:
{
"actions" : [
{
"action" : "name_of_command",
"arguments" : {
"arg1" : "param1"
}
},
{
"action" : "reset_keys",
"arguments" : {
}
}
]
}
Actions would be nouns that are part of the endpoint, and the server will process actions that are submitted (or posted) within the endpoint. And an array of actions would be best suited to allow multiple actions to be sent. And each action having arguments would also be desirable for future actions that would need arguments.

what's the RESTful way to command the server to reset the keys for example:
How would you do it with a web site?
You would be looking at some web page like /www/applications/my-new-app; within the data or the metadata you would find a link. Following that link would bring you to a form; the form would have input controls describing what fields you need to provide to send the message, in addition to any "hidden" inputs. When you click the submit button, your user agent would collect your inputs, construct from them the appropriate message body, then use the form metadata to determine what request method and uri to use.
The client never has to guess what URI to use, because the server is providing links to guide the way.
Hypertext is at the heart of the uniform interface
REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state.
Because the server is providing the URI for each of the links, you've got some freedom ot choose which resource "handles" which message.
One interesting way to resolve this to look at HTTP's rules for cache invalidation. The short version is that successful unsafe requests (PATCH/POST/PUT) invalidate the representations of the target-uri.
In other words, we take advantage of cache-invalidation by sending the command to the resource that we are trying to change.
So, assuming that retrieving the representation of the app occurred via a request like:
GET /applications/my-new-app HTTP/x.y
Then we would ask the server to change that resource by sending a request with that same target-uri. Something analogous to:
POST /applications/my-new-app HTTP/x.y
Content-Type: text/plain
Please rotate the keys
Form submissions on the web are usually a representation of key/value pairs, so a more likely spelling would be:
POST /applications/my-new-app HTTP/x.y
Content-Type: applications/x-www-form-urlencoded
action=Please%20rotate%20the%20keys
Your form that describes this request my have an "action" input control, that accepts text from the client, or more likely in this case action would be a hidden control with a pre-defined value.
Note: if we have multiple actions that should invalidate the /applications/my-new-app representations, we would probably use POST for all of them, and resolve the ambiguity at the server based on the request-body (if our routing framework gives us the degree of control we need, we can use that - but more common would be to have a single POST handler for each Content-Type, and parse the request body "by hand".
POST serves many useful purposes in HTTP, including the general purpose of “this action isn’t worth standardizing.” -- Fielding 2009
PUT /applications/my-new-app/update_keys is not noun-based and thus, not restful,
That's not true: REST doesn't care what spelling conventions you use for your resource identifiers. For example
https://www.merriam-webster.com/dictionary/get
https://www.merriam-webster.com/dictionary/post
https://www.merriam-webster.com/dictionary/put
https://www.merriam-webster.com/dictionary/update
These all work fine, just like every other resource on the web.
You absolutely can design your resource model so that editing the update_keys document also modifies the my-new-app document.
The potential difficulty is that general purpose components are not going to know what is going on. HTTP PUT means "update the representation of the target resource", and every general purpose component knows that; the origin server is allowed to modify other resources as a consequence of the changes to the "update-keys" resource.
But we don't have a great language for communicating the general purpose components all of the side effects that may have happened. Without some special magic, previously cached copies of my-new-app, with the original, unrotated, keys, will be left lying around. So the client may be left with a stale copy of the document that describes the app.
(An example of "some special magic" would be Linked Cache Invalidation, which affords describing caching relationships between resources using web linking. Unforunately, LCI has not been adopted as a standard, and you won't find the described link relations in the IANA registry.)

Related

REST API allow update of resource depending on state of resource

I have recently read the guide on implementing RESTful API's in Spring Boot from the official Spring.io tutorials website (link to tutorial: https://spring.io/guides/tutorials/rest/)
However, something in the guide seemed to contradict my understanding of how REST API's should be built. I am now wondering if my understanding is wrong or if the guide is not of as high a quality as I expected it to be.
My problem is with this implementation of a PUT method to update the status of an order:
#PutMapping("/orders/{id}/complete")
ResponseEntity<?> complete(#PathVariable Long id) {
Order order = orderRepository.findById(id) //
.orElseThrow(() -> new OrderNotFoundException(id));
if (order.getStatus() == Status.IN_PROGRESS) {
order.setStatus(Status.COMPLETED);
return ResponseEntity.ok(assembler.toModel(orderRepository.save(order)));
}
return ResponseEntity //
.status(HttpStatus.METHOD_NOT_ALLOWED) //
.header(HttpHeaders.CONTENT_TYPE, MediaTypes.HTTP_PROBLEM_DETAILS_JSON_VALUE) //
.body(Problem.create() //
.withTitle("Method not allowed") //
.withDetail("You can't complete an order that is in the " + order.getStatus() + " status"));
}
From what I read at https://restfulapi.net/rest-put-vs-post/ a PUT method should be idempotent; meaning that you should be able to call it multiple times in a row without it causing problems. However, in this implementation only the first PUT request would have an effect and all further PUT requests to the same resource would result in an error message.
Is this okay according to RESTful API's? If not, what would be a better method to use? I don't think POST would be any better.
Also, in the same guide, they use the DELETE method in a similar way to change the status of an order to cancelled:
#DeleteMapping("/orders/{id}/cancel")
ResponseEntity<?> cancel(#PathVariable Long id) {
Order order = orderRepository.findById(id) //
.orElseThrow(() -> new OrderNotFoundException(id));
if (order.getStatus() == Status.IN_PROGRESS) {
order.setStatus(Status.CANCELLED);
return ResponseEntity.ok(assembler.toModel(orderRepository.save(order)));
}
return ResponseEntity //
.status(HttpStatus.METHOD_NOT_ALLOWED) //
.header(HttpHeaders.CONTENT_TYPE, MediaTypes.HTTP_PROBLEM_DETAILS_JSON_VALUE) //
.body(Problem.create() //
.withTitle("Method not allowed") //
.withDetail("You can't cancel an order that is in the " + order.getStatus() + " status"));
}
This looks very wrong to me. We are not deleting anything here, it is basically the same as the previous PUT method just with a different state we want to move to. Am I correct to assume that this part of the tutorial is bogus?
TL;DR: what HTTP method is right to use when you want to advance the status of a resource to the next stage without giving an option of going back to an earlier stage? Basically an update/patch that will invalidate its own pre-conditions.
something in the guide seemed to contradict my understanding of how REST API's should be built. I am now wondering if my understanding is wrong or if the guide is not of as high a quality as I expected it to be.
I wouldn't consider this guide to be a reliable authority - the described resource model has some very questionable choices.
From what I read at https://restfulapi.net/rest-put-vs-post/ a PUT method should be idempotent; meaning that you should be able to call it multiple times in a row without it causing problems. However, in this implementation only the first PUT request would have an effect and all further PUT requests to the same resource would result in an error message.
The authoritative definition of idempotent semantics in HTTP is currently RFC 7231.
A request method is considered "idempotent" if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request.
Note: "effect", not "response".
PUT /orders/12345/complete
means "please replace the current representation of /orders/12345/complete with the representation in the payload". In other words "save this file on top of your current copy". Saving the same file two or three times in row produces the same effect as saving the file once, so that's "idempotent".
HTTP does not define exactly how a PUT method affects the state of an origin server beyond what can be expressed by the intent of the user agent request and the semantics of the origin server response. It does not define what a resource might be, in any sense of that word, beyond the interface provided via HTTP. It does not define how resource state is "stored", nor how such storage might change as a result of a change in resource state, nor how the origin server translates resource state into representations. Generally speaking, all implementation details behind the resource interface are intentionally hidden by the server. -- RFC 7231
So in their CURL example
PUT /orders/4/complete HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
The meaning of this message is "replace the current representation of /orders/4/complete with an empty representation". But the origin server gets to choose how to do that, and which standardized responses to return to the client.
So this is fine.
All work is transacted by politely placing documents in in-trays, and then some side effect of placing that document in an in-tray causes some business activity to occur -- Jim Webber, 2011.
In this case, the document we are putting into the "in-tray" happens to be blank.
#DeleteMapping("/orders/{id}/cancel")
I would never approve that choice in a code review. DELETE (like PUT) has semantics in the "transfer of documents over a network domain".
The DELETE method requests that the origin server remove the association between the target resource and its current functionality. In effect, this method is similar to the rm command in UNIX: it expresses a deletion operation on the URI mapping of the origin server rather than an expectation that the previously associated information be deleted.
Trying to hijack the method because the spelling is kind of like the domain action is the wrong heuristic to use in choosing methods.
Relatively few resources allow the DELETE method -- its primary use is for remote authoring environments, where the user has some direction regarding its effect.
The point being that we have a general purpose document manipulation interface, and we are using that interface as a facade that allows us to drive business activity. So we should be using our standardized message semantics the same way every other page on the web does.
#PutMapping would be defensible, using the same justification as we did for /complete.
what HTTP method is right to use when you want to advance the status of a resource to the next stage without giving an option of going back to an earlier stage? Basically an update/patch that will invalidate its own pre-conditions.
PUT, PATCH, and POST are all appropriate methods to use when editing the representation of a resource. Use PUT or PATCH when you are sending a replacement representation for the resource, use POST when you are asking the server to calculate what the edit to the representation should be.

which type of request is used for the `delete` button in the REST context?

I am creating a REST API for the Order screen. I have methods:
GET /api/orders
GET /api/orders/{orderId}
I have some buttons on the Order page and I created few endpoints for that:
PATCH /api/order/buttons/mark-as-read
PATCH /api/order/buttons/change-status
Now I need to add the delete button. But I don't understand how to do that. I have 2 options:
DELETE /api/orders/{orderId} - but I should send 2 additional parameters in this request
PATCH /api/order/buttons/delete - I can send my DTO in the body, but it is not a REST approach.
I want to understand which type of request is used for the delete button in the REST context?
PATCH /api/order/buttons/mark-as-read
PATCH /api/order/buttons/change-status
These are a bit strange. PATCH is a method with remote authoring semantics; it implies that you are making a change to the resource identified by the effective target URI.
But that doesn't seem to be the case here; if you are expecting to apply the changes to the document identified by /api/orders/{orderId}, then that should be the target URI, not some other resource.
PATCH /api/orders/1
Content-Type: text/plain
Please mark this order as read.
PATCH /api/orders/1
Content-Type: text/plain
Please change the status of this order to FULFILLED
Of course, we don't normally use "text/plain" and statements that require a human being to interpret, but instead use a patch document format (example: application/json-patch+json) that a machine can be taught to interpret.
I want to understand which type of request is used for the delete button in the REST context?
If the semantics of "delete" belong to the Orders domain (for instance, if it is a button that signals a desire to cancel an order) then you should be using PUT or PATCH (if you are communicating by passing updated representations of the resource) or POST (if you are sending instructions that the server will interpret).
The heuristic to consider: how would you do this on a plain HTML page? Presumably you would have a "cancel my order" form, with input controls to collect information from the user, and possibly some hidden fields. When the user submits the form, the browser would use the form data and HTML's form processing rules to create an application/x-www-form-urlencoded representation of the information, and would then POST that information to the resource identified by the form action.
The form action could be anything; you could use /api/orders/1/cancel, analogous to your mark-as-read and change-status design; but if you can use the identifier of the order (which is to say, the resource that you are changing), then you get the advantages of standardized cache invalidation for free.
It's normal for a single message handler, which has a single responsibility in the transfer of documents over a network domain, ex POST /api/orders/{orderId}, to interpret the payload and select one of multiple handlers (change-status, mark-as-read, cancel) in your domain.
you offer to use something like this: PATCH /api/orders/{orderId} and OrderUpdatesDto as JSON string in the request body?
Sort of.
There are three dials here: which effective request URI to use, which payload to use, which method to use.
Because I would want to take advantage of cache invalidation, I'm going to look for designs that use: /api/order/{orderId} as the effective request URI, because that's the URI for the responses that I want to invalidate.
It's fine to use something like a JSON representation of an OrderUpdate message/command/DTO as the payload of the request. But that's not really a good match for remote authoring. So instead of PATCH, I would use POST
POST /api/orders/1 HTTP/1.1
Content-Type: application/prs.pavel-orderupdate+json
{...}
But you can instead decide to support a remote authoring interface, meaning that the client just edits their local copy of /api/order/1 and then tells you what changes they made.
That's the case where both PUT (send back the entire document) and PATCH (send back a bunch of edits) can make sense. If GET /api/orders/1 returns a JSON document, then I'm going to look into whether or not I can support one of the general purpose JSON patch document formats; JSON Patch or JSON Merge Patch or something along those lines.
Of course, it can be really hard to get from "changes to a document" to a message that will be meaningful to a non-anemic domain. There are reasons that we might prefer supporting a task based experience, but sending a task centric DTO is not a good fit for PUT/PATCH if you also want caching to work the way I've described above.

Different response for same API but different method (GET and POST)

I think this is a classic and typical question but I didn't find its answer.
In my knowledge, the POST method is used to send data to the server with request parameter in message body to make it secure. And GET method is to retrieve data with parameters in the URL.
But what I didn't understand is how the same api may have different behavior by just changing the method.
Here is an example. I use SoapUI 5.5.0, this is the link of the api: https://reqres.in/api/users/1
when I use GET method I get this:
{
"data": {
"id": 1,
"email": "george.bluth#reqres.in",
"first_name": "George",
"last_name": "Bluth",
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
}
}
and by changing only the method to POST, I get this:
{
"id": "244",
"createdAt": "2020-02-27T14:30:32.100Z"
}
(the id and date changes each time)
as described in this link https://reqres.in/ that it is creating an instance and we can add parameters..
BUT, can any one explain how is it technically possible to have different behavior with different methods on the same URL.
In my knowledge, the POST method is used to send data to the server with request parameter in message body to make it secure. And GET method is to retrieve data with parameters in the URL.
That's probably getting in your way.
HTTP Requests are messages; each message starts with a request-line
method SP request-target SP HTTP-version CRLF
The request-target identifies the target resource upon which to apply the request
The method token indicates the request method to be performed on the target resource.
You can think of it being like a function call
GET(target-resource)
POST(target-resource, message-body)
Or equivalently you can think of the resources as objects that share an understanding of message semantics
target-resource.GET()
target-resource.POST(message-body)
But what I didn't understand is how the same api may have different behavior by just changing the method.
The same way that an api can exhibit different behavior by just changing the request-target.
In HTTP, the request-line is literally human readable text that the server will parse. Having parsed the request-line, the server program can then branch to whatever code it wants to use to do the work, based on the values it found in the message.
In many frameworks (Spring, Rails) the branching logic is provided by the framework code; your bespoke handlers only need to be correctly registered and the framework ensures that each request is forwarded to the correct handler.
how is it technically possible to have different behavior with
different methods on the same URL
for the technical possibility, you can look at the spring framework's answer to this.
You can have a controller that is accessible on a single url but can contacted in four says, GET, PUT, POST, DELETE. To do this, Spring provides the annotations #GetMapping, #PostMapping, #PutMapping, #DeleteMapping.
All the requests are sent to the same url and Spring works out which method to call based on the verb.
In Restful APIs, verbs have very important meaning.
GET: Retrieve data
POST: Create a new entity with the request's body
PUT: Replace an entity with the request's body
PATCH: Update some properties of an entity with the request's body. A.K.A. Partial update
In your case, changing the verb from get to post has the effect of creating a new entity with ID 1. That's why you get a response with the newly created ID and a createdAt timestamp.

REST endpoint: how to proper design an action on a resource?

I have the resource /contracts with the following structure:
{
name: "Contract name",
signedAt: 123213231,
// etc.
}
While basic CRUD operations are well clear (GET /contracts, GET /contracts/{id}, POST /contracts, etc.) some doubts come when I have to do some concrete actions on the resource.
One of these actions is the following:
sign: means the contract is signed, so the signedAt date will need to be updated with the moment (date-time) the contract was signed.
So far I've been thinking about these different approaches:
PATCH-ing the resource
This approach will mean having the following endpoint method:
PATCH /contracts/{id}
and just posting the signedAt date { signedAt: 123213231 } meaning that after this the contract will be signed.
What I don't like about this approach is that the signature date comes from the client, I was thinking that having this date initialized on the backend side whenever a contract is signed could be better and more consistent.
Totally discarded, as the signedAt date should be set on the server
side exactly at the moment the sign is done.
POST-ing a new resource
This approach will mean having the signature action as a resource:
POST /contracts/{id}/sign
with an empty body in this case as we don't need to pass anything else so, once it is posted, the backend side would be the responsible for having the signature date initialized.
POST-ing the resource using 'action'
Similar to the previous approach, in this case I would use a query parameter called action on the contract resource:
POST /contracts/{idContract}?action=sign
also with an empty body where ?action=sign. Like in the previous approach, once posted the backend side would be the responsible for having the signature date initialized.
Questions
What would be the proper way to have this designed at a REST API level?
Is any of the approaches above close to a good design or not?
Would I need to modify any of the approaches?
Is there any better alternative?
I have designed a few rest APIs myself but I am not a restful evangelist so my answer might not be the best. I would suggest some of the following:
Create a custom converter for date values in your rest service that accepts date AND other specific fields. If you checkGoogle reporting APIs for example they allow you to use specific date range and also CURRENT_WEEK, CURRENT_MONTH etc. So you can add such specific value and use it. For example PATCH signedAt=CURRENT_DATE and the API handles that properly.
Add a boolean signed field to the resource. Do a POST or PATCH with signed=true. This way you will be able to eventually query only signed resources easily ;) Also it might be the case that people care only about if it is signed than when it was signed
I wouldn't use ?action=sign or /contracts/{id}/sign because these are not RESTFUL and even if you do use GET and POST you would use them in a way to create a workaround in order to implement actions in your design which shouldn't have actions
just posting the signedAt date { signedAt: 123213231 } meaning that after this the contract will be signed.
On HTTP Patch
The set of changes is represented in a format called a "patch document" identified by a media type.
Rather than rolling your own bespoke media type, you might want to consider whether one of the standard formats is suitable.
For example: JSON Patch.
Content-Type: application/json-patch+json
[ { "op": "replace", "path": "signedAt", "value": 123213231 }
JSON Merge Patch is another reasonable option
Content-Type: application/merge-patch+json
{ signedAt: 123213231 }
From what I can see, the primary difference is that JSON Patch provides a test operation which gives you finer grain control than simply relying upon validators
But you are absolutely right - PATCH gives the client code authority to specify the time value. If that's not appropriate for your use case, then PATCH is the wrong tool in the box.
POST /contracts/{id}/sign
POST /contracts/{idContract}?action=sign
As far as REST/HTTP are concerned, these two choices are equivalent -- you are updating the state of one resource by sending an unsafe request to a different resource. There are some mild differences in how these spellings act when resolving references, but as request-targets, it doesn't make a difference to the client.
An option that you seem to have overlooked:
POST /contracts/{id}
action=sign
This has the advantage that, when successful, you get cache invalidation for free.
In a hypermedia API, the flow might go something like this: the client would GET the resource; because the resource hasn't been signed yet, the representation could include a form, with a "sign" button on it. The action on the form would be /contracts/{id}. The consumer "signs" the contract by submitting the form -- the agent gathers up the information described by the form, encodes it into the request body, and then posts the request to the server. The server responds success, and the client's cache knows to invalidate the previously fetched copy of the resource.

HTTP GET with request body

I'm developing a new RESTful webservice for our application.
When doing a GET on certain entities, clients can request the contents of the entity.
If they want to add some parameters (for example sorting a list) they can add these parameters in the query string.
Alternatively I want people to be able to specify these parameters in the request body.
HTTP/1.1 does not seem to explicitly forbid this. This will allow them to specify more information, might make it easier to specify complex XML requests.
My questions:
Is this a good idea altogether?
Will HTTP clients have issues with using request bodies within a GET request?
https://www.rfc-editor.org/rfc/rfc2616
Roy Fielding's comment about including a body with a GET request.
Yes. In other words, any HTTP request message is allowed to contain a message body, and thus must parse messages with that in mind. Server semantics for GET, however, are restricted such that a body, if any, has no semantic meaning to the request. The requirements on parsing are separate from the requirements on method semantics.
So, yes, you can send a body with GET, and no, it is never useful to do so.
This is part of the layered design of HTTP/1.1 that will become clear again once the spec is partitioned (work in progress).
....Roy
Yes, you can send a request body with GET but it should not have any meaning. If you give it meaning by parsing it on the server and changing your response based on its contents, then you are ignoring this recommendation in the HTTP/1.1 spec, section 4.3:
...if the request method does not include defined semantics for an entity-body, then the message-body SHOULD be ignored when handling the request.
And the description of the GET method in the HTTP/1.1 spec, section 9.3:
The GET method means retrieve whatever information ([...]) is identified by the Request-URI.
which states that the request-body is not part of the identification of the resource in a GET request, only the request URI.
Update
The RFC2616 referenced as "HTTP/1.1 spec" is now obsolete. In 2014 it was replaced by RFCs 7230-7237. Quote "the message-body SHOULD be ignored when handling the request" has been deleted. It's now just "Request message framing is independent of method semantics, even if the method doesn't define any use for a message body" The 2nd quote "The GET method means retrieve whatever information ... is identified by the Request-URI" was deleted. - From a comment
From the HTTP 1.1 2014 Spec:
A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.
While you can do that, insofar as it isn't explicitly precluded by the HTTP specification, I would suggest avoiding it simply because people don't expect things to work that way. There are many phases in an HTTP request chain and while they "mostly" conform to the HTTP spec, the only thing you're assured is that they will behave as traditionally used by web browsers. (I'm thinking of things like transparent proxies, accelerators, A/V toolkits, etc.)
This is the spirit behind the Robustness Principle roughly "be liberal in what you accept, and conservative in what you send", you don't want to push the boundaries of a specification without good reason.
However, if you have a good reason, go for it.
You will likely encounter problems if you ever try to take advantage of caching. Proxies are not going to look in the GET body to see if the parameters have an impact on the response.
Elasticsearch accepts GET requests with a body. It even seems that this is the preferred way: Elasticsearch guide
Some client libraries (like the Ruby driver) can log the cry command to stdout in development mode and it is using this syntax extensively.
Neither restclient nor REST console support this but curl does.
The HTTP specification says in section 4.3
A message-body MUST NOT be included in a request if the specification of the request method (section 5.1.1) does not allow sending an entity-body in requests.
Section 5.1.1 redirects us to section 9.x for the various methods. None of them explicitly prohibit the inclusion of a message body. However...
Section 5.2 says
The exact resource identified by an Internet request is determined by examining both the Request-URI and the Host header field.
and Section 9.3 says
The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI.
Which together suggest that when processing a GET request, a server is not required to examine anything other that the Request-URI and Host header field.
In summary, the HTTP spec doesn't prevent you from sending a message-body with GET but there is sufficient ambiguity that it wouldn't surprise me if it was not supported by all servers.
GET, with a body!?
Specification-wise you could, but, it's not a good idea to do so injudiciously, as we shall see.
RFC 7231 §4.3.1 states that a body "has no defined semantics", but that's not to say it is forbidden. If you attach a body to the request and what your server/app makes out of it is up to you. The RFC goes on to state that GET can be "a programmatic view on various database records". Obviously such view is many times tailored by a large number of input parameters, which are not always convenient or even safe to put in the query component of the request-target.
The good: I like the verbiage. It's clear that one read/get a resource without any observable side-effects on the server (the method is "safe"), and, the request can be repeated with the same intended effect regardless of the outcome of the first request (the method is "idempotent").
The bad: An early draft of HTTP/1.1 forbade GET to have a body, and - allegedly - some implementations will even up until today drop the body, ignore the body or reject the message. For example, a dumb HTTP cache may construct a cache key out of the request-target only, being oblivious to the presence or content of a body. An even dumber server could be so ignorant that it treats the body as a new request, which effectively is called "request smuggling" (which is the act of sending "a request to one device without the other device being aware of it" - source).
Due to what I believe is primarily a concern with inoperability amongst implementations, work in progress suggests to categorize a GET body as a "SHOULD NOT", "unless [the request] is made directly to an origin server that has previously indicated, in or out of band, that such a request has a purpose and will be adequately supported" (emphasis mine).
The fix: There's a few hacks that can be employed for some of the problems with this approach. For example, body-unaware caches can indirectly become body-aware simply by appending a hash derived from the body to the query component, or disable caching altogether by responding a cache-control: no-cache header from the server.
Alas when it comes to the request chain, one is often not in control of- or even aware, of all present and future HTTP intermediaries and how they will deal with a GET body. That's why this approach must be considered generally unreliable.
But POST, is not idempotent!
POST is an alternative. The POST request usually includes a message body (just for the record, body is not a requirement, see RFC 7230 §3.3.2). The very first use case example from RFC 7231 (§4.3.3) is "providing a block of data [...] to a data-handling process". So just like GET with a body, what happens with the body on the back-end side is up to you.
The good: Perhaps a more common method to apply when one wish to send a request body, for whatever purpose, and so, will likely yield the least amount of noise from your team members (some may still falsely believe that POST must create a resource).
Also, what we often pass parameters to is a search function operating upon constantly evolving data, and a POST response is only cacheable if explicit freshness information is provided in the response.
The bad: POST requests are not defined as idempotent, leading to request retry hesitancy. For example, on page reload, browsers are unwilling to resubmit an HTML form without prompting the user with a nonreadable cryptic message.
The fix: Well, just because POST is not defined to be idempotent doesn't mean it mustn't be. Indeed, RFC 7230 §6.3.1 writes: "a user agent that knows (through design or configuration) that a POST request to a given resource is safe can repeat that request automatically". So, unless your client is an HTML form, this is probably not a real problem.
QUERY is the holy grail
There's a proposal for a new method QUERY which does define semantics for a message body and defines the method as idempotent. See this.
Edit: As a side-note, I stumbled into this StackOverflow question after having discovered a codebase where they solely used PUT requests for server-side search functions. This were their idea to include a body with parameters and also be idempotent. Alas the problem with PUT is that the request body has very precise semantics. Specifically, the PUT "requests that the state of the target resource be created or replaced with the state [in the body]" (RFC 7231 §4.3.4). Clearly, this excludes PUT as a viable option.
You can either send a GET with a body or send a POST and give up RESTish religiosity (it's not so bad, 5 years ago there was only one member of that faith -- his comments linked above).
Neither are great decisions, but sending a GET body may prevent problems for some clients -- and some servers.
Doing a POST might have obstacles with some RESTish frameworks.
Julian Reschke suggested above using a non-standard HTTP header like "SEARCH" which could be an elegant solution, except that it's even less likely to be supported.
It might be most productive to list clients that can and cannot do each of the above.
Clients that cannot send a GET with body (that I know of):
XmlHTTPRequest Fiddler
Clients that can send a GET with body:
most browsers
Servers & libraries that can retrieve a body from GET:
Apache
PHP
Servers (and proxies) that strip a body from GET:
?
What you're trying to achieve has been done for a long time with a much more common method, and one that doesn't rely on using a payload with GET.
You can simply build your specific search mediatype, or if you want to be more RESTful, use something like OpenSearch, and POST the request to the URI the server instructed, say /search. The server can then generate the search result or build the final URI and redirect using a 303.
This has the advantage of following the traditional PRG method, helps cache intermediaries cache the results, etc.
That said, URIs are encoded anyway for anything that is not ASCII, and so are application/x-www-form-urlencoded and multipart/form-data. I'd recommend using this rather than creating yet another custom json format if your intention is to support ReSTful scenarios.
I put this question to the IETF HTTP WG. The comment from Roy Fielding (author of http/1.1 document in 1998) was that
"... an implementation would be broken to do anything other than to parse and discard that body if received"
RFC 7213 (HTTPbis) states:
"A payload within a GET request message has no defined semantics;"
It seems clear now that the intention was that semantic meaning on GET request bodies is prohibited, which means that the request body can't be used to affect the result.
There are proxies out there that will definitely break your request in various ways if you include a body on GET.
So in summary, don't do it.
From RFC 2616, section 4.3, "Message Body":
A server SHOULD read and forward a message-body on any request; if the
request method does not include defined semantics for an entity-body,
then the message-body SHOULD be ignored when handling the request.
That is, servers should always read any provided request body from the network (check Content-Length or read a chunked body, etc). Also, proxies should forward any such request body they receive. Then, if the RFC defines semantics for the body for the given method, the server can actually use the request body in generating a response. However, if the RFC does not define semantics for the body, then the server should ignore it.
This is in line with the quote from Fielding above.
Section 9.3, "GET", describes the semantics of the GET method, and doesn't mention request bodies. Therefore, a server should ignore any request body it receives on a GET request.
Which server will ignore it? – fijiaaron Aug 30 '12 at 21:27
Google for instance is doing worse than ignoring it, it will consider it an error!
Try it yourself with a simple netcat:
$ netcat www.google.com 80
GET / HTTP/1.1
Host: www.google.com
Content-length: 6
1234
(the 1234 content is followed by CR-LF, so that is a total of 6 bytes)
and you will get:
HTTP/1.1 400 Bad Request
Server: GFE/2.0
(....)
Error 400 (Bad Request)
400. That’s an error.
Your client has issued a malformed or illegal request. That’s all we know.
You do also get 400 Bad Request from Bing, Apple, etc... which are served by AkamaiGhost.
So I wouldn't advise using GET requests with a body entity.
According to XMLHttpRequest, it's not valid. From the standard:
4.5.6 The send() method
client . send([body = null])
Initiates the request. The optional argument provides the request
body. The argument is ignored if request method is GET or HEAD.
Throws an InvalidStateError exception if either state is not
opened or the send() flag is set.
The send(body) method must run these steps:
If state is not opened, throw an InvalidStateError exception.
If the send() flag is set, throw an InvalidStateError exception.
If the request method is GET or HEAD, set body to null.
If body is null, go to the next step.
Although, I don't think it should because GET request might need big body content.
So, if you rely on XMLHttpRequest of a browser, it's likely it won't work.
If you really want to send cachable JSON/XML body to web application the only reasonable place to put your data is query string encoded with RFC4648: Base 64 Encoding with URL and Filename Safe Alphabet. Of course you could just urlencode JSON and put is in URL param's value, but Base64 gives smaller result. Keep in mind that there are URL size restrictions, see What is the maximum length of a URL in different browsers? .
You may think that Base64's padding = character may be bad for URL's param value, however it seems not - see this discussion: http://mail.python.org/pipermail/python-bugs-list/2007-February/037195.html . However you shouldn't put encoded data without param name because encoded string with padding will be interpreted as param key with empty value.
I would use something like ?_b64=<encodeddata>.
I wouldn't advise this, it goes against standard practices, and doesn't offer that much in return. You want to keep the body for content, not options.
You have a list of options which are far better than using a request body with GET.
Let' assume you have categories and items for each category. Both to be identified by an id ("catid" / "itemid" for the sake of this example). You want to sort according to another parameter "sortby" in a specific "order". You want to pass parameters for "sortby" and "order":
You can:
Use query strings, e.g.
example.com/category/{catid}/item/{itemid}?sortby=itemname&order=asc
Use mod_rewrite (or similar) for paths:
example.com/category/{catid}/item/{itemid}/{sortby}/{order}
Use individual HTTP headers you pass with the request
Use a different method, e.g. POST, to retrieve a resource.
All have their downsides, but are far better than using a GET with a body.
What about nonconforming base64 encoded headers? "SOMETHINGAPP-PARAMS:sdfSD45fdg45/aS"
Length restrictions hm. Can't you make your POST handling distinguish between the meanings? If you want simple parameters like sorting, I don't see why this would be a problem. I guess it's certainty you're worried about.
I'm upset that REST as protocol doesn't support OOP and Get method is proof. As a solution, you can serialize your a DTO to JSON and then create a query string. On server side you'll able to deserialize the query string to the DTO.
Take a look on:
Message-based design in ServiceStack
Building RESTful Message Based Web Services with WCF
Message based approach can help you to solve Get method restriction. You'll able to send any DTO as with request body
Nelibur web service framework provides functionality which you can use
var client = new JsonServiceClient(Settings.Default.ServiceAddress);
var request = new GetClientRequest
{
Id = new Guid("2217239b0e-b35b-4d32-95c7-5db43e2bd573")
};
var response = client.Get<GetClientRequest, ClientResponse>(request);
as you can see, the GetClientRequest was encoded to the following query string
http://localhost/clients/GetWithResponse?type=GetClientRequest&data=%7B%22Id%22:%2217239b0e-b35b-4d32-95c7-5db43e2bd573%22%7D
IMHO you could just send the JSON encoded (ie. encodeURIComponent) in the URL, this way you do not violate the HTTP specs and get your JSON to the server.
For example, it works with Curl, Apache and PHP.
PHP file:
<?php
echo $_SERVER['REQUEST_METHOD'] . PHP_EOL;
echo file_get_contents('php://input') . PHP_EOL;
Console command:
$ curl -X GET -H "Content-Type: application/json" -d '{"the": "body"}' 'http://localhost/test/get.php'
Output:
GET
{"the": "body"}
Even if a popular tool use this, as cited frequently on this page, I think it is still quite a bad idea, being too exotic, despite not forbidden by the spec.
Many intermediate infrastructures may just reject such requests.
By example, forget about using some of the available CDN in front of your web site, like this one:
If a viewer GET request includes a body, CloudFront returns an HTTP status code 403 (Forbidden) to the viewer.
And yes, your client libraries may also not support emitting such requests, as reported in this comment.
If you want to allow a GET request with a body, a way is to support POST request with header "X-HTTP-Method-Override: GET". It is described here : https://en.wikipedia.org/wiki/List_of_HTTP_header_fields. This header means that while the method is POST, the request should be treated as if it is a GET. Body is allowed for POST, so you're sure nobody willl drop the payload of your GET requests.
This header is oftenly used to make PATCH or HEAD requests through some proxies that do not recognize those methods and replace them by GET (always fun to debug!).
An idea on an old question:
Add the full content on the body, and a short hash of the body on the querystring, so caching won't be a problem (the hash will change if body content is changed) and you'll be able to send tons of data when needed :)
Create a Requestfactory class
import java.net.URI;
import javax.annotation.PostConstruct;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
#Component
public class RequestFactory {
private RestTemplate restTemplate = new RestTemplate();
#PostConstruct
public void init() {
this.restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestWithBodyFactory());
}
private static final class HttpComponentsClientHttpRequestWithBodyFactory extends HttpComponentsClientHttpRequestFactory {
#Override
protected HttpUriRequest createHttpUriRequest(HttpMethod httpMethod, URI uri) {
if (httpMethod == HttpMethod.GET) {
return new HttpGetRequestWithEntity(uri);
}
return super.createHttpUriRequest(httpMethod, uri);
}
}
private static final class HttpGetRequestWithEntity extends HttpEntityEnclosingRequestBase {
public HttpGetRequestWithEntity(final URI uri) {
super.setURI(uri);
}
#Override
public String getMethod() {
return HttpMethod.GET.name();
}
}
public RestTemplate getRestTemplate() {
return restTemplate;
}
}
and #Autowired where ever you require and use, Here is one sample code GET request with RequestBody
#RestController
#RequestMapping("/v1/API")
public class APIServiceController {
#Autowired
private RequestFactory requestFactory;
#RequestMapping(method = RequestMethod.GET, path = "/getData")
public ResponseEntity<APIResponse> getLicenses(#RequestBody APIRequest2 APIRequest){
APIResponse response = new APIResponse();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Gson gson = new Gson();
try {
StringBuilder createPartUrl = new StringBuilder(PART_URL).append(PART_URL2);
HttpEntity<String> entity = new HttpEntity<String>(gson.toJson(APIRequest),headers);
ResponseEntity<APIResponse> storeViewResponse = requestFactory.getRestTemplate().exchange(createPartUrl.toString(), HttpMethod.GET, entity, APIResponse.class); //.getForObject(createLicenseUrl.toString(), APIResponse.class, entity);
if(storeViewResponse.hasBody()) {
response = storeViewResponse.getBody();
}
return new ResponseEntity<APIResponse>(response, HttpStatus.OK);
}catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<APIResponse>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}