How to automatically validate rest parameters in #RestController? - java

I want to automatically validate REST parameters on a spring REST service.
I tried it using #Valid #NotNull, but the rest request does not get rejected automatically, but the dao method is executed with null argument. Why?
#RestController
public class RestController {
#RequestMapping(value = "/")
public Boolean getResponse(#Valid #NotNull #Length(max = 20) String username) {
return daoService.lookup(username); //is executed if username = null
}
}
How can I get automatically a returned HTTP error, eg 400?

Here is an example of validation on request parameters..
public ResponseEntity<AgencyResource> saveAgency(
#Valid #RequestBody AgencyResource agencyResource) {
return new ResponseEntity<AgencyResource>(agencyResource, HttpStatus.OK);
}
This is from the POST http://www.leveluplunch.com/java/tutorials/017-validate-spring-rest-webservice-request/
Hope this helps.
Thanks,
Paul

Related

How to send a 401 as unauthorized user and a 404 if a database query returned a null object in a Spring boot webapp REST API controller?

I'm using a bit of a personalized security back-end due to the nature of the app and was trying out how to implement a few simple error returns in my REST API controller. It's simple enough to do in a html page controller like I have in the following:
#Controller
public class HomeController {
#Autowired
private UserService userService;
#GetMapping("/home.html")
public String home(Model model) {
String redirect = "home";
if(!userService.getCurrentUser().isCanAccessService()) {
redirect = "unauthorized";
}
return redirect;
}
}
I can easily just redirect it to the unauthorized page that I made since I'm returning the string value here. However, when I go to a REST API it's not as simple:
#RestController
public class bagelController {
#Autowired
private bagelService bagelService;
#Autowired
private UserService userService;
#GetMapping("/rest/bagel/search")
public Bagel searchBagel (#RequestParam(value = "bagel", required = false) String bagel,
#RequestParam(value = "bagelInd", required = false, defaultValue = "1") int bagelInd) {
Bagel bagel;
if(!userService.getCurrentUser().isBagelEditAccess()) {
bagel = null;
// I want to return a 401 or direct to my unathorized page if I get an invalid user here.
}
else {
bagel = bagelService.getbagel(bagel, bagelInd);
// if my bagel object returns null, I want to return a 404 or direct to a 404 not
found page here.
}
return bagel;
}
You can have a ControllerAdvice which handles exceptions and their HTTP return code. Then you can annotate a method in it the following way for example:
#ExceptionHandler(NoSuchEntityException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
This will return a 404 code every time it encounters a NoSuchEntityException (custom exception). So you can throw such an exception when you check if an entity is null. You can use the same thing for 401 or any other HTTP code as well.
One way to do this.
#GetMapping("/rest/bagel/search")
public ResponseEntity<Bagel> searchBagel (#RequestParam(value = "bagel", required = false) String bagel,
#RequestParam(value = "bagelInd", required = false, defaultValue = "1") int bagelInd) {
Bagel bagel = null;
if(!userService.getCurrentUser().isBagelEditAccess()) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
else {
bagel = bagelService.getbagel(bagel, bagelInd);
if(bagel == null) {
return ResponseEntity.notFound().build();
}
}
return ResponseEntity.ok(bagel);
}
You can create custom exceptions within your application for this scenario like BagelNotFoundException and UnauthorizedException. Both these custom exception classes can extend Exception class or more specific classes from java exception hierarchy. You can annotate these custom exception classes with #ResponseStatus annotation to provide the http status code that should be sent in the response.
Next, you need to throw the objects of these exceptions within your controller.
Once this exception is thrown, an exception handler should be present within your application to take care of these exceptions. The same can be defined using #ControllerAdvice and #ExceptionHandler within your custom exception handler classes.
This way you'll be able to send appropriate response to the client, and the client application needs to redirect the user to error pages based on the response code received.
Hope this helps!

What is the difference between #RequestMapping and #PostMapping

in the below example, I am trying to undersatnd the difference between #RequestMapping and #PostMapping.
For #RequestMapping:
when i do the POST request:
http://localhost:8085/call1/initparam1?val=1111 via postman, it executes correctly.
but when its is proceeded by by GET request
http://localhost:8085/call1/getparam1
i do not get 1111 as a result.
For #PostMapping, when i do the POST request:
http://localhost:8085/call1/initparam2/1999 via postman, it executes correctly.
but when its is proceeded by by GET request
http://localhost:8085/call1/getparam1
i do not get 1999 as a result.
please explain to me what is the difference between using both annotations, as i spent time googling and researching but i could not figure out why the first example is not working.
Controller1
#Controller
#ResponseBody
#RequestMapping("/call1")
public class Call1 {
public String str = "inti";
#RequestMapping(value = "/initparam1", method = RequestMethod.POST)
public void initparam1(#RequestParam(value = "val") String val) {
this.str = val;
}
#PostMapping(value = "/initparam2/{val}")
public void initparam2(#PathVariable String val) {
this.str = val;
}
#RequestMapping("/getparam1")
#ResponseBody
public String getParam1() {
return this.str;
}
}
From the #PostMapping docs :
Specifically, #PostMapping is a composed annotation that acts as a shortcut for #RequestMapping(method = RequestMethod.POST).
So it is only convenience annotation that is more "verbose" and indicates that method annotated with it is used for handling POST HTTP requests.
I have just checked your controller methods with 2.1.4 spring boot version and your scenarios work as expected so there has to be something wrong in your configuration or the way you are sending requests.

Spring boot rest controller not converting request body to custom object

I have spring boot application which used spring rest controller .
This is the controller , below is the response an getting. Am using postman tool for sending request to this controller. And am sending content type as application/json
#RequestMapping(value = "/test", method = RequestMethod.POST)
public String test(#RequestBody WebApp webapp, #RequestBody String propertyFiles, #RequestBody String) {
System.out.println("webapp :"+webapp);
System.out.println("propertyFiles :"+propertyFiles);
System.out.println("propertyText :"+propertyText);
return "ok good";
}
2018-03-21 12:18:47.732  WARN 8520 --- [nio-8099-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: Stream closed
This is my postman request
{
"webapp":{"webappName":"cavion17","path":"ud1","isQA":true},
"propertyFiles":"vchannel",
"propertytText":"demo property"}
I tried by removing the RequestBody annotation, then able to hit the service , but param objects are received as null.
So please suggest how to retrieve objects in the restcontroller?
You cannot use multiple #RequestBody annotations in Spring. You need to wrap all these in an object.
Some like this
// some imports here
public class IncomingRequestBody {
private Webapp webapp;
private String propertryFiles;
private String propertyText;
// add getters and setters here
}
And in your controller
#RequestMapping(value = "/test", method = RequestMethod.POST)
public String test(#RequestBody IncomingRequestBody requestBody) {
System.out.println(requestBody.getPropertyFiles());
// other statement
return "ok good";
}
Read more here
Passing multiple variables in #RequestBody to a Spring MVC controller using Ajax
Based on the sample postman payload you gave, you will need:
public class MyObject {
private MyWebapp webapp;
private String propertyFiles;
private String propertytText;
// your getters /setters here as needed
}
and
public class MyWebapp {
private String webappName;
private String path;
private boolean isQA;
// getters setters here
}
Then on your controller change it to:
#RequestMapping(value = "/test", method = RequestMethod.POST)
public String test(#RequestBody MyObject payload) {
// then access the fields from the payload like
payload.getPropertyFiles();
return "ok good";
}

Spring Boot REST: #DeleteMapping that consuming form_urlencoded not work as expect

I'm using Spring boot 1.4.0, Consider below code in a #RestController, what I expect is, the server side will receive a http body with form_urlencoded content type, but unfortunately it demands me a query parameter type with email and token. What's the problem here and how to fix?
#DeleteMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void removeAdmin(#RequestParam(value = "email") String email, #RequestParam(value = "token") String token) {
//...
}
#DeleteMapping is only a convenience extension the provides #RequestMapping(method=DELETE) It will not handle request paramters. You will still have to map those in the controllers method signature if you need the data to perform the work.
Since you want a body, You could create an object and mark it as #RequestBody:
public class DeleteBody {
public String email;
public String token;
}
public void removeAdmin(#RequestBody DeleteBody deleteBody) {
...
}

How to validate request parameter if it is not a bean in spring MVC?

Below is a POST end point in my spring MVC REST service. I want to use spring validation frame work to make sure that list I receive is not empty. How do I do it? Do I have to provide wrapper bean to around listOfLongs?
#RequestMapping(value = "/some/path", method = RequestMethod.POST)
#ResponseBody
public Foo bar(#Valid #NotEmpty #RequestBody List<Long> listOfLongs) {
/* if (listOfLongs.size() == 0) {
throw new InvalidRequestException();
}
*/
// do some useful work
}
What should be the Request Body?
1) [123,456,789]
2) { listOfLongs : [123,456,789]}
Providing a wrapper bean is a good practice.
class LongList {
#NotEmpty
private List<Long> listOfLongs;
// Setters and Getters ...
}
Then, the Request Body should be { listOfLongs : [123,456,789]}
#RequestMapping(value = "/some/path", method = RequestMethod.POST)
#ResponseBody
public Foo bar(#Valid #RequestBody LongList listOfLongs) {
// do some useful work
}

Categories