Get headers from #RequestHeader vs HttpServletRequest - spring

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.

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.

How to set Content-Type in spring-mvc?

I am wondering how I can set the Content-Type header when using a template engine.
I know there is a default configuration that can be set at the ViewResolver configuration, but I want, per method, set a different content type (html or json)
I tried using the produces attribute from #GetMapping but it is not used in the View object.
AFAIK, the Content-Type is set per View and the View is common across all the queries :(
you can pass HttpServletResponse to your method and set its content type
#GetMapping
public Entity getEntity (HttpServletResponse response) {
response.setContentType("application/json;charset=UTF-8");

Spring MVC Response already committed Issue

#RequestMapping(value="/getStats/{requestData}" , consumes=MediaType.APPLICATION_JSON_VALUE , method=RequestMethod.GET)
public void testRequest(
#PathVariable (value="requestData") GetStatsRequestBean getStats){
System.out.println("inside (testRequest)");
System.out.println(getStats);
}
In server log am getting
SRTServletRes W WARNING: Cannot set header. Response already committed.
And my app url is..
http://myhost:9080/myapp/getStats/{"startDate":"2013-10-05","endDate":"2013-10-05"}
Trying to set a response header after the response is committed causes this warning. The code you shared does not seem to do this. Are you sure you do not have any filter configured which tries to do this ? Your controller handler method return type is void which implies that you intend to generate the response yourself by using http servlet response object which needs to be provided in tehemethod parameter so that Spring can inject it and you can use this. As you have not provided the httpservlet response object in parameter, the actual view served would be implicitly determined by through a RequestToViewNameTranslator configured in your context.

Spring MVC - HTTP status code 400 (Bad Request) for missing field which is defined as being not required

I have Spring MVC application with this controller method.
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addNumber(#RequestParam(value="number", required=false) Long number) {
...
return "redirect:/showAll/";
}
In my JSP I have a standard HTML form which is posting a value named "number" to the controller method above. However, if I leave out the value (do not enter anything into the text field) and POST the data to the controller, before the controller method is called my browser shows
HTTP Status 400 - Required Long parameter 'number' is not present
although the controller method annotation clearly defines the "number"-parameter as not required.
Does anyone have a slight idea of what could be going on?
Thank you.
PS: The exception that is being thrown is as follows:
org.springframework.web.bind.MissingServletRequestParameterException: Required Long parameter 'number' is not present
EDIT: This is a Spring 3.2.3.RELEASE bug ( see here). With version 3.1.4.RELEASE I do not have this problem anymore.
I came across the same situation, and this happens when your parameter is present in the request with an empty value.
That is, if your POST body contains "number=" (with empty value), then Spring throws this exception. However, if the parameter is not present at all in the request, it should work without any errors.
My problem was that some of the headers in a request I was sending with Postman were not present (were unchecked):
When I checked back the Content-Length header, the request worked fine (200 OK response).

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