Spring MVC response Headers: ETag has double quotes for GET request but not for PUT request - spring

We upgraded our Spring MVC from 4.0 to 4.3 in our service. It caused ETag format change in the response headers for "GET" method. Clients making "GET" calls will get ETags with double quotes in the response headers. Previously ETag in the response headers had no double quotes for "GET" method.
For example:
Now: etag →"TVMzTWFpbmxpbmVEZXZvLTI5ODIxMQ"
Previously: etag →TVMzTWFpbmxpbmVEZXZvLTI5ODIxMQ
The response of "PUT" request does not have double quotes around ETag in Headers, just like before.
Anyone has any ideas why?

Prior to Spring 4.2.x, there was no management for the ETag header. It has been introduced since then in the HttpEntityMethodProcessor. The class has evolved during time and the management of the ETag header respect the RFC (or is close enough).
As you can see in this commit, Spring team fixed an issue with its management:
Fix missing ETag/LastModified headers in responses
Prior to this commit, the HttpEntityMethodProcessor would avoid
writing ETag/Last-Modified response headers before calling
ServletWebRequest to process conditional requests. This was done to
avoid duplicate response header values due to headers being already
written to the underlying servlet response.
This is still necessary for GET/HEAD requests, since this is properly
handled by ServletWebRequest for those cases. But
HttpEntityMethodProcessor should not make that decision for
PUT/PATCH/POST responses since developers are adding response headers on
purpose and should be in control of the situation — whereas
ServletWebRequest does not write those headers in those cases.
The relevant part of modified code is here.
So basically, when you are manually adding an ETag header, in case of a 200 status of a GET or HEAD method, the framework removes it, then recreates it. That is why with a PUT, there are no double quotes.
In HttpEntityMethodProcessor:
if (inputMessage.getMethod() == HttpMethod.GET || inputMessage.getMethod() == HttpMethod.HEAD) {
responseHeaders.remove(HttpHeaders.ETAG);
responseHeaders.remove(HttpHeaders.LAST_MODIFIED);
}
Then in ServletWebRequest:
private String padEtagIfNecessary(String etag) {
if (!StringUtils.hasLength(etag)) {
return etag;
}
if ((etag.startsWith("\"") || etag.startsWith("W/\"")) && etag.endsWith("\"")) {
return etag;
}
return "\"" + etag + "\"";
}
As you can see, this respects the chapter 2.4 of the RFC:
2.4. When to Use Entity-Tags and Last-Modified Dates
In 200 (OK) responses to GET or HEAD, an origin server:
o SHOULD send an entity-tag validator unless it is not feasible to
generate one.
o MAY send a weak entity-tag instead of a strong entity-tag, if
performance considerations support the use of weak entity-tags, or
if it is unfeasible to send a strong entity-tag.
o SHOULD send a Last-Modified value if it is feasible to send one.
In other words, the preferred behavior for an origin server is to
send both a strong entity-tag and a Last-Modified value in successful
responses to a retrieval request.
But I found that it is not backward compatible and breaks what developer could have used prior to these versions, without the possibility to skip/override what they did.
Here is the description of the ETag from the MDN (more clearer).
Hope it helped in comprehension.

Related

Gin-Gonic Content-Type restriction

There is a service written in golang, using gin-gonic framework.
I only ever want to support application/json as a mime-type and it would be great if it always be in UTF-8. The business logic of the service might break if it will get values in different encodings.
Is it a good idea to write a custom middleware that checks if Content-Type header has value "application/json; charset=utf-8" and returns some 4xx status in case it is not?
UPDATE:
Just found out that ctx.ContentType() doesn't return charset part of the header. Is there a way to get it?
Nothing prevents you from simply looking at the "Content-Type" http header directly, to the tune of ctx.Request.Header.Get("Content-Type").
The helper ContentType method is provided by gin-gonic especially for the rather common case of querying the "unadulterated" mime type of incoming data without too much hassle.

How to return Http Status-Line with Error Response?

I am working on a Spring-Boot application. With each response I would like to return http Status-Line, Headers and Body. As per standards a Status-Line looks like: HTTP-Version SP Status-Code SP Reason-Phrase CRLF.
For Example: Http/1.1 400 Bad Request
I am using ResponseEntity with VnDErrors but Status-Line is not forming as per standards. I am able to see only "Http/1.1 400". Here Reason-Phrase is missing.
I have tried with #ResponseBody with #ResponseStatus annotation but no luck to achieve desired result.
Here is piece of code which I am using:
#ExceptionHandler(HttpRequestMethodNotSupportedException)
ResponseEntity<VndErrors> httpRequestMethodNotSupportedException(ex) {
LOGGER.error(ex.message)
ResponseEntity.status(BAD_REQUEST).contentType(VND_ERROR).body(new
VndErrors(BAD_REQUEST, exceptionMessage))
}
Expected reponse which conatins Status-Line: "Http/1.1 400 Bad Request"
Would like to know is this achievable? If yes, then how I can proceed to do same.
This is the standard behavior of tomcat see tomcat-8.5-changelog.html
spring-boot-issue-6789
RFC 7230 states that clients should ignore reason phrases in HTTP/1.1 response messages. Since the reason phrase is optional, Tomcat no longer sends it. As a result the system property org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER is no longer used and has been removed. (markt)

Spring's support for If-Match Header

Coming to HTTP conditional request, briefly, benefiting from the Etag and If-*, we can realize response cache(Etag+If-None-Match) and optimistic lock(Etag+If-Match).
As I see, it's convenient to perform the response cache using Spring which provides the specific filer ShallowEtagHeaderFilter to generate Etag value and check it against If-None-Match header. However, I cannot find corresponding components in Spring to do optimistic lock. Therefore, how can I implement that in Spring?
I had the same issue. Didn't find any native way in Spring to check it. But you can always extract the headers and do manual comparisons. For example (in Kotlin):
#PutMapping
fun update(
#RequestHeader("If-Match") ifMatch: String?
) : ResponseEntity<Void>{
if(ifMatch != null && ifMatch.trim() != computeETag()){
return ResponseEntity.status(412).build()
}

HttpClient is caching requests to the same URL

On Xamarin iOS. I'm using HttpClient to get a JSON string. The problem is that it ignores updates and gives me the same JSON response if I query the same URL. I want it to not cache anything and always actually query the URL and give me the new response.
This sounds trivial, there must be a simple way for this. I'm using a Forms shared project.
I assume you are setting the cache-control header to no-cache?
client.DefaultRequestHeaders.CacheControl.NoCache = true;
If so but it still doesn't work - maybe the server is caching the response? If it comes down to it, you can usually defeat something like that by adding a cachebuster to the querystring though. Just append a bogus param and pass it a unique value each time. For example, if your URL is http://my.url.com/resource/someid then you can defeat the caching by using http://my.url.com/resource/someid?b=1 and then increment that "b" param with each call.

Difference between the two Web API headers response.Content.Headers and response.Headers

I find WebAPI separate HTTP response headers into different places, one is in Response.Headers, the other in in Response.Content.Headers. For example, etag is in Response.Headers while lastModified is in the other. What is the reason behind that?
There are a couple of answers to that question. One is because that's the way the HTTP spec defines the headers.
RFC 2616
Content header fields here
https://www.rfc-editor.org/rfc/rfc2616#section-7.1
Request header fields here https://www.rfc-editor.org/rfc/rfc2616#section-5.3
Response header fields here https://www.rfc-editor.org/rfc/rfc2616#section-6.2
The other more practical reason for separating out the content headers is that it is easier write code that processes data into HTTP payloads and sets the related headers, independent of the request/response objects.
Unfortunately, the more recent HTTPbis specification did some reorganization of where they think headers should go and now LastModified and Allow are considered response fields, not content fields.
This means that the headers as defined in System.Net.HttpHeaders will no longer match the spec, which really sucks. It also means that we are probably stuck with LastModified as a HttpContent header and Etag as a response header.
HTTPbis
Content related headers are defined here.
Request headers here.
Response headers here.

Resources