Should I use #RequestBody or #RequestParam for PatchMapping - java

I was working on a SpringBoot Rest Api.
I've a Comment entity. I just want to update comment's text. So I decided to use patchmapping.
I'm beginner on SpringBoot. I'm learning by the training. My plan was to find the comment by given Id. And update the text as a given String parameter.
When I use this controller with #RequestBody. And send a String via postman.
#PatchMapping("/updateStatus/{id}")
public ResponseEntity<CommentDto> updateTheUserStatus(#PathVariable Long id, #RequestBody String text){
return ResponseEntity.ok(commentService.changeStatus(id, text));
}
The Postman gives 500 Internal Error. But in the Database the text field is changing as I want.
{
"timestamp": "2022-04-13T08:17:13.615+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/v1/api/comment/updateStatus/4"
}
When I use this controller with #RequestParam Postman Gives 400 Bad Request and nothing is changing.
#PatchMapping("/updateStatusMe/{id}")
public #ResponseBody ResponseEntity<CommentDto> updateTheUserStatus1(#PathVariable Long id, #RequestParam String text){
return ResponseEntity.ok(commentService.changeStatus(id, text));
}
{
"timestamp": "2022-04-13T08:19:41.391+00:00",
"status": 400,
"error": "Bad Request",
"path": "/v1/api/comment/updateStatusMe/4"
}
I know that probably I'm totally wrong. But I'm asking this question for you to learn what am I missing. Thank you for your help!

Related

Spring post json

I try to build my first api. I got problem when i want to register new user. The problem is when i want to send request from postman. I using also SwaggerUI, so when i use Post Request to my end point /registration in SwaggerUI by textfields always i got http status 201 so its works good. Problem is when i want to make Mock to this controller or when i want to send new user in postman request but not always. I show you in example
If i use postman -> post: localhost:8080/registration -> Raw -> JSON
{
"email": "testtest#gmail.com",
"id": 0,
"password": "Test1234567 ",
"username": "testtest"
}
Then i got message
{
"status": "BAD_REQUEST",
"timestamp": "01-03-2021 11:44:26",
"message": "Value cannot be empty!",
"debugMessage": null,
"subErrors": null
}
So its should be good because i used catch exception. But Value isnt empty, so whats happend?I dont know.
But when i go to x-www-form-urlencoded and there i put keys: email, username and password then, user is created!
Another, when im put this same info to Swagger then also my user is created.
Below i add my code from controller and test.
#Test
void shouldCreateNewUser() throws Exception {
UserRegistrationDto user = new UserRegistrationDto( null,"seba12345", "lelelele1908#gmail.com", passwordEncoder.encode("Respeck123"));
mockMvc.perform(post("/registration")
.header("header1", "1")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(user)))
.andExpect(status().isCreated());
}
#PostMapping("/registration")
public ResponseEntity<UserRegistrationDto> registerUser(UserRegistrationDto userRegistrationDto) {
HttpHeaders headers = new HttpHeaders();
userService.save(userRegistrationDto);
return new ResponseEntity<>(userRegistrationDto, headers, HttpStatus.CREATED);
}
You need #RequestBody in your controller method to tell Spring that you want the content of the request body:
#PostMapping("/registration")
public void post(#RequestBody MyDTO dto) {
...
}

Missing request param when it is included in body

I am posting a POJO where I get an error saying the field is not included.
Asset POJO
public class Asset {
private MultipartFile[] files;
private String name;
private String meta;
//Constructor/Getters n Setters
}
Resource Method
#PostMapping("asset")
public ResponseEntity uploadAsset(#RequestParam("asset") Asset asset) {
System.out.println(asset);
return new ResponseEntity(HttpStatus.ACCEPTED);
}
PostMan JSON Body
{
"asset" : {
"files": [
"#/home/Downloads/1.jpeg",
"#/home/Downloads/2.jpeg"
],
"name": "assetName",
"meta": "assetMeta"
}
}
PostMan JSON Response
{
"timestamp": "2019-10-29T20:46:19.536+0000",
"status": 400,
"error": "Bad Request",
"message": "Required Asset parameter 'asset' is not present",
"path": "/asset"
}
I don't understand why I get the Required Asset parameter 'asset' is not present message when I have it in the JSON body. Any ideas on this?
Use #RequestBody rather than #RequestParam
public ResponseEntity uploadAsset(#RequestBody Asset asset) {
Based on your payload, Spring is expecting an object that looks like this:
public class SomeClass {
private Asset asset;
}
Change your payload to look like this:
{
"files": [
"#/home/Downloads/1.jpeg",
"#/home/Downloads/2.jpeg"
],
"name": "assetName",
"meta": "assetMeta"
}
RequestParam
Annotation which indicates that a method parameter should be bound to a web request parameter.
RequestBody
Annotation indicating a method parameter should be bound to the body of the web request. The body of the request is passed through an HttpMessageConverter to resolve the method argument depending on the content type of the request. Optionally, automatic validation can be applied by annotating the argument with #Valid.
HttpMessageConverter
Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
You need to check converter dependency. because you using application/json.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
Q : Missing request param when it is included in body
A : Use #RequestBody annotation.
I tried #Jordans answer and the endpoint was called with all values set to null :(
Doing more research I came across this statement https://stackoverflow.com/a/51982230/2199102 and tried it out.
Combining #Jordans answer and then the annotation change, I was able to get the answer I wanted

How to change Status code in spring boot error response?

I am making a simple rest service that makes some http calls and aggregates data using RestTemplate.
Sometimes i get NotFound error and sometimes BadRequest errors.
I want to respond with the same status code to my client and Spring seems to have this mapping out of the box. the message is okay but the Status code is always 500 Internal Server error.
I Would like to map my status code to the one i am initially receiving
"timestamp": "2019-07-01T17:56:04.539+0000",
"status": 500,
"error": "Internal Server Error",
"message": "400 Bad Request",
"path": "/8b8a38a9-a290-4560-84f6-3d4466e8d7901"
}
i would like it to be this way
"timestamp": "2019-07-01T17:56:04.539+0000",
"status": 400,
"error": "Internal Server Error",
"message": "400 Bad Request",
"path": "/8b8a38a9-a290-4560-84f6-3d4466e8d7901"
}
It throws HttpClientErrorException.BadRequest or HttpClientErrorException.NotFound
my code is a simple endpoint :
#GetMapping("/{id}")
public MyModel getInfo(#PathVariable String id){
return MyService.getInfo(id);
}
You can create global exception handling with #ControllerAdvice annotation. Like this:
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = YourExceptionTypes.class)
protected ResponseEntity<Object> handleBusinessException(RuntimeException exception, WebRequest request) {
return handleExceptionInternal(exception, exception.getMessage(), new HttpHeaders(), HttpStatus.NOT_ACCEPTABLE, request);
}
}
When an exception is thrown, the handler will catch and transform it to the desired response. The original exception wont be propagated.
The accepted solution with the #ControllerAdvice is insufficient. That surely marks the response with the custom status code for the exception. It does, however, not return the wanted response body as JSON but as only simple string - the message from the exception.
To get the correct status code and the default error body the DefaultErrorAttributes can help.
#ControllerAdvice
public class PackedTemplateNotRecodableExceptionControllerAdvice extends ResponseEntityExceptionHandler {
#Autowired
private DefaultErrorAttributes defaultErrorAttributes;
#ExceptionHandler(PackedTemplateNotRecodableException.class)
public ResponseEntity<Object> handlePackedTemplateNotRecodableException(final RuntimeException exception, final WebRequest webRequest) {
// build the default error response
webRequest.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpStatus.BAD_REQUEST.value(), RequestAttributes.SCOPE_REQUEST);
final Map<String, Object> errorAttributes = defaultErrorAttributes.getErrorAttributes(webRequest, ErrorAttributeOptions.defaults());
// return the error response with the specific response code
return handleExceptionInternal(exception, errorAttributes, new HttpHeaders(), HttpStatus.BAD_REQUEST, webRequest);
}
}
That way you'll receive the wanted error response, e.g. something like this:
{
"timestamp": "2019-07-01T17:56:04.539+0000",
"status": 400,
"error": "Internal Server Error",
"message": "400 Bad Request",
"path": "/8b8a38a9-a290-4560-84f6-3d4466e8d7901"
}
I have spent a lot of time looking into this issue, including solutions from answers here, which didn't work for me (or I didn't implement correctly).
I finally got a breakthrough. Instead of throwing a generic Exception such as throw new Exception(message), I created classes that extends the Exception class for the specific exception type - with their respective HTTP error codes and message
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class BadRequestException extends Exception{
public BadRequestException(String message) {
super(message);
}
}
In your application logic, you can now throw the Bad Request exception with a message like so throw new BadRequestException("Invalid Email"). This will result in an exception thrown thus :
{
"timestamp": "2021-03-01T17:56:04.539+0000",
"status": 400,
"error": "Bad Request",
"message": "Invalid Email",
"path": "path to controller"
}
You can now create other custom exception classes for the different exceptions you want, following the above example and changing the value parameter in the #ResponseStatus, to match the desired response code you want. e.g for a NOT FOUND exception #ResponseStatus (value = HttpStatus.NOT_FOUND), Java provides the different HTTP status codes via the HttpStatus enum.
For more context
I hope this is detailed enough and helps someone :)
Possible duplicate of Spring Resttemplate exception handling Your code needs a controller advice to handle the exceptions from the service it is calling.

Google Calendar insert event : JSON parse error through #RequestBody Spring

I try to insert an event to Google Calendar.
If I create my event with Java Object directly, using the Java client, I have no problem.
My problem is when I send my event in JSON to my controller: (The Content-type is application/json, and I use postman or command line to send it)
My JSON:
{
"summary": "Google I/O 2015",
"location": "800 Howard St., San Francisco, CA 94103",
"description": "A chance to hear more about Google\"s developer products.",
"start":{
"dateTime":"2018-04-07T17:00:00.000-04:00"
},
"end":{
"dateTime":"2018-04-07T18:00:00.000-04:00"
},
"attendees": [
{"email": "lpage#example.com"},
{"email": "sbrin#example.com"}
]
}
My controller:
#PostMapping(path = "/new/event/{calendarId}/{sendNotification}")
public ResponseEntity<?> newEvent(#NonNull #PathVariable String calendarId,
#NonNull #PathVariable boolean sendNotification,
#NonNull #RequestBody Event event) {
Event eventCreated = postEventService.postNewEvent(calendarId, sendNotification, event);
return new ResponseEntity<>(eventCreated, HttpStatus.CREATED);
}
And here is my error:
{
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Can not set com.google.api.services.calendar.model.EventDateTime field com.google.api.services.calendar.model.Event.start to java.util.LinkedHashMap; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not set com.google.api.services.calendar.model.EventDateTime field com.google.api.services.calendar.model.Event.start to java.util.LinkedHashMap (through reference chain: com.google.api.services.calendar.model.Event[\"start\"])",
"path": "/new/event/primary/true"
}
My problem is that the exact same JSON works perfectly on the Try this API in the documentation.
Thanks in advance for your help!
You can resolve this error by creating a custom JSON deserializer. In Spring Boot you can do that using #JsonComponent annotation.

How to insert a map<Integer, String> as #RequestParam with Postman

first of all, sorry for my english, it's not my main language, and I'm not sure the question is fully understandable.
I need to do some queries after receiving a Map as a #RequestParam of a Rest Web Service.
I'm trying to call the web service with Postman, and here is the full POST request
http://localhost:8080/CDRestApi/rest/cd/esibizione/getIdUdFromIstanzaMetadatoByMap/5/map?25=ALAN&26=IANESELLI
This is my WS code:
#RequestMapping(value = { "/getIdUdFromIstanzaMetadatoByMap/{id}/map" }, method = RequestMethod.POST, produces = {
MediaType.APPLICATION_JSON_VALUE })
#ResponseBody
public String selectIstanzaMetadato(#PathVariable Long id,
#RequestParam (value="map", required=true) Map<Integer,String> mapQuery) {
Integer key = 25;
System.out.println(mapQuery.get(key));
return mapQuery.get(key).toString();
}
And this is the postman answer:
{
"timestamp": 1505834218902,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.NullPointerException",
"message": "No message available",
"path": "/CDRestApi/rest/cd/esibizione/getIdUdFromIstanzaMetadatoByMap/5/map"
}
the System.out print is:
17:16:58,891 INFO [stdout] (default task-4) null
I suppose the mapQuery object has no value, because it is not correctly valorized
I have already seen those posts, but they were not useful to me:
Map<String, String> as #RequestParam in Spring MVC
and
Spring MVC + RequestParam as Map + get URL array parameters not working
Do I miss the correct Postman POST request ? or it is a problem of the webservice itself ?
I resolved this getting the map and converting it into a map.
Map<Integer, String> mappaQuery = mapQuery.entrySet().stream().collect(Collectors.toMap(e -> Integer.parseInt(e.getKey()), Map.Entry::getValue));
It is not efficient, but it worked for the test purposes of Postman call; in the real call of the WS, I can pass a proper Map object.

Categories

Resources