Spring's support for If-Match Header - spring

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()
}

Related

Spring Integration DSL: How to add the HTTP.outboundGateway header?

What is the easiest way to add the HTTP.outboundGateway header in my program?
What I want to do is:
I first do the HTTP GET for the URL
http://localhost:8050/session
then I get the JSON
{
"session": "session8050"
}
I extract the value of the session variable and add that to the next HTTP GETas the session header variable.
Currently I have working code, but I was thinking could I do this easier? My implementation
Extracts the session variable from the JSON with the jsonPath method
Then the implementation adds the session variable to the integration flow message header with the enrichHeaders method
Then the implementation adds the session variable to the HTTP call header with the HeaderMapper class
My implementation is
integrationFlowBuilder
.transform(p -> authenticationJson)
.enrichHeaders(h -> h.header("Content-Type", "application/json"))
.handle(Http.outboundGateway("http://localhost:8050/session").httpMethod(HttpMethod.POST)
.expectedResponseType(String.class))
.enrichHeaders(
h -> h.headerExpression("session", "#jsonPath(payload, '$.session')", true)
.handle(Http
.outboundGateway(completeFromUrl)
.httpMethod(HttpMethod.GET).mappedRequestHeaders("session").headerMapper(headerMapper())
.expectedResponseType(String.class))
My headerMapper is
#Bean
HeaderMapper headerMapper() {
final DefaultHttpHeaderMapper headerMapper = new DefaultHttpHeaderMapper();
final String[] headerNames = { "session" };
headerMapper.setOutboundHeaderNames(headerNames);
headerMapper.setUserDefinedHeaderPrefix("");
return headerMapper;
}
Is it possible to extract the session variable from the JSON and add it straight to the HTTP headers??
Why the HeaderMapper must be used? Why the integration flow message headers don't go straight to the HTTP.outboundGateway call as the payload goes?
First of all you need to understand that main goal of Spring Integration as any other EIP solution is to make components in the flow as isolated as possible, so in the future you can add some intermediate steps or remove without big impact for the whole solution and other components in your integration flow. This should be an answer to your questions about why HeaderMapper must be used.
As you see the contract of the HeaderMapper to remap MessageHeaders to the target protocol headers representation. There is nothing about payload, hence you need to map the value from the payload into the headers, first of all. And then say Http.outboundGateway() what should be remapped from the MessageHeaders into the HttpHeaders.
By default the DefaultHttpHeaderMapper (it is present there in the Http.outboundGateway()) maps only standard HTTP headers suitable for the HTTP request.
If you need to include some custom header, like in your case with that session, you really can use a custom configuration for the DefaultHttpHeaderMapper, or just configure a convenient option on the Http.outboundGateway():
.mappedRequestHeaders("session")
The setUserDefinedHeaderPrefix("") is not necessary from version 5.0. It is empty string by default now, since there is no requirements in the prefixes for custom headers in the HTTP protocol.

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

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.

How to validate request against XSD and return an error object?

My task is to implement a webservice that:
consumes an XML file on a POST endpoint
in happy flow, it returns a DTO as JSON + HTTP 2xx
the incoming XML file is validated against a XSD; if the validation fails, a JSON with a list of all validation errors is returned (including the line, column, error) with HTTP Bad request
the application exposes two endpoints, only one of them should be validated
I have started the implementation with Spring Boot + web, using regular #PostMapping which has "consumes" and "produces" set to application/xml and application/json, respectively. The usual flow works perfectly fine. Now, I stumbled upon the issue of validating the incoming payload. What I figured out:
1) I have to validate the payload before it is converted (marshalled) to an object.
2) Once validated, I have to either:
allow further processing
stop any further processing, write the error object to the response and set the status code to 400 Bad request
My approaches were:
1) using a RequestBodyAdvice, more specifically the beforeBodyRead method implementation. I had the following issue here: I don't know how to write anything to the output in case the validation fails.
2) using a Filter (I've extended OncePerRequestFilter) - fortunately, I can read the request (request.getInputStream()) and write to the response (response.getOutputStream()).
However, how can I do the selective filtering (as mentioned, I only want to validate one single endpoint)?
Are there any other alternatives for placing the incoming request XSD validation? Is spring-web the appropriate choice here? Would you recommend some other library / framework?
To validate xml against xsd schema, my preference is XML Beans. It is very easy to use. Other options are JABX, Castor. Take a look at Java to XML conversions?.
You will need to jar using xsd schmema and will need to put it in the classpath of your application so that it's classes are available for you for validation. Please take a look at this blog.
You can use validation API as mentioned here.
I would prefer to write validation code in the aspect so that it can be reused with other APIs.
If validation fails, throw valid exception from the aspect itself.
If validation is passed, process your input string that you receive.
Please let us know if you need any more information.

Get headers from #RequestHeader vs HttpServletRequest

What's the difference between read header data from #RequestHeader annotation vs HttpServletRequest?
The advantage of using Spring #RequestHeader is that it will automatically throw an exception like
HTTP Status 400 - Missing request header 'X' for method parameter of type, if the header is NOT sent in the input request (by setting required=true). An example usage shown below:
#RequestMapping(method=RequestMethod.GET)
public String users(#RequestHeader(required=true)String myHeader, Model model) {
//your Code
}
You can also set the default value for the header if you use #RequestHeader, you can refer here
We need to manually check the condition for header value !=null, throw the exception or set the default value, if you use HttpServletRequest, which will make your code verbose.
There is no difference in Performance. It is more convenient to use #RequestHeader than HttpServletRequest.getHeader().
This is similar to #RequestParam and HttpServletRequest.getParameter().
Spring has done the work of converting the request Headers, Parameters to method Parameters so that you can avoid boilerplate code.
There is a Spring written MethodArgumentResolver named RequestHeaderMethodArgumentResolver which in fact uses HttpServletRequest.getHeader() to get the header.

Validate request headers with Spring validation framework

Is it possible to use the Spring validation framework with Spring MVC to validate the presence and value of an HTTP request header?
To check the presence of a request header, you don't need the validation framework. Request header parameters are mandatory by default, and if a mandatory header is missing in a request, Spring MVC automatically responds with 400 Bad Request.
So the following code automatically checks the presence of the header "Header-Name"...
#PostMapping("/action")
public ResponseEntity<String> doAction(#RequestHeader("Header-Name") String headerValue) {
// ...
}
... and if the header shall be optional, the annotation would need to be replaced by:
#RequestHeader(name = "Header-Name", required = false)
To check the value of a request header, the Spring validation framework can be used. To do this, you need to
Add #Validated to the controller class. This is a workaround needed until this feature is implemented.
Add the JSR-303 annotation to the request header parameter, e.g.
#RequestHeader("Header-Name") #Pattern(regexp = "[A-Za-z]*") String headerValue
Note however that this will result in a 500 in case of an invalid header value. Check this question for how to also get the correct status code (i.e. 400) for this case.
I don't see how this would be possible, since the validation framework only operates on your domain objects, not on the HTTP request itself. Specifically, the Validator interface doesn't specify any methods that take the HttpServletRequest object, which is what you'd need to have access to in order to grab the headers and test them.
Using the validation framework feels like the wrong solution to whatever problem you're trying to solve, especially since it's hard to know how there'd be a unique HTTP request header for a given form submission. Are you looking to test for an HTTP header that should always be present in requests to your app? Then you might want to consider implementing a HandlerInterceptor, which will intercept and process all requests to pages that you've mapped in any HanderMappings. Are you looking to test for an HTTP header that should always be present in any page view of your app? Then you'd want to implement a Filter, which operates outside of the context of Spring MVC.

Resources