How to get parameters from RequestBody in Spring REST controller method - java

In a Spring Boot application I have the following method signature in a Controller:
#PostMapping(value="/borrow")
public ResponseEntity<Void> postBorrowBook(#RequestBody String personId,
#RequestBody String bookId) {
LOG.info(RESTController.class.getName() + ".postBorrowBook() method called.");
...
return new ResponseEntity<Void>(HttpStatus.OK);
}
I want to get the values of the two parameters from the RequestBody.
Can anyone let me know how this be done if the request I am making is JSON as follows:
{"personId":"207","bookId":"5"}
I am currently receiving:
{
"timestamp": "2018-06-17T20:59:37.330+0000",
"status": 400,
"error": "Bad Request",
"message": "Required request body is missing: public org.springframework.http.ResponseEntity<java.lang.Void> com.city2018.webapps.code.controller.RESTController.postBorrowBook(java.lang.String,java.lang.String)",
"path": "/rest/borrow/"
}
I already have the following working in a similar scenario for simple non-REST requests:
#RequestMapping(value="/borrow", method=RequestMethod.POST)
public String postBorrowBook(#RequestParam("personId") String personId,
#RequestParam("bookId") String bookId,
Model model) {
LOG.info(PersonController.class.getName() + ".postBorrowBook() method called.");

You can declare a POJO with 2 fields (personId and bookId) and change your signature as follow:
#PostMapping(value="/borrow")
public ResponseEntity<Void> postBorrowBook(#RequestBody RequestDTO requestBody) {
requestBody.getPersonId();
requestBody.getBookId();
...
}

First you should define a POJO class as:
public class BorrowBookEntity{
public String personId;
public String bookId;
}
Then you rely on spring to get the value as:
#PostMapping("/request")
public ResponseEntity<Void> postController(
#RequestBody BorrowBookEntity borrowBookEntity) {
...

You can also try another .
e.g. #RequestBodyParam
#RequestMapping(value = "/saveData", headers="Content-Type=application/json", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Boolean> saveData(#RequestBodyParam String source,#RequestBodyParam JsonDto json) throws MyException {
...
}
https://github.com/LambdaExpression/RequestBodyParam

Related

Spring #ResponseStatus returns empty response

In my Spring API I wanted to handle responses from operations like create, put and delete with Spring's annotation #ResponseStatus. Every endpoint works correctly but they always return empty response.
Why response from annotated endpoints is empty?
Controller:
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.http.HttpStatus;
#RestController
#RequestMapping(value = "/v1/portfolios")
public class PortfolioController {
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public void create(#RequestBody Portfolio resource) {
repo.save(resource);
}
#RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
#ResponseStatus(HttpStatus.OK)
public void delete(#PathVariable("id") String id) {
repo.removeById(id);
}
}
Why response from annotated endpoints is empty?
Because your methods return void (means without body). Status code - it's not a body.
You can try this to return response with message explicity:
#RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public ResponseEntity<String> delete(#PathVariable("id") String id) {
repo.removeById(id);
return new ResponseEntity<>("Your message here", HttpStatus.OK);
}
Instead of ResponseEntity<String> you can put ResponseEntity<YourCustomObject>and then return new ResponseEntity<>(yourCustomObject instance, HttpStatus.OK); It will be conver into JSON during response.
You also can make this:
#ResponseStatus(value = HttpStatus.OK, reason = "Some reason")
and in this case you will return something like this:
{
"timestamp": 1504007793776,
"status": 200,
"error": "OK",
"message": "Some reason",
"path": "/yourPath"
}

RestController with GET + POST on same method?

I'd like to create a single method and configure both GET + POST on it, using spring-mvc:
#RestController
public class MyServlet {
#RequestMapping(value = "test", method = {RequestMethod.GET, RequestMethod.POST})
public void test(#Valid MyReq req) {
//MyReq contains some params
}
}
Problem: with the code above, any POST request leads to an empty MyReq object.
If I change the method signature to #RequestBody #Valid MyReq req, then the post works, but the GET request fails.
So isn't is possible to just use get and post together on the same method, if a bean is used as input parameters?
The best solution to your problem seems to be something like this:
#RestController
public class MyServlet {
#RequestMapping(value = "test", method = {RequestMethod.GET})
public void testGet(#Valid #RequestParam("foo") String foo) {
doStuff(foo)
}
#RequestMapping(value = "test", method = {RequestMethod.POST})
public void testPost(#Valid #RequestBody MyReq req) {
doStuff(req.getFoo());
}
}
You can process the request data in different ways depending on how you receive it and call the same method to do the business logic.
#RequestMapping(value = "/test", method = { RequestMethod.POST, RequestMethod.GET })
public void test(#ModelAttribute("xxxx") POJO pojo) {
//your code
}
This will work for both POST and GET. (make sure the order first POST and then GET)
For GET your POJO has to contain the attribute which you're using in request parameter
like below
public class POJO {
private String parameter1;
private String parameter2;
//getters and setters
URl should be like below
/test?parameter1=blah
Like this way u can use it for both GET and POST
I was unable to get this working on the same method and I'd like to know a solution, but this is my workaround, which differs from luizfzs's in that you take the same request object and not use #RequestParam
#RestController
public class Controller {
#GetMapping("people")
public void getPeople(MyReq req) {
//do it...
}
#PostMapping("people")
public void getPeoplePost(#RequestBody MyReq req) {
getPeople(req);
}
}

neither #RequestBody nor #RequestParam work

I want to make a PUT call in spring.
this is my controller code:
#RequestMapping(value = "/magic", method = RequestMethod.PUT)
TodoDTO magic(#RequestBody String id){
return service.magic(id);
}
because i want to pass a id string in the call.
the problem is, i receive this
{
"timestamp": 1486644310464,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.NullPointerException",
"message": "{\n\t\"id\":\"589c5e322abb5f28631ef2cc\"\n}",
"path": "/api/todo/magic"
}
if i change the code like this:
#RequestMapping(value = "/magic", method = RequestMethod.PUT)
TodoDTO magic(#RequestParam(value = "id") String id){
return service.magic(id);
}
i receive
{
"timestamp": 1486644539977,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.bind.MissingServletRequestParameterException",
"message": "Required String parameter 'id' is not present",
"path": "/api/todo/magic"
}
i make the same call, a PUT at link http://localhost:8080/api/todo/magic
with the body
{
"id":"589c5e322abb5f28631ef2cc"
}
which is the id of one object in my db.
my question is, how can i achieve my goal? if i pass the param in the link, like api/todo/magic/589c5e322abb5f28631ef2cc, with #PathVariable, it works
Create your own custom class like below
Class Request
{
private String id;
//getter and setter
}
And change method to
#RequestMapping(value = "/magic", method = RequestMethod.PUT)
TodoDTO magic(#RequestBody Request request){
return service.magic(request.getId());
}
You can take id in url also and use #Pathvariable in method signature
#RequestMapping(value = "/magic/{id}", method = RequestMethod.PUT)
TodoDTO magic(#PathVariable String id){
return service.magic(request.getId());
}
When you're using #RequestBody String id it expects just a string:
"589c5e322abb5f28631ef2cc"
If you want to send an object with id field like
{
"id":"589c5e322abb5f28631ef2cc"
}
you should create a class with id field and modify method's signature to get this class instead of String.
While creating a wrapper class as suggested in the other answers will work, I think it may be possible to avoid this overhead and simply use a Map.
#RequestMapping(value = "/magic", method = RequestMethod.PUT)
TodoDTO magic(#RequestBody Map<String, String> data){
return service.magic(data.get("id");
}

Spring jackson deserialization and exception handling

I'm building a REST api in Spring and I have problems with my exception handling. I want to validate the full request and give information about the payload in one go.
Suppose my object is
public class StubJson {
private BigDecimal bigDecimalField;
#NotEmpty
private String stringField;
public void setBigDecimalField(BigDecimal bigDecimalField) { this.bigDecimalField = bigDecimalField; }
public String setStringField(String stringField) { this.stringField = stringField; }
}
And my controller is
#RestController
public class StubController {
#RequestMapping(value = "/stub", method = POST)
public void stub(#Valid #RequestBody StubJson stubJson) {}
}
The validation on this object is in a #ControllerAdvice that translates FieldError objects into translated errors for the end user.
#ResponseStatus(BAD_REQUEST)
#ResponseBody
#ExceptionHandler(value = MethodArgumentNotValidException.class)
public List<ErrorJson> processValidationError(MethodArgumentNotValidException ex) {}
If I pass in this json
{"bigDecimalField": "text", "stringField": ""}
I want a response like this
[
{
"field": "stringField",
"message": "Cannot be empty."
},
{
"field": "bigDecimalField",
"message": "Not a number."
}
]
If I do this I get a
com.fasterxml.jackson.databind.exc.InvalidFormatException
on the BigDecimalField which only contains information about only one field. The only option I see is passing it in as a String or writing custom validation annotations. Is there an easier way of achieving this?
You can use controller advice for this purpose. Declare a controller advice in your application, catch you expected exception, transform to you required response and return. Just remember controller advice will be applied to all of your controller.
#ControllerAdvice
public class ExceptionHandlerController {
#ExceptionHandler(InvalidFormatException.class)
#ResponseBody public String typeMismatchException(HttpServletRequest request, HttpServletResponse servletResponse, InvalidFormatException e ) {
String yourResponse = "";
return yourResponse;
}
}

How to get the object from an API REST with Spring MVC

I have a Spring MVC project and I have configured the jackson library to automatically transform the response (java object) to a json and it works in GET requests as following.
#RequestMapping(method = RequestMethod.GET)
public #ResponseBody Orders createOrder(Model model){
Orders orders = new Orders();
//Populate orders.....
return orders;
}
But when a I try to process the POST request and get the object from the json's request, I get the error "400 Bad Request" as Spring cannot create the object Orders from the json. I put the same json file that the GET method response, so I suppose that the file is well formatted.
#RequestMapping(method = RequestMethod.POST)
public #ResponseBody ResponseEntity<String> createOrder(#RequestBody Orders orders){
LOG.info(orders.toString());
return new ResponseEntity<String>("", HttpStatus.CREATED);
}
If I change the #RequestBody class to String (createOrder(#RequestBody String orders)), the POST request is well processed.
Do I have to create a mapper that maps the input json to the class Order?
UPDATE:
I have created the most simple example to try it and I receive the error 400. Exmaple:
Domain: Home.java
public class Home {
private String address = "Calle mármoles";
public Home(){
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Controller:
#RequestMapping(method = RequestMethod.POST)
public #ResponseBody ResponseEntity<String> createOrder2(#RequestBody Home orders){
return new ResponseEntity<String>("{}", HttpStatus.CREATED);
}
JSON (POST):
{
address: "Prueba"
}
[SOLVED]
I missed to put "" in the name of the parameter name.
[SOLVED]
I missed to put "" in the name of the parameter name.
Besides, the class has to have a default blank constructor in order to allow the jackson library creating the object.

Categories

Resources