Why #RequestParam annotation bind the value from request body? - java

I have this request
#RequestMapping(value = "/test", method = RequestMethod.POST)
public void test(ModelMap modelMap, #RequestParam(value = "name") String name) {
modelMap.put("result",name);
}
When I call this request from Postman and pass the Name parameter in the request body and in the URL, the result is like this :
But if I remove the parameter from request body, the request is like this :
Why does #RequestParam annotation bind the value from the request body first? and if it doesn't exist in the body, it bind the value from URL parameters

Because it's how ServletRequest works. Behind the scene #RequestParam is using ServletRequest#getParameter. If you take a look at the java doc it clearly state that query parameter or form post data are used.
For HTTP servlets, parameters are contained in the query string or posted form data.
If there is a multiple value for instance same key in query and post data then it returns the first value in the array returned by getParameterValues.
Furthermore you are using multipart/form-data content type so spring handle it with DefaultMultipartHttpServletRequest where parameters found in the body are returned first:
#Override
public String[] getParameterValues(String name) {
String[] parameterValues = super.getParameterValues(name);
String[] mpValues = getMultipartParameters().get(name);
if (mpValues == null) {
return parameterValues;
}
if (parameterValues == null || getQueryString() == null) {
return mpValues;
}
else {
String[] result = new String[mpValues.length + parameterValues.length];
System.arraycopy(mpValues, 0, result, 0, mpValues.length);
System.arraycopy(parameterValues, 0, result, mpValues.length, parameterValues.length);
return result;
}
}

Related

I can not take a value from the header in the Controller in Spring Boot

I have built a web application in Spring Boot that has a form and besides the values from the form that it inserts in the database it takes from the header the username. This is the code from the Controller:
#PostMapping("/BilantErr/")
public String postIndex(#RequestParam(name = "cui") String cui, #RequestParam(name = "an_raportare") String anRaportare,
#RequestParam(name = "perioada") String perioada,
#RequestHeader("iv-user") String operator, #RequestParam(name = "motivatie") String motivatie, Model model) {
String page = "";
//String operator = "serban";
try {
if (repository.insert(cui, anRaportare, operator, motivatie, perioada) == true) {
page = "success";
} else {
page = "error";
}
} catch (Exception e) {
Logger logger = LoggerFactory.getLogger(BilantErr.class);
}
return page;
}
The error that I am getting is : Resolved [org.springframework.web.bind.MissingRequestHeaderException: Required request header 'iv-user' for method parameter type String is not present]
What may be the problem ? There is an application already built in JSF that works and takes the user from the header and I have to replace it with a Spring app. What am I doing wrong ? Thanks
MissingRequestHeaderException means the HTTP request doesn't contain a "iv-user" header. You must have a look to your request first. You can read all headers of the HTTP request by following code snippet:
#GetMapping("/listHeaders")
public ResponseEntity<String> multiValue(
#RequestHeader MultiValueMap<String, String> headers) {
headers.forEach((key, value) -> {
LOG.info(String.format(
"Header '%s' = %s", key, value.stream().collect(Collectors.joining("|"))));
});
return new ResponseEntity<String>(
String.format("Listed %d headers", headers.size()), HttpStatus.OK);
}
The request header "iv-user" is required but does not seem to be present in the request you receive. You could fix your request or make the header optional: #RequestHeader(value = "iv-user", required = false)

PostMapping application/x-www-form-urlencoded;charset=UTF-8 NULL?

application/x-www-form-urlencoded content type work very well
but application/x-www-form-urlencoded;charset=UTF-8 not work. return null value;
I tried send the request value from postman.
set key = body, value = "1234" all types such as form-data, raw, x-www.form
#PostMapping(value = "/test")
public ModelAndView success(HttpServletRequest request, #RequestParam String body, Header header) throws Exception{
log.debug("encodeData="+ body);
return null;
}
if I send string value through body, null return.

how to Populate fields from URL parameter in java Springboot

I need to add support for passing field values to a form via a URL parameter when viewing a form which will be used to populate values into the form when it loads. should support a JSON formatted value of field names and values. Example:
https://web/form/view?id=1&data={'fieldname':'value'}
#RequestMapping(value = "/form/view", method = RequestMethod.GET)
public ModelAndView viewDomainForm(HttpSession session, #ModelAttribute("command") FormBean inFormBean) {
Form form = formService.getForm(inFormBean.getId());
FormInstance formInstance = formInstanceService.createFormInstance(form);
String formInstanceId = (String) session.getAttribute("formInstanceId");
if (formInstanceId != null && formInstanceId.length() > 0) {
formInstanceService.deleteFormInstanceById(formInstanceId);
}
formInstanceId = formInstance.getId();
session.setAttribute("formInstanceId", formInstanceId);
FormBean formBean = prepareFormBean(form);
formBean.setEmbed(inFormBean.isEmbed());
formBean.setFormInstanceId(formInstance.getId());
Map<String, Object> model = new HashMap<String, Object>();
model.put("externalBaseUrl", configurationService.getValue("core.external.url.base"));
model.put("fromTaskList", false);
model.put("form", formBean);
model.put("flow", new FlowBean());
model.put("task", new TaskBean());
return new ModelAndView("form-viewer", model);
}
I have this method for viewing. what would be the best way to Populate fields from URL parameter?
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
// this is the value that will populate into the form when it loads.
First question, why not use POST with the JSON in the request body?
If the JSON will be large, then you will run in a URL limitation problem. And you have to encode your URL with % and different delimiters which will make it even less debuggable.
But it is doable.
Let's say that you correctly encoded your URL and the JSON is as a query param.
What you could do is receive it and either traverse it and create/populate your form or map it to an object and use that:
public ModelAndView viewDomainForm(... #RequestParam("data") String jsonDataInUrl...)
{
// Work with jsonDataInUrl either through parsing it or through transforming it into a java bean.
...
}
Here is a URL decoder/encoder in case you need it.

Redirect from Spring REST controller with parameters

I have two different Spring applications (app-one, app-two), app-one should receive a response and then redirect to app-two with some parameters. So, I have the following REST controller in app-one:
#RestController
public class RedirectController
{
#GetMapping(value = "/redirect")
public ResponseEntity<Void> redirectEndpoint(HttpServletRequest request,
RedirectAttributes redirectAttributes)
{
// Do some business logic
// Set parameters
redirectAttributes.addAttribute("attribute", "Value 1");
redirectAttributes.addFlashAttribute("flashAttribute", "Value 2");
// Redirect to success URL
String redirectURL = "http://app-two/success";
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(redirectURL));
return ResponseEntity.status(HttpStatus.FOUND).headers(headers).build();
}
}
And the following REST controller in app-two:
#RestController
public class SuccessController
{
#GetMapping(value = "/success")
public ResponseEntity<Void> successEndpoint(HttpServletRequest request, Model model,
#RequestParam(value = "attribute", required = false) String attribute,
#ModelAttribute(value = "flashAttribute") String flashAttribute)
{
// Get parameters
System.out.println("attribute: " + attribute);
System.out.println("flashAttribute: " + flashAttribute);
String flashAttributeFromModelMap = (String) model.asMap().get("flashAttribute");
System.out.println("flashAttributeFromModelMap: " + flashAttributeFromModelMap);
Map<String, ?> flashMap = RequestContextUtils.getInputFlashMap(request);
if (flashMap != null)
{
String flashAttributeFromFlashMap = (String) flashMap.get("flashAttribute");
System.out.println("flashAttributeFromFlashMap: " + flashAttributeFromFlashMap);
}
// Do some business logic
return ResponseEntity.status(HttpStatus.OK).build();
}
}
I was able to redirect successfully by returning FOUND (302). But when adding attributes to RedirectAttributes (in this case attribute and flashAttribute), these attributes are not found after the redirection done (attribute gets null and flashAttribute gets empty).
I tried to get the attributes values by different ways (#RequestParam, #ModelAttribute, model.asMap().get(), and RequestContextUtils.getInputFlashMap(request).get()) but none of them gets the correct value.
What I need is to get the correct attributes' values in successEndpoint. Any suggestions on how to accomplish that?
Thanks in advance.

Bug: Required request body is missing

I'm trying spring framework.
I have RestController and function:
#RequestMapping(value="/changePass", method=RequestMethod.POST)
public Message changePassword(#RequestBody String id, #RequestBody String oldPass,
#RequestBody String newPass){
int index = Integer.parseInt(id);
System.out.println(id+" "+oldPass+" "+newPass);
return userService.changePassword(index, oldPass, newPass);
}
and code angularJS
$scope.changePass = function(){//changePass
$scope.data = {
id: $scope.userId,
oldPass:$scope.currentPassword,
newPass:$scope.newPassword
}
$http.post("http://localhost:8080/user/changePass/", $scope.data).
success(function(data, status, headers, config){
if(date.state){
$scope.msg="Change password seccussful!";
} else {
$scope.msg=date.msg;
}
})
.error(function(data, status, headers, config){
$scope.msg="TOO FAIL";
});
}
and when i run it.
Error Message :
Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public com.csc.mfs.messages.Message com.csc.mfs.controller.UserController.changePassword(java.lang.String,java.lang.String,java.lang.String)
Help me fix it, pls...
Issue is in this code.
#RequestBody String id, #RequestBody String oldPass,
#RequestBody String newPass
You cannot have multiple #RequestBody in same method,as it can bind to a
single object only (the body can be consumed only once).
APPROACH 1:
Remedy to that issue create one object that will capture all the relevant data, and than create the objects you have in the arguments.
One way for you is to have them all embedded in a single JSON as below
{id:"123", oldPass:"abc", newPass:"xyz"}
And have your controller as single parameter as below
public Message changePassword(#RequestBody String jsonStr){
JSONObject jObject = new JSONObject(jsonStr);
.......
}
APPROACH 2:
Create a custom implementation of your own for ArgumentResolver
You can't have request body for the GET method. If you want to pass username and password as part of request body then change RequestMethod type to POST/PUT.
If you want to use GET only then you will have to pass username and password as either path variables or request/query parameters - which is not best practice.
I would recommend changing RequestMethod and pass username & password as request body.

Categories

Resources