#RequestHeader mapped value contains header twice - spring

We are facing a super strange problem: in our endpoint:
#PostMapping(value = "/send_event_to_payment_process")
#Async
public void sendEvent(#Valid #RequestBody final SendEventRequestDto dto, #RequestHeader(value = TENANT) String foo) {
the mapped #RequestHeader variable foo contains the vaue twice joined with a ',' ("test,test"). If we read the header programmatically using the request context:
public void sendEvent(#Valid #RequestBody final SendEventRequestDto dto, #Context final HttpServletRequest request) {
final String tenant = request.getHeader(TENANT);
we receive the proper value (only once: "test").
Any clues what the problem might be?!
Thank you!

You are comparing different things.
The HttpServletRequest.getHeader method always returns a single value, even if there are multiple values for the header. It will return the first (see the javadoc of the method).
Spring uses the HttpServletRequest::getHeaders method to get all the values. Which retrieves all header values and, depending on the value, return the String[] or creates a single concatenated String.
To compare the same things you also should use the getHeaders method and then you will have the same result. Which means your request contains 2 header values for the given header.

Related

Two similar Spring endpoints, wrong one is being called

I have two endpoints in the same controller file
#PostMapping("{app}/{schema}")
public ResponseEntity createResource(
#PathVariable final String app,
#PathVariable final String schema,
#RequestBody final Object input,
final HttpServletRequest request) {
//do stuff
}
#PostMapping("{app}/bulk")
public ResponseEntity bulk(
#PathVariable final String app,
#RequestBody final Object input,
final HttpServletRequest request) {
//do stuff
}
The createResource gets called when I do POST app/bulk.
Your first endpoint
#PostMapping("{app}/{schema}")
is a wildcard endpoint due to two PathVariables. Therefor, any request to a path yourapp.com/xyz is caught by it and the second endpoint is always ignored.
To solve this, you should either create two distinct endpoints without using the {schema} PathVariable like you are doing in the second endpoint, or just use the first one as is and check the {schema} variable for the current path in //do stuff.
This is because when the URL gets resolve, your first endpoint will perfectly fit in.
#PostMapping("{app}/{schema}")
Here bulk will be resolve as {schema} string. You can avoid it by defining a unique name like this :
#PostMapping("{app}/schema/{schema}")
This should solve your problem.
You should merge two endpoint like this
#PostMapping("{app}/{schema}")
public ResponseEntity createResource(
#PathVariable final String app,
#PathVariable final String schema,
#RequestBody final Object input,
final HttpServletRequest request) {
if ("bulk".equals(schema)) {
// do bulk stuff
} else {
//do stuff
}
}

how to capture some parameters using #RequestParam using spring mvc?

Suppose a hyperlink is clicked and an url is fired with the following parameter list myparam1=myValue1&myparam2=myValue2&myparam3=myValue3 . Now how can I capture some of the parameters using #RequestParam in spring mvc?
My requirement is I have to capture some of the params and build the request to server. can I make all the request params as optional and used when required?
Suppose I want to use first two params and want to ignore the third.
For eg. http://localhost:8080/api?myparam1=myValue1&myparam2=myValue2 and just not giving 3rd parameter in request.
In the next scenario, I want to use second and third and want to ignore the first parameter.
For eg. http://localhost:8080/api?myparam2=myValue2&myparam3=myValue3 and just not giving 1st parameter in request.
In another scenario, I don't want to use any of the request param.
For eg. http://localhost:8080/api and just not giving any parameters in request.
is there any way I could achieve this? Please help...!
You can capture all the params in a Map (the key is the name of the param) like this :
public void requestAllParams(#RequestParam Map<String, String> params)
You can also capture optional param using Optional like this :
public void requestParamOptional(#RequestParam(required=false) Optional<String> param)
A parameter with #RequestParam is by default required. It can be marked as not required:
#GetMapping
public ResponseEntity<Object> aMapping(#RequestParam String myparam1, #RequestParam String myparam2, #RequestParam(required = false) String myparam3) {
// response
}

Spring throw Bad Request if the request param not available in Controller method

I want to restrict the list of allowed request parameters in my controller, so that if I get a request with an undefined parameter in the controller, it should return a bad request, but it returns 200 OK.
I think that this one should be fixed on the framework level, but not in the controller layer.
I am confused about it, and could not find anything on the internet
For e.g I have the following controller definition:
#GetMapping("/Client")
public ResponseEntity<List<Client>> getAllClients(HttpServletRequest httpServletRequest,
#RequestParam(value = McsConstants.PAGE_HEADER, required = false) Integer page,
#RequestParam(value = McsConstants.SIZE_HEADER, required = false) Integer size) {...}
And the valid request would be
GET .../Client
GET .../Client?page=1
GET .../Client?size=10
GET .../Client?page=1&size=10
How can I validate the case when I have an undefined request param like below?
GET .../Client?someUndefinedParam=1
Let me know please for any ideas or links
Thanks!
One way to handle this can be using #RequestParam annotation on a Map or MultiValueMap argument.
The map is populated with all the request parameters, therein you can write your logic to validate and reject unidentified query params.
EDIT: Following is an example for the same-
#RequestMapping(value = "/test", method = RequestMethod.GET)
public void testMethod(#RequestParam Map<String, String> allRequestParams) {
if (!isValidParams(allRequestParams)) {
// isValidParams() will validate the map with a reference map containing all the expected params
// throw BadRequestException here
}
}
Hope this helps!
let me share my knowledge it may helpful for some other scenarios
If the requestparam and variable is same name you no need to mention the value #RequestParam .
below i have mentioned with code example. Please share ur feedback
#GetMapping("/Client")
public ResponseEntity<List<Client>> getAllClients(HttpServletRequest httpServletRequest,
#RequestParam <Optional>Integer page,
#RequestParam <Optional>Integer size) {
//you can do ur logics
}

Spring model and request objects

In Spring, why can't we simply use a request object to carry data around in the application? Why did we switch over to using a model object? Also I read on a popular Spring book that it is still a request object (even in a Spring application) that does most of the hardwork of carrying data. Does it mean that the models we create in Spring are somehow linked to a request object underneath the hood?
In Spring, why can't we simply use a request object to carry data
around in the application?
You can use HttpServletRequest or even WebRequest as method argument and then inspect those two in order to extract the data out of it. For example, if you want to get the value of a query string named page:
#RequestMapping("/users")
public void listUsers(HttpServletRequest request) {
String page = request.getParameter("page");
...
}
Why did we switch over to using a model object?
You can simply read from a HttpServletRequest and finally write to a HttpServletResponse in any controller method but spring also provides nicer abstractions for handling typical web scenarios. For example if your page parameter is a required int parameter with the default value of 1, you can simply write:
#RequestMapping("/users")
public void listUsers(#RequestParam(defaultValue = "1") Integer page) {
...
}
Spring automatically does all the string to int conversion and populating with default values for you. Or suppose you want to automatically validate your json or xml Request Body and return validation errors. With these higher abstractions you will define a model object that contains all the validation logic in it and simply add #Valid annotation before its reference it method arguments, that's it!
#RequestMappting(value = "/users", method = POST)
public void addNewUser(#RequestBody #Valid User user) {
// automatically validates the user
// and send validation error, if user wasn't valid
}
Of course you can do the same by inspecting HttpServletRequest but it would be so cumbersome.
Does it mean that the models we create in Spring are somehow linked to
a request object underneath the hood?
They're not necessarily link to them but usually these model objects will be populated from request parameters.
For example, spring convert the request body to the method argument by using an HttpMessageConverter. HttpMessageConverter is responsible for converting from the HTTP request message to an object and converting from an object to the HTTP response body. For instance FormHttpMessageConverter converts form data to/from a MultiValueMap<String, String>. This class has a read method that reads the request body and converts it to a MultiValueMap<String, String>. This method looks like this:
public MultiValueMap<String, String> read(Class<? extends MultiValueMap<String, ?>> clazz,
HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
MediaType contentType = inputMessage.getHeaders().getContentType();
Charset charset = (contentType.getCharSet() != null ? contentType.getCharSet() : this.charset);
String body = StreamUtils.copyToString(inputMessage.getBody(), charset);
String[] pairs = StringUtils.tokenizeToStringArray(body, "&");
MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>(pairs.length);
for (String pair : pairs) {
int idx = pair.indexOf('=');
if (idx == -1) {
result.add(URLDecoder.decode(pair, charset.name()), null);
}
else {
String name = URLDecoder.decode(pair.substring(0, idx), charset.name());
String value = URLDecoder.decode(pair.substring(idx + 1), charset.name());
result.add(name, value);
}
}
return result;
}
By String body = StreamUtils.copyToString(inputMessage.getBody(), charset);, it actually reads the request body and converts it to a String.

Need to set parameter value and datatypes taken from URI of resful web service into HashMap

#RequestMapping(value = "/{methodName}/{responseType}", method = RequestMethod.GET)
public ModelAndView execute(#PathVariable String methodName, #PathVariable String responseType,
HttpServletRequest request, HttpServletRequest response){
Map<String,String> yesMap = new HashMap<String,String>();
}
As shown in code above I need to get data types of parameters passed from {responseType} and set the same in yesMap against parameter values.
String param=request.getParameter("id");
is returning null values.
need to get data types of parameters passed from {responseType}
Its type will always be String as you are collecting it in responseType which is String.
get data types of parameters passed from {responseType} and set the same in yesMap against parameter values
I think yesMap.put("String",responseType); will do.
In the code you posted above, the follwign call
yourRootUrl/cheese/stilton
will result in these being true in your "execute" method:
methodName.equals("cheese");
responseType.equals("stilton");
I don't understand what you are trying to achieve. Your parameter names are confusing.
Its quite simple, you have declared two string paramters, which thanks to spring can be path variables.
This is a GET request, so getParamter won't work, try using String param=request.getAttribute("id"); which is completely unrelated to your path variables. Or the differetn annaotion RequestParam like so :
#RequestMapping(value = "/{methodName}/{responseType}", method = RequestMethod.GET)
public ModelAndView execute(#PathVariable String methodName, #PathVariable String responseType, #RequestParam int id)

Resources