neither #RequestBody nor #RequestParam work - java

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");
}

Related

What is the DTO pattern when you have multiple method calls but different request bodies?

I was told that when building RestFul APIs, use nouns and not verbs. I was also told that each DTO gets its own controller. What does my DTO look like in the case that "edit property" and "add property" methods require two different request bodies?
#RestController
#RequestMapping(value = "/property")
public class PropertyController{
#RequestMapping(method = RequestMethod.GET)
//get properties method definition goes here
#RequestMapping(method = RequestMethod.PUT)
// edit property method definition goes here
#RequestMapping(method = RequestMethod.POST)
// add property method definition goes here
}
edit property requires:
{
"userloginid": "3333",
"propertyname": "My edited property name",
"propertyid": "1"
}
add property requires:
{
"userloginid": "3333",
"propertyname": "My new property name"
}
/users/{loginid}/properties/{propertyid} - please consider this endpoint as it is more RESTful
For PUT updates you can consider the endpoint above, and get your userloginid and propertyid from the Path Variables.
In the PUT request PUT /users/3333/properties/1 I would send the following Request Body:
{
"propertyname": "My edited property name"
}
For POST requests to create a new property I would consider sending a Request to this URL /users/{loginid}/properties/ with the same Request Body structure:
{
"propertyname": "My new property name"
}
As you can see I am not sending any ID in the POST request as ID generation for stored objects should happen in the DAO.
The client will receive the ID of the created/updated property in the Response Body:
{
"propertyname": "My updated/created property name",
"propertyid": "1"
}
So from the above examples, I would create two DTOs for handling the output and input situations.
From your json data, your DTO might be
PropertyDTO{
long userloginid;
String propertyname;
long propertyid;
}
So your API will look like
#RestController
#RequestMapping(value = "/property")
public class PropertyController{
#RequestMapping(method = RequestMethod.GET)
public PropertyDTO get(){
return null;
}
#RequestMapping(method = RequestMethod.PUT)
public Boolean update(#RequestBody PropertyDTO dto){
return true;
}
#RequestMapping(method = RequestMethod.POST)
public Boolean insert(#RequestBody PropertyDTO dto){
return true;
}
}
You should create the same DTO with these three properties (userloginid, propertyname, propertyid). But you have to consider that the propertyid can be null. Then when the request paramters are sent, if propertyid is not sent, the parser will create an instance of DTO with propertyid=null; else the parser will create an instance of DTO with propertyid=value_passed_in parameter.

How to get parameters from RequestBody in Spring REST controller method

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

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"
}

How to pass Array of Objects as parameter in Java swagger UI?

I need to pass an array of objects as parameter in java Swagger UI. I am unable to do it. However I can easily pass array of string as parameters. But cant figure out how to pass array of objects nested inside another class or DTO. I am using Java Spring and developing RESTful webservice.
Below is my 1st DTO that is passed as ModelAttribute in my REST api as parameter:
public class BusinessDetailsDTO {
private BusinessMembersDTO[] businessMembers;
public BusinessMembersDTO[] getBusinessMembers() {
return businessMembers;
}
public void setBusinessMembers(BusinessMembersDTO[] businessMembers) {
this.businessMembers = businessMembers;
}
}
Below is 2nd DTO whose array is made property in 1st DTO:
public class BusinessMembersDTO {
private String ownerRole;
private String businessLocation;
private Long memberId;
//getter setter below
}
Below is the controller method definition:
#ApiOperation(value = "Add business info")
#RequestMapping(method = RequestMethod.POST, value = "/addBusinessInfo", produces={"application/json"})
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success", response = ResponseEntity.class),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 403, message = "Forbidden"),
#ApiResponse(code = 404, message = "Not Found"),
#ApiResponse(code = 500, message = "Failure") })
public ResponseEntity<BusinessDetails> addBusinessInfo(#RequestParam(
#ModelAttribute BusinessDetailsDTO businessDetailsDTO) {
My problem is, I am unable to figure out how should I pass values ownerRole which is present in BusinessMembersDTO.
The swagger UI shows Array[] in DataType column next to businessMembers members field.
Plz help.
Try to put BusinessDetailsDTO directly as response in ApiResponse as below
#ApiResponse(code = 200, message = "Success", response = BusinessDetailsDTO.class)

Pass multiple parameters to rest API - Spring

I am trying to figure out if it is possible to pass a JSON object to rest API, Or pass a multiple parameters to that API ? And how to read these parameters in Spring ? Lets assume that the url looks like the below examples :
Ex.1 http://localhost:8080/api/v1/mno/objectKey?id=1&name=saif
Is it valid to pass a JSON object like in the url below ?
Ex.2 http://localhost:8080/api/v1/mno/objectKey/{"id":1, "name":"Saif"}
Questions:
1) Is it possible to pass a JSON object to the url like in Ex.2?
2) How can we pass and parse the parameters in Ex.1?
I tried to write some methods to achieve my goal, but could not find the right solution?
I tried to pass JSON object as #RequestParam
http://localhost:8080/api/v1/mno/objectKey?id=1 There was an unexpected error (type=Unsupported Media Type, status=415). Content type 'null' not supported
http://localhost:8080/api/v1/mno/objectKey/id=1 There was an unexpected error (type=Not Found, status=404). No message available
http://localhost:8080/api/v1/mno/objectKey/%7B%22id%22:1%7D There was an unexpected error (type=Not Found, status=404). No message available
#RequestMapping(value="mno/{objectKey}",
method = RequestMethod.GET,
consumes="application/json")
public List<Book> getBook4(#RequestParam ObjectKey objectKey) {
...
}
I tried to pass the JSON object as #PathVariable
#RequestMapping(value="ghi/{objectKey}",method = RequestMethod.GET)
public List<Book> getBook2(#PathVariable ObjectKey objectKey) {
...
}
I created this object to hold the id parameter and other parameters like name , etc ....
class ObjectKey{
long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
(1) Is it possible to pass a JSON object to the url like in Ex.2?
No, because http://localhost:8080/api/v1/mno/objectKey/{"id":1, "name":"Saif"} is not a valid URL.
If you want to do it the RESTful way, use http://localhost:8080/api/v1/mno/objectKey/1/Saif, and defined your method like this:
#RequestMapping(path = "/mno/objectKey/{id}/{name}", method = RequestMethod.GET)
public Book getBook(#PathVariable int id, #PathVariable String name) {
// code here
}
(2) How can we pass and parse the parameters in Ex.1?
Just add two request parameters, and give the correct path.
#RequestMapping(path = "/mno/objectKey", method = RequestMethod.GET)
public Book getBook(#RequestParam int id, #RequestParam String name) {
// code here
}
UPDATE (from comment)
What if we have a complicated parameter structure ?
"A": [ {
"B": 37181,
"timestamp": 1160100436,
"categories": [ {
"categoryID": 2653,
"timestamp": 1158555774
}, {
"categoryID": 4453,
"timestamp": 1158555774
} ]
} ]
Send that as a POST with the JSON data in the request body, not in the URL, and specify a content type of application/json.
#RequestMapping(path = "/mno/objectKey", method = RequestMethod.POST, consumes = "application/json")
public Book getBook(#RequestBody ObjectKey objectKey) {
// code here
}
you can pass multiple params in url like
http://localhost:2000/custom?brand=dell&limit=20&price=20000&sort=asc
and in order to get this query fields , you can use map like
#RequestMapping(method = RequestMethod.GET, value = "/custom")
public String controllerMethod(#RequestParam Map<String, String> customQuery) {
System.out.println("customQuery = brand " + customQuery.containsKey("brand"));
System.out.println("customQuery = limit " + customQuery.containsKey("limit"));
System.out.println("customQuery = price " + customQuery.containsKey("price"));
System.out.println("customQuery = other " + customQuery.containsKey("other"));
System.out.println("customQuery = sort " + customQuery.containsKey("sort"));
return customQuery.toString();
}
Multiple parameters can be given like below,
#RequestMapping(value = "/mno/{objectKey}", method = RequestMethod.GET, produces = "application/json")
public List<String> getBook(HttpServletRequest httpServletRequest, #PathVariable(name = "objectKey") String objectKey
, #RequestParam(value = "id", defaultValue = "false")String id,#RequestParam(value = "name", defaultValue = "false") String name) throws Exception {
//logic
}
Yes its possible to pass JSON object in URL
queryString = "{\"left\":\"" + params.get("left") + "}";
httpRestTemplate.exchange(
Endpoint + "/A/B?query={queryString}",
HttpMethod.GET, entity, z.class, queryString);

Categories

Resources