Form application encoded value is not working in spring rest - java

I have the below post request and of which below is the controller code
#RestController
#RequestMapping(/flow", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
#Override
#ResponseStatus(HttpStatus.OK)
#PostMapping("{abcCode}/token")
public TokenResponse createToken(#PathVariable("abcCode") String abcCode,
#RequestParam("grant_type") String grantType,
#RequestParam String code,
#RequestParam("redirect_uri") String redirectUri,
#RequestParam String clientId) {
LOG.info(
"Received call for createIdToken for abcCode: {} , clientId: {} , grantType: {} ,code: {} , redirectUri: {}",
abcCode, clientId, grantType, code, redirectUri);
}
Now the problem is that when I test the same above controller through postman by choosing the body type as application form-encoded then it is working fine but when I choose the body type as none in postman and just pass the above request parameters as query one then also it works which ideally it should not please advise how can I overcome from the same
http://localhost:19080/testService/flow/token?grant_type=authorization_code&code=3272&redirect_uri=http://www.abchui.com&clientId=ATS
it should not work for the above URL

From spring sources:
public static final String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded";
According to docs, when using url-form-encoded data pass as query params.
Try to change form mime type.

Related

RestTemplate for #RequestBody and #RequestParam

I'm trying to use RestTemplate to call POST api like that :
RequestorParam test = new RequestorParam();
test.setScopeMcg("MCG");
test.setSituatedDealIds(situatedDealIds);
String url = "http://localhost:" + serverPort + "/retrieveAttributes";
ResponseEntity<SituatedDeals> response = restTemplate.postForEntity(url, test, SituatedDeals.class);
and the code of the controller is like ;
#PostMapping(value = "/retrieveAttributes", produces = "application/json")
#ResponseBody
#Duration
public SituatedDeals retrieveAttributes(
#RequestParam String scopeMcg,
#RequestBody SituatedDealIds situatedDealIds
) {
log.info("success")
}
i'm getting bad request, can someone help ?
According to your controller code, you are actually not returning any Response Entity of type SituatedDeals, just logging it as success. this might be the reason for the null object in response.
The scopeMcg is a RequestParameter so you should be passing it in a request param format i.e http://localhost:8080/retrieveAttributes?scopeMcg=MCG
Reference:Spring Request Param
Your test Object is the actual payload for your post request which should be of type SituatedDealIds object.
Reference: Rest-Template post for Entity

How to send post request from angular app to spring?

I am trying to send port request with username and password:
signUp(username, password): Observable<String> {
return this.http.post<String>("http://localhost:8080/signUp", {
params: { username: username, password: password }
});
}
To a spring service that has this method:
#CrossOrigin(origins = "http://localhost:4200")
#RequestMapping(value="/signUp", method=RequestMethod.POST)
public ResponseEntity<String> signUp(#RequestParam ("username") String username, #RequestParam("password") String password) throws IOException {
//not important
return new ResponseEntity<String>("Added successfully.", HttpStatus.OK);
}
When I send it, in angular I get http 400 error. In spring service I see this message:
2020-03-13 19:32:38.486 WARN 13200 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'username' is not present]
I know that there are values sent from Angular application in that http request (I checked with hardcoded). Can someone help me solve it? Thanks in advance.
Seems like there is a confusion between #RequestBody and #RequestParam - they're two entirely different things.
#RequestBody Indicates that the API is expecting a request payload
and
#RequestParam expects one or more params to be passed in to the API
url when it is invoked.
Now, the backend expects a request parameter to be passed in when it is invoked. For eg: /signUp/username=abc, so from the UI you need to pass in this key-value pair i.e
http.post<String>(`http://localhost:8080/signUp?username=${username}&password=${password}`)
The 400 bad request originates as you are passing in a request body rather than a request parameter. An alternate solution is to change the backend to accept a request payload - you would then need to use #RequestBody.
A possible solution could be this request:
signUp(username, password): Observable<String> {
return this.http.post<String>("http://localhost:8080/signUp", {
username: username, password: password
});
}
...with a class for the request body in your backend:
public class SignUp {
private String username;
private String password;
// constructor, getters and setters or lombok #Data
}
...and then in your controller:
#RequestMapping(value="/signUp", method=RequestMethod.POST)
public ResponseEntity<String> signUp(#RequestBody SignUp signUp) {
// signUp.getUsername()...
return new ResponseEntity<String>("Added successfully.", HttpStatus.OK);
}

Combine file upload and request body on a single endpoint in rest controller

The UI for my webapp has the ability to either upload a file(csv), or send the data as json in request body. However either a file upload, or a json request would be present in the request and not both. I am creating a spring rest controller which combine file upload and also accepts the request json values as well.
With the below endpoint tested from postman, I am not getting exception:
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
#RestController
public class MovieController {
private static final Logger LOGGER = LoggerFactory.getLogger(MovieController.class);
#PostMapping(value="/movies", consumes = {"multipart/form-data", "application/json"})
public void postMovies( #RequestPart String movieJson, #RequestPart(value = "moviesFile") MultipartFile movieFile ) {
// One of the below value should be present and other be null
LOGGER.info("Movies Json Body {}", movieJson);
LOGGER.info("Movies File Upload {}", movieFile);
}
}
Appreciate any help in getting this issue solved?
Note: I was able to build two separate endpoint for file upload and json request, but that won't suffice my requirement. Hence I'm looking for a solution to combine both
Try something like:
#RequestMapping(value = "/movies", method = RequestMethod.POST, consumes = { "multipart/form-data", "application/json" })
public void postMovies(
#RequestParam(value = "moviesFile", required = false) MultipartFile file,
UploadRequestBody request) {
In RequestBody you can add the parameters you want to send.
This will not send the data as JSON.
Edit:- I forgot to add the variable for the Multipart file and I mistakenly used the RequestBody which is reserved keyword in spring.
Hope it helps.
I would suggest to create two separate endpoints. This splits and isolates the different functionality and reduces the complexity of your code. In addition testing would be easier and provides better readability.
Your client actually has to know which variable to use. So just choose different endpoints for your request instead of using different variables for the same endpoint.
#PostMapping(value="/movies-file-upload", consumes = {"multipart/form-data"})
public void postMoviesFile(#RequestPart(value = "moviesFile") MultipartFile movieFile ) {
LOGGER.info("Movies File Upload {}", movieFile);
}
#PostMapping(value="/movies-upload", consumes = {"application/json"})
public void postMoviesJson( #RequestPart String movieJson) {
LOGGER.info("Movies Json Body {}", movieJson);
}

parameter passing for http.post in Angular calling java web API

I have a weird situation that may be because I missed something that I didn't realized or know.
I am creating a simple login UI using Angular and call the Web API created in java.
The java web API function is as follows
#RequestMapping(value = "/logon", method = RequestMethod.POST, produces = {"application/json"})
#ResponseBody
public String logon(
#RequestParam(value = "userID", required = true) String userID,
#RequestParam(value = "password", required = true) String password,
HttpServletRequest request)
Now if I use the http.post as follows
login(username: string, password: string) {
return this.http.post(this.url+"/security/logon/",
JSON.stringify({ userID: username, password: password }) )
Then I get the following error in the Google Chrome browser:
POST http://localhost:8080/logon/ 400 (Required String parameter 'userID' is not present)
But if I change the code as follows:
login(username: string, password: string) {
var usrpwd = "userID=" + username + "&password=" + password;
return this.http.post(this.url+"/security/logon?"+usrpwd, usrpwd )
It work perfectly.
Am I missing something? Why the second parameter of http.post that should be the parameter passed not seems to be working?
Thanks in advance for any reply or feedback.
You are defining your endpoint url with two mandatory parameters, and such parameters must be in the url (check here), so when you make a request to your endpoint, the url must be :
http://localhost:8080/logon?userID=yourUserId&password=yourUserPassword
In the first implementation you are not adding the query parameters to the url so the request is made to the url http://localhost:8080/logon/ as it doesn't have the required parameters, your web tier is returning the 400 http code, which implies a bad request (because again, your url doesn't contains the required parameters).
constructor(private http:HttpClient){
}
login(usrName,usrPwd){
let body = {userName:usrName, userPwd:usrPwd};
this.http.post("http://localhost:5000/security/login", body)
.subscribe(
res => console.log(res),
error => console.log(error)
)
}

"status":415,"error":"Unsupported Media Type"

I have following controller:
#PostMapping(value = {"/test/set_timeout"})
#ResponseBody
public void setAlertTimeout(#RequestBody Long timeout) {
and I make following request in postman:
text from error:
{"timestamp":1495560868392,"status":415,"error":"Unsupported Media Type","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'multipart/form-data;boundary=----WebKitFormBoundary9IbVq5JAKxCYUs7P;charset=UTF-8' not supported","path":"/test/set_timeout"}
What the reason of the problem and how to send request correct?
P.S.
If to use
public static class LongWrapper{
private long timeout;
public long getTimeout() {
return timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
}
instead of Long and pass json({"timeout":"2"}) - it works correct
When reading json your content type should be application/json.
if you need to use application/json type and write below code in controller method :-
#RequestMapping(method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
public RestResponse save(#RequestParam("timestamp") String timestamp, #RequestParam("status") String status) {
#RequestBody is used to deserialize JSON to POJO. If you need to submit form data to the controller try something like
#PostMapping(value = {"/test/set_timeout"})
#ResponseBody
public void setAlertTimeout(HttpServletRequest request) {
Long l = request.getParameter("timeout");
// continue
}
In your request you are sending the timeout property in a multipart/form-data body.
In postman, select option 'raw' for the body and set the content type to application/json. Then enter the following as body content:
{timeout: 4}
You should also set the consumes MediaType on your controller. (Although it could be that it automatically resolves to json) For example:
#PostMapping(value = {"/test/set_timeout"}, consumes = {MediaType.APPLICATION_JSON_VALUE})
There is no need to add a content-type header manually. You are overriding the value set by Postman. Just select form-data in POST request and send your request to see if it works.

Categories