Using HTTP 304 in response to POST - http

I have a REST API that allows modification of resources using HTTP POST. It's possible that a client may submit a POST request that results in no modification of the resource. I'm thinking about using the 304 response generally used for conditional responses to indicate that the request had no effect. I haven't been able to find any examples of this being done, so I figured I'd ask here and see if anyone else is doing this or has an opinion about it.

After some consideration, I've decided to stick to a normal 200 response with the unchanged resource entity. My initial intent was to provide a concise way to indicate to the client that the resource was not modified. As I thought more about it I realized that in order to do anything useful with the 304 response, they would have to already have a cached version and in that case it would be trivial to compare the version of the cached copy with the version returned in a 200 response.

I have a REST API that allows modification of resources using HTTP POST. It's possible that a client may submit a POST request that results in no modification of the resource.
HTTP POST in the RESTful approach implies a creation of a resource, not a modification. For modification you should use HTTP PUT.
Solution of your problem is HTTP Status 200 OK when something was modified and HTTP Status 204 No Content when there was no modification. According to:
The common use case is to return 204 as a result of a PUT request, updating a resource, without changing the current content of the page displayed to the user. If the resource is created, 201 Created is returned instead. If the page should be changed to the newly updated page, the 200 should be used instead.
-- MDN web docs
For example:
-- Request
POST /people HTTP/1.1
Content-Type: application/json
{
"name": "John"
}
-- Response
HTTP/1.1 201 Created
Location: /people/1
-- Request
PUT /people/1 HTTP/1.1
Content-Type: application/json
{
"name": "John"
}
-- Response
HTTP/1.1 204 No Content
-- Request
PUT /people/1 HTTP/1.1
Content-Type: application/json
{
"name": "Robert"
}
-- Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"name": "Robert"
}

Related

Should a 304 Not Modified response to a CORS request contain CORS headers?

Say that a client has made a cross-origin request to read an object, which passed, and the client cached the results. Now, the client makes a new request to read the object, and the previous result is still cached in the browser.
The client makes this request:
GET /pony.png HTTP/1.1
Host: server.com
Origin: field.com
If-None-Match: "etag-abcd"
Now, say that "etag-abcd" is valid for the object. The server replies
HTTP/1.1 304 Not Modified
Etag: "etag-abcd"
Date: Tue, ...
Expires: Wed, ...
If this is a valid cross-origin request, is the server obliged to provide appropriate Access-Control-Allow-Origin and other headers? Or, is the client obliged to respect the CORS headers that would have come along with the original, cached result?
The Fetch standard is quite complex, but it contains phrases like "As the CORS check is not to be applied to responses whose status is 304 or 407...." that make me suspect fresh CORS headers would not be necessary in a 304 response.
On the other hand, my reading of §4.6, step 10.4, suggests that the headers in the 304 response take precedence and even replace any cached result in the headers of the original GET response.
Yeah, HTTP-network-or-cache fetch does indeed handle the 304 response, but it ends up returning the updated stored response, upon which the CORS check is performed in HTTP fetch. So for a typical 304 no CORS headers would be needed as they would already be present on the stored response.
There are some edge cases:
The server could return a 304 response even when the client does not perform a validation request. In that case it would have to have CORS headers as the CORS check would be performed on the 304 response (and not a stored response updated with the 304 response).
The 304 response could update the CORS headers. (I'm not sure we have adequate test coverage for this scenario.)

Google Calendar API batch inserting has stopped working

I have an application that has been successfully using HTTP batch requests to insert, edit, and delete events via the Google Calendar API. In the last couple of days, the individual requests within the batches have started returning 404 errors (although the batch itself gets a 200 success response). Making those same requests as individual requests using the same authorization header is still working.
I'm pretty sure that this isn't related to the forthcoming shutdown of Google's global HTTP batch endpoints because we're using https://www.googleapis.com/batch/calendar/v3 as our endpoint.
Here's an example of what I'm trying to do:
https://www.googleapis.com/batch/calendar/v3
Authorization: Bearer your_auth_token
Content-Type: multipart/mixed; boundary=batch_google_calendar
--batch_google_calendar
Content-Type: application/http
Content-ID: <item-0-batchevent#example.com>
POST calendar/v3/calendars/your_calendar_id#group.calendar.google.com/events
Content-Type: application/json
{"summary":"batch API test","start":{"date":"2020-07-31"},"end":{"date":"2020-07-31"}}
--batch_google_calendar--
And the response is:
--batch_3J6sfuPtVQbjZLcpUe06245gKlO31YnC
Content-Type: application/http
Content-ID: <response-item-0-batchevent#example.com>
HTTP/1.1 404 Not Found
Vary: Origin
Vary: X-Origin
Vary: Referer
Content-Type: application/json; charset=UTF-8
[{
"error": {
"code": 404,
"message": "URL path: /v3/calendars/your_calendar_id#group.calendar.google.com/events could not be resolved. Maybe there is an error parsing the batch item.",
"status": "NOT_FOUND"
}
}
]
--batch_3J6sfuPtVQbjZLcpUe06245gKlO31YnC--
And here's an example of an individual request that's working:
https://www.googleapis.com/calendar/v3/calendars/your_calendar_id#group.calendar.google.com/events
Authorization: Bearer your_auth_token
Content-Type: application/json
{"summary":"API test","start":{"date":"2020-07-31"},"end":{"date":"2020-07-31"}}
Why might the individual request be succeeding but the batch request fail?
Google gave a helpful reply via their issue tracker: there was an error in the way that batch entry paths were specific in my application. This had worked without errors until last week, so I think something must have changed at their end to make it less tolerant of mistakes.
The error we had made was omitting the leading slash in the path in each batch entry. Here's what we were doing:
POST calendar/v3/calendars/your_calendar_id#group.calendar.google.com/events
And here's what we should have been doing:
POST /calendar/v3/calendars/your_calendar_id#group.calendar.google.com/events
I hope that this might be helpful to anyone else who ever finds themselves in a similar situation!

Sending HTTP requests with AT commands on the ESP32

I'm trying to send AT commands to my ESP32* module and am not getting any response back.
I need to perform a POST request that contains the username and password and other requests later on. I am not structuring these correctly and there is not a lot of good documentation for this.
NOTE: because I cannot share my complete url due to privacy I will use something with the same length ********connected.com:443
Send login information to ********connected.com/login (POST) body{"email":"myemail.ca", "password":"xxxxx"}
once I get the token I will make other requests.
get information regarding user profile ********connected.com/getRoutine ( GET) query param username="bob"
I really want to understand how these requests are structured so if someone can explain it to me elegantly that would be great!
Here is what I have tried..
AT
OK
AT+CIPSTART="TCP","********connected.com",443
CONNECT
OK
AT+CIPSEND=48
> "GET ********connected.com:443/getUsersOnline"
OK
>
Recv 48 bytes
SEND OK
CLOSED
REQUESTED POST REQUEST I HAVE USED
AT+CIPSEND=177 “POST \r Host: ********connected.com\r\n Accept: application/json\r\n Content-Length: 224r\n Content-Type: application/jsonr\n { "email":"myemail.com", "password":"myPassword" } “
There are actually several parts of your system that might be the cause of the malfunctioning:
The AT commands sent (it is not clear how you check for server responses. Responses could proviede clues about what's wrong)
The server side app seems to be a custom implementation that might have bugs as well
The POST request might be malformed
Let's focus on the last one.
POST are described in RFC 7231, and though it is an obscure description without examples, it makes one thing clear: there's not actually a well defined standard... because it is strictly application dependant!
I also quote the relevant part of this brilliant answer to an old SO question:
When receiving a POST request, you should always expect a "payload", or, in HTTP terms: a message body. The message body in itself is pretty useless, as there is no standard.
For this reason, all we can do is to build a POST request as accurate as possible and then to debug the system as a whole thing making sure that the request matches what expected by the server side application.
In order to do this, let's check another external link I found: POST request examples. We found this request:
POST /test HTTP/1.1
Host: foo.example
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
field1=value1&field2=value2
Now let's compare this example to your request:
POST
Host: ********connected.com
Accept: application/json
Content-Length: 224
Content-Type: application/jsonr
{ "email":"myemail.com", "password":"myPassword" }
You are saying to the server that you want to pass a resource to an unspecified application (no path), and that this resource is 224 bytes long (wrong! Message body is shorter).
For these reasons, at least these things can be improved:
POST /path/invic18app.php HTTP/1.1 //The path to the application and HTTP version are missing
Content-Length: 48 //This must be the length of the message body, without the header. I also would write it as the last option, just before message body
Write TWO empty lines before message body, otherwise the server will interpret it as further (wrong) options
I hope this helps you, even if it is a tentative answer (I cannot try this request myself). But, again, you definitely need to sniff packets a TCP levels, in order to avoid debugging the server if you are not sure that data is actually received! If you cannot install Wireshark, also tcpdump will be ok.

How to Construct a POST Request Which Expects No Body

I've an HTTP client sending many POST requests to a server. The server responds to all requests with 201 Created and a response body. For my purposes, the response header is enough, as I'm only interested in the Location header. I'd like to avoid that the server produces a response body in order to significantly decrease network traffic.
According to RFC 7231, ...
[...] if one or more resources has been created on the origin server as a
result of successfully processing a POST request, the origin server
SHOULD send a 201 (Created) response containing a Location header [...]
..., thus, I assume, the server COULD also respond e.g. with 204 No Content, omiting the body.
Therefore my question: Is it possible to construct a POST request which makes the server respond with 204 No Content or to omit the response body in another way?
Update 1: The server side is a Spring Data REST project and I'm free to configure it. I know that I could set RepositoryRestConfiguration#setReturnBodyOnCreate to false, but that would be overdone as it affects all incoming requests. Therefore, I'd prefer to make the decision on the client side.
There's no real lever you can pull from the client side to control if the server will respond with a body or not, unless the service you work with has a specific feature that allows this.
A header that a server might use is Prefer: return=minimal but if the service doesn't explicitly document support for this, chances are low that this will work.
Really the only think you can do one the client is to:
Kill the TCP connection as soon as you got the response headers
Kill the HTTP/2 stream when you recieved the headers.
This is a pretty 'drastic' thing but clients do use this mechanism for some cases and it does work. However, if the POST response body was somewhat small there's a chance that it's not really making a ton of difference because the response might already have been sent.
There is no way to do it client side only as it is not natively implemented in Spring REST server.
Anyway, any client demand can be transformed as an extra custom header or a query parameter in the request.
A way could be to override default response handlers and detect custom header (implements Prefer: return=minimal as suggested before for instance) and/or query param presence to trigger an empty response with a 204 status. This post may help you to figure it out.
Can you try changing your client such that you
a) Query the server with HTTP HEAD requests instead of POST requests
b) Analyze the response headers. There is no response body for HEAD requests as the purpose of HEAD requests is very similar to your requirement
c) Perform the necessary POST requests only when required
I understand that you may have difficulties at the client end to apply these changes. But, in the longer run, I believe this would be worth it.
Based on Evert's and Bertrand's answers plus a bit of googling, I finally implemented the following interceptor in the Spring Data REST server:
#Configuration
class RepositoryConfiguration {
#Bean
public MappedInterceptor preferReturnMinimalMappedInterceptor() {
return new MappedInterceptor(new String[]{"/**"}, new HandlerInterceptor() {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if ("return=minimal".equals(request.getHeader("prefer"))) {
response.setContentLength(0);
response.addHeader("Preference-Applied", "return=minimal"");
}
return true;
}
});
}
}
It produces the following communication, which is good enough for my purposes:
> POST /versions HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.59.0
> Accept: */*
> Content-Type: application/json
> Prefer: return=minimal
> Content-Length: 123
>
> [123 bytes data]
...
< HTTP/1.1 201
< Preference-Applied: return=minimal
< ETag: "0"
< Last-Modified: Fri, 30 Nov 2018 12:37:57 GMT
< Location: http://localhost:8080/versions/1
< Content-Type: application/hal+json;charset=UTF-8
< Content-Length: 0
< Date: Fri, 30 Nov 2018 12:37:57 GMT
I would like to share the bounty evenly, but this is not possible. It goes to Bertrand, as he came with an answer which guided me to the very implementation. Thanks for your help.

How to cache an HTTP POST response?

I would like to create a cacheable HTTP response for a POST request.
My actual implementation responds the following for the POST request:
HTTP/1.1 201 Created
Expires: Sat, 03 Oct 2020 15:33:00 GMT
Cache-Control: private,max-age=315360000,no-transform
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 9
ETag: 2120507660800737950
Last-Modified: Wed, 06 Oct 2010 15:33:00 GMT
.........
But it looks like that the browsers (Safari, Firefox tested) are not caching the response.
In the HTTP RFC the corresponding part says:
Responses to this method are not cacheable unless the response includes appropriate Cache-Control or Expires header fields. However, the 303 (See Other) response can be used to direct the user agent to retrieve a cacheable resource.
So I think it should be cached. I know I could set a session variable and set a cookie and do a 303 redirect, but I want to cache the response of the POST request.
Is there any way to do this?
P.S.: I've started with a simple 200 OK, so it does not work.
I'd also note that caching is always optional (it's a MAY in the HTTP/1.1 RFC). Since under most circumstances, a successful POST invalidates a cache entry, it's probably simply the case that the browser caches you're looking at just don't implement caching POST responses (since this would be pretty uncommon--usually this is accomplished by formatting things as a GET, which it sounds like you've done).
Short answer: POST caching rarely makes sense. A cache may serve GET requests to a URL which is the same as that of a previous POST, whose response came with a Content-Location header containing the POST's request URI.
From rfc-7231 (http-bis, superseding rfc-2616):
Responses to POST requests are only cacheable when they include
explicit freshness information (see Section 4.2.1 of [RFC7234]).
However, POST caching is not widely implemented. For cases where an
origin server wishes the client to be able to cache the result of a
POST in a way that can be reused by a later GET, the origin server
MAY send a 200 (OK) response containing the result and a
Content-Location header field that has the same value as the POST's
effective request URI (Section 3.1.4.2).
See also Mark Nottinghams Blog:
POSTs don't deal in representations of identified state, 99 times out
of 100. However, there is one case where it does; when the server goes
out of its way to say that this POST response is a representation of
its URI, by setting a Content-Location header that's the same as the
request URI. When that happens, the POST response is just like a GET
response to the same URI; it can be cached and reused -- but only for
future GET requests.
The rfc also describes a PRG sequence which has a similar effect, allowing the response cycle to a POST to fill the cache for a subsequent GET - which is probably more widely implemented.
Can you try to change the Cache-Control to public instead of private and see if it's working?

Resources