Is it possible to use validators for validation request parameters from javax.validation.constraints package in any way? I.e. like the following:
#Controller
public class test {
#RequestMapping("/test.htm")
public String test(#RequestParam("name") #NotNull String name)
{
return "index";
}
}
Use this way:
public class Comment{
#NotEmpty
#Length(max = 140)
private String text;
//Methods are omitted.
}
Now use #Valid in controller
#Controller
public class CommentController {
#RequestMapping(value = "/api/comment", method = RequestMethod.POST)
#ResponseBody
public Comment add(#Valid #RequestBody Comment comment) {
return comment;
}
}
When you are applying #Valid for Comment object in your cotroller,it will apply the validation mentioned in Comment class and its attribute like
#NotEmpty
#Length(max = 140)
private String text;
You can also check this out for little alternate way of doing:
http://techblogs4u.blogspot.in/2012/09/method-parameter-validation-in-spring-3.html
you can try this
#Controller
public class test {
#RequestMapping("/test.htm")
public String test(#RequestParam(value="name",required=true) String name)
{
return "index";
}
}
Related
In my request body DTO, I want specific fields to be required for one of the API but not for another.
My request body:
#Data
class MyClass {
#NotNull
private String fullName;
#NotNull
private String firstName;
#NotNull
private String lastName;
}
I want fullName to be required for /api/v1 but not for /api/v2. I am using #Valid on request body from javax.validation.
You have to use Spring's #Validated, which is a variant of JSR-303's #Valid. This is used at the method-level:
Controller:
#RequestMapping(value = "apiV1Method")
public String apiV1Method(#Validated(Account. ValidationAPI1.class) MyClassDTO myClassDTO) {...}
#RequestMapping(value = "apiV2Method")
public String apiV2Method(#Validated(Account. ValidationAPI2.class) MyClassDTO myClassDTO) {...}
Object:
class MyClassDTO {
public interface ValidationAPI1 {}
public interface ValidationAPI2 {}
#NotNull(groups = {ValidationAPI1.class})
private String fullName;
#NotNull(groups = {ValidationAPI1.class, ValidationAPI2.class})
private String firstName;
#NotNull(groups = {ValidationAPI1.class, ValidationAPI2.class})
private String lastName;
...
}
I am trying to apply validations on my SPRING REST-API but i am getting this exception:
Apr 10, 2020 12:05:26 PM org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver doResolveHandlerMethodExceptionWARNING: Failed to invoke #ExceptionHandler method: public com.luv2code.springdemo.exceptionhandling.RestFieldErrorValidation com.luv2code.springdemo.exceptionhandling.GlobalExceptionHandler.processValidationError(org.springframework.web.bind.MethodArgumentNotValidException)org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class com.luv2code.springdemo.exceptionhandling.RestFieldErrorValidation at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:226)
Entity Class:
#Entity#Table(name="customer")
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="first_name")
#NotNull(message = "Firstname is necessary")
#Size(min=1,message="This field is required")
private String firstName;
#Column(name="last_name")
#NotNull(message = "Lastname is necessary")
#Size(min=1,message="This field is required")
private String lastName;
#Column(name="email")
private String email;
// getters and setters
}
FieldValidation Handler classes:
public class RestFieldError {
private String field;
private String message;
public RestFieldError() {
}
// getters and setters
}
and
public class RestFieldErrorValidation {
private List<RestFieldError> fieldErrors = new ArrayList<>();
public RestFieldErrorValidation() {
}
public void addFieldError(String path, String message) {
RestFieldError error = new RestFieldError(path, message);
fieldErrors.add(error);
}
}
RestController Code:
#RestController
#RequestMapping("/api")
public class CustomerRestController {
// autowire the CustomerService
#Autowired
private CustomerService customerService;
#InitBinder
public void initBinder(WebDataBinder dataBinder) {
System.out.println("Entered init binder");
StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
dataBinder.registerCustomEditor(String.class, stringTrimmerEditor);
}
// add the mapping for POST/customers (add a new customer)
#PostMapping("/customers")
#ResponseBody
public Customer addCustomer(#Valid #RequestBody Customer theCustomer) {
System.out.println("theCustomer :"+theCustomer.getFirstName());
theCustomer.setId(0);
customerService.saveCustomer(theCustomer);
return theCustomer;
}
}
Exception handler Class:
#ControllerAdvice
public class GlobalExceptionHandler {
// Adding Validation Support on REST APIs--------------------------------------------------------->
private MessageSource messageSource;
#Autowired
public GlobalExceptionHandler(MessageSource messageSource) {
this.messageSource = messageSource;
}
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public RestFieldErrorValidation processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
return processFieldErrors(fieldErrors);
}
private RestFieldErrorValidation processFieldErrors(List<FieldError> fieldErrors) {
RestFieldErrorValidation dto = new RestFieldErrorValidation();
for (FieldError fieldError: fieldErrors) {
String localizedErrorMessage = resolveLocalizedErrorMessage(fieldError);
dto.addFieldError(fieldError.getField(), localizedErrorMessage);
}
return dto;
}
private String resolveLocalizedErrorMessage(FieldError fieldError) {
Locale currentLocale = LocaleContextHolder.getLocale();
String localizedErrorMessage = messageSource.getMessage(fieldError, currentLocale);
//If the message was not found, return the most accurate field error code instead.
//You can remove this check if you prefer to get the default error message.
if (localizedErrorMessage.equals(fieldError.getDefaultMessage())) {
String[] fieldErrorCodes = fieldError.getCodes();
localizedErrorMessage = fieldErrorCodes[0];
}
return localizedErrorMessage;
}
}
Here is the google drive link of the project if you can check the code:
https://drive.google.com/open?id=1QSFVMi3adHGkc7BqXsqAY0P_tO2UfT2I
Here is the Article that i followed:
https://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-adding-validation-to-a-rest-api/
I'm assuming you are using plain Spring here, not Spring Boot.
The question is: To what exactly do you want to convert your RestFieldErrorValidation object? XML? JSON?
For either, you need an appropriate third-party library on your classpath, so Spring can do the conversion automatically.
In the case of JSON, you might want to add this dependency to your project.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
I am trying to add HATEOAS links with Resource<>, while also filtering with #JsonView. However, I don't know how to add the links to nested objects.
In the project on on Github, I've expanded on this project (adding in the open pull request to make it work without nested resources), adding the "Character" entity which has a nested User.
When accessing the ~/characters/resource-filtered route, it is expected that the nested User "player" appear with the firstNm and bioDetails fields, and with Spring generated links to itself, but without the userId and lastNm fields.
I have the filtering working correctly, but I cannot find an example of nested resources which fits with the ResourceAssembler paradigm. It appears to be necessary to use a ResourceAssembler to make #JsonView work.
Any help reconciling these two concepts would be appreciated. If you can crack it entirely, consider sending me a pull request.
User.java
//package and imports
...
public class User implements Serializable {
#JsonView(UserView.Detail.class)
private Long userId;
#JsonView({ UserView.Summary.class, CharacterView.Summary.class })
private String bioDetails;
#JsonView({ UserView.Summary.class, CharacterView.Summary.class })
private String firstNm;
#JsonView({ UserView.Detail.class, CharacterView.Detail.class })
private String lastNm;
public User(Long userId, String firstNm, String lastNm) {
this.userId = userId;
this.firstNm = firstNm;
this.lastNm = lastNm;
}
public User(Long userId) {
this.userId = userId;
}
...
// getters and setters
...
}
CharacterModel.java
//package and imports
...
#Entity
public class CharacterModel implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(CharacterView.Summary.class)
private Long characterId;
#JsonView(CharacterView.Detail.class)
private String biography;
#JsonView(CharacterView.Summary.class)
private String name;
#JsonView(CharacterView.Summary.class)
private User player;
public CharacterModel(Long characterId, String name, String biography, User player) {
this.characterId = characterId;
this.name = name;
this.biography = biography;
this.player = player;
}
public CharacterModel(Long characterId) {
this.characterId = characterId;
}
...
// getters and setters
...
}
CharacterController.java
//package and imports
...
#RestController
#RequestMapping("/characters")
public class CharacterController {
#Autowired
private CharacterResourceAssembler characterResourceAssembler;
...
#JsonView(CharacterView.Summary.class)
#RequestMapping(value = "/resource-filtered", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public Resource<CharacterModel> getFilteredCharacterWithResource() {
CharacterModel model = new CharacterModel(1L, "TEST NAME", "TEST BIOGRAPHY", new User(1L, "Fred", "Flintstone"));
return characterResourceAssembler.toResource(model);
}
...
}
CharacterResourceAssembler.java
//package and imports
...
#Component
public class CharacterResourceAssembler implements ResourceAssembler<CharacterModel, Resource<CharacterModel>>{
#Override
public Resource<CharacterModel> toResource(CharacterModel user) {
Resource<CharacterModel> resource = new Resource<CharacterModel>(user);
resource.add(linkTo(CharacterController.class).withSelfRel());
return resource;
}
}
I have this Controller and an Interface, when i try to implement the interface for applying Preauthorize annotation , it cause a damage to the controller , so the methods aren't working at that case . I know that i can apply the annotation directly inside the controller but i'll be happy if i can apply it using the interface as read in Spring's example
public interface PermissionsSecurity {
#PreAuthorize("hasRole('ROLE_ADMIN')")
String deletePost(#RequestParam(value = "id", required = true) Long id);
#PreAuthorize("hasRole('ROLE_ADMIN')")
String permissions(ModelMap model, #RequestParam(value = "q", required = false) String q);
}
Controller :
#Controller
public class PermissionsController implements PermissionsSecurity{
#Autowired
#Qualifier("permissionValidator")
private Validator validator;
#Autowired
private Permissions permissionsService;
#InitBinder
private void initBinber(WebDataBinder binder){
binder.setValidator(validator);
}
#RequestMapping(value="/permissions:delete", method=RequestMethod.POST)
public String deletePost(#RequestParam(value = "id", required = true) Long id) {
permissionsService.delete(id);
return "redirect:/permissions";
}
#RequestMapping(value = "/permissions", method=RequestMethod.GET)
public String permissions(ModelMap model, #RequestParam(value = "q", required = false) String q){
model.addAttribute("q", (q != null)? q : "");
model.addAttribute("viewTemplate", "permissions");
model.addAttribute("roles", permissionsService.getAll());
return "welcome";
}
}
I am validating my form field with given piece of code.
//controller method
public String addBusiness(#Valid #ModelAttribute("myForm") MyForm myForm, ...)
{
//logic will go here.
}
//form
#Component
public class MyForm{
#Pattern(regexp = "[0-9]{3,10}", message = "should be valid number")
public String getZip_code()
{
return this.zip_code;
}
}
Now I want same validation on zip_code in another method of controller like,
#RequestMapping(value = "${validation.url}", method = RequestMethod.GET)
#ResponseBody
public List<String> getCityList(#RequestParam(value = "zip_code", required = true) final String zip_code)
{
//logic goes here
}
How is it possible?
It's not. #Valid doesn't apply to #RequestParam annotated parameters. You can create a custom HandlerMethodArgumentResolver to do this or do the validation yourself in the method.