Spring MVC - REST Api, keep getting 400 Bad Request when trying to POST - spring

i have a REST api service which should receive POST calls.
I'm using POSTMAN to test them, but i keep getting a 400 Bad Request Error, with no body, maybe i'm building bad my controller...
This is the controller
#PostMapping("/object/delete")
public ResponseEntity<?> deleteObject(#RequestBody long objectId) {
logger.debug("controller hit");
Object o = service.findByObjectId(objectId);
if(o!=null){
service.deleteObject(object);
return new ResponseEntity<>(HttpStatus.OK);
}
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Using #RequestBody i should send the request in JSON, in this way:
{
"objectId":100
}
But i get a 400 error, and the strange think is that my logger logger.debug("controller hit"); it's not printed in logs...

Sending { "objectId":100 } would result in receiving an object X with an objectId attribute in your java method.
If you just need to send an id, you can use #PathVariable
#PostMapping("/object/{id}/delete")
public ResponseEntity<?> deleteObject(#PathVariable("id") long objectId) {
Also, consider using DeleteMapping instead of PostMapping to delete an object.

Related

Testing JsonPatch in Spring Boot application

I'm trying out JsonPatch but ran into an issue when I try to test it. My REST controller looks like this:
#RestController
#RequestMapping("/v1/arbetsorder")
class ArbetsorderController {
#PatchMapping(path ="/tider/{tid-id}", consumes = "application/json-patch+json")
public ResponseEntity<Persontid> patchTid(#PathVariable("tid-id") Long id, #RequestBody JsonPatch patch) {
Persontid modifieradPersontid = personTidService.patchPersontid(id, patch);
return new ResponseEntity<>(modifieradPersontid, HttpStatus.OK);
}
}
I'm testing this endpoint using this method:
#Test
void patchPersontid() {
String body = "{\"op\":\"replace\",\"path\":\"/resursID\",\"value\":\"Jonas\"}";
given()
.auth().basic(user, pwd)
.port(port)
.header(CONTENT_TYPE, "application/json-patch+json")
.body(body)
.when()
.patch("/v1/arbetsorder/tider/3")
.then()
.assertThat()
.statusCode(HttpStatus.OK.value())
.body("resursID", equalTo("Jonas"));
}
But this test fails because the status code is 500 instead of 200. I'm not reaching my endpoint.
I have tried using regular JSON and then everything works fine.
Can anyone help me understand why I'm not reaching my endpoint when using JsonPatch?
Apparently the body I send to the endpoint has to be an array in order for it to be converted to a JsonPatch object. Changing my body to:
String body = "[{\"op\":\"replace\",\"path\":\"/resursID\",\"value\":\"Jonas\"}]";
solved my problem!

401 and 404 error when validation error happen

today I faced an error, I did solve it, but I am having trouble understanding why the error happens that way.
so I was just experimenting with Spring Boot and building a sample application with #RestController, I used "spring-boot-starter-validation" as my user input validation.
my restcontroller endpoint method is like this
#PostMapping("/newEmployee")
#ApiOperation(value = "creates a new employee",
response =EmployeeDTO.class)
public EmployeeDTO newEmployee(#Valid #RequestBody EmployeeDTO newEmployee) {
employeeValidator.validateEmployeeDTO(newEmployee);
return employeeManagementBO.saveOrUpdateEmployee(newEmployee);
}
then I defined a handler method like below
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(MethodArgumentNotValidException.class)
//note that if #ResponseBody is not annotated here, you may get weird error like 404 or 401
#ResponseBody
public Map<String, String> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
System.out.println("error caught...");
return errors;
}
so if I remove the #ResponseBody, when I test the endpoint with PostMan, I get error code 404 (but I did debug and saw that the handler method ran), which means that after errors were returned I get 404 error.
however, if I annotate it with #ResponseBody, then everything work as expected and I get a JSON response body back with the expected error structure in a map.
so I can make sense of the #ResponseBody making my return value as the Response of my endpoint, but my question is how does the 404 error happen? also, if I make the return type a String, then I get a 405 error (saying 'POST' is not allowed).
does anybody knows without the #ResponseBody, what is happening? and is there any usecase for not annotating it with #ResponseBody?

Spring Reactive API response blank body while transforming Mono<Void> to Mono<Response>

I have a service call that returns Mono. Now while giving API response to user I want to send some response. I tried with flatMap as well as a map but it won't work. It gives me an empty body in response.
Here the code sample
//Service Call
public Mono<Void> updateUser(....) {
.......... return Mono<Void>...
}
//Rest Controller
#PostMapping(value = "/user/{id})
public Mono<Response> update(......) {
return service.updateUser(....)
.map(r -> new Response(200, "User updated successfully"));
}
When I hit the above API it gives me an empty body in response. Can anyone please help me to get body in API response?
Thank you
if you wish to ignore the return value from a Mono or Flux and just trigger something next in the chain you can use either the then operator, or the thenReturn operator.
The difference is that then takes a Mono while thenReturn takes a concrete value (witch will get wrapped in a Mono.
#PostMapping(value = "/user/{id})
public Mono<Response> update(......) {
return service.updateUser(....)
.thenReturn(new Response(200, "User updated successfully"));
}

Error Handling in REST API: Best Practice

I am trying to develop a rest api that basically return information about country. So my url will be something like
http://myrestservice/country/US
so when a request come with valid country, my rest service will prepare information for that country and prepare object called countryInfo and return this as
return ResponseEntity.status(200).body(countryInfo);
now say user send request as
http://myrestservice/country/XX. In this case since XX is not valid country i have send response. I read in different place and most of them only explain about status code. My question is what is the best way to return error.
return ResponseEntity.status(404).body("Invalid Country");
return ResponseEntity.status(404).body(myobject); //here myObject will be null.
Prepare a Class say MyResponse.java as below.
public class MyResponse {
private String errorCode;
private String errorDescription;
private CountryInfo countryInfo
}
And return this object no matter if there are error or not. If there is error set errorCode and errorDescription field with proper value and set countryInfo to null and for no error set errorCode and errorDescription as empty and countryInfo with data.
Which of the above option is considered standard way to handle error.
You should indeed return a 404, but what you return in the body depends on you.
Some people just return a html response with some human-readable information, but if you want your API client to get some more information about why the 404 happened, you might also want to return JSON.
Instead of using your own format, you should use the standard application/problem+json. It's a very simple format:
https://www.rfc-editor.org/rfc/rfc7807
You could use #ControllerAdvice to handle exceptions:
Your endpoint needs to identify the error and throw an error:
#RequestMapping("/country/{code}")
public ResponseEntity<Country> findCountry(String code) {
Country country = this.countryRepository(code);
if(country == null) throws new IllegalArgumentException("Invalid country code: " + code);
return ResponseEntity.status(200).body(country);
}
Then you create a class that will handle the exceptions of your endpoints:
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = { IllegalArgumentException.class })
protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest request) {
String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse,
new HttpHeaders(), HttpStatus.CONFLICT, request);
}
}
There you have to define the status code to indicate user what kind of error was generated, 409 in order to indicate there was a conflict.
Also, there you can define a body including further information, either a string or a custom object containing an error message and description you offer to the client thru documentation.
You can use Zalando Problem, a library that implements application/problem+json.
https://github.com/zalando/problem
https://thecodingduck.blogspot.com/2023/01/best-practices-for-rest-api-design.html#standard_error

get error HTTP Status 405 for rest web service POST method

I load at browser as
localhost:8080/picking/addPick get error HTTP Status 405 - Request method 'GET' not supported.
What wrong?Hope advice thanks
#Controller
#RequestMapping("/picking")
public class PickerController {
#RequestMapping(method = RequestMethod.GET)
public #ResponseBody ArrayList getAllPickingItems()throws SQLException, ClassNotFoundException{
//....
}
#RequestMapping(value="/addPick",method=RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public Boolean add(#RequestBody PickingInfo pickingInfo,HttpServletResponse response){
try{
Boolean success = pickerMethod.addPickingInfo(pickingInfo);
response.setHeader("addPickingInfo", success+"");
return true;
}catch(Exception ex){
System.out.println(ex.getMessage());
}
return false;
}
}
You limited URI/picking/addPick to POST requests :
#RequestMapping(value="/addPick",method=RequestMethod.POST)
When you try to open that URI from your browser you're sending a GET request, not a POST. If you want to be able to access /picking/addPick from your browser you must either :
remove the restriction method=RequestMethod.POST
allow explicitely GET requests : method = { RequestMethod.POST, RequestMethod.GET }
If you just want to test a POST method, use SoapUI. Simply create a "New REST Project", paste your service URI, and send any type of HTTP Request you want.
You have mapped /addPick to the add method only for POST requests. Therefor GET is not mapped to anything (and in this case there is no point in mapping get to the method since you are also using #RequestBody)

Resources