SpringBoot handle #RequestParam exception - spring

I have controller and method deleteFolder has #RequestParam:
#RestController
public class FolderController extends BaseController {
#DeleteMapping(path = Const.APIVersions.API_V1 + "/folders", params = "id")
public ResponseEntity<HttpStatus> deleteFolder(KeycloakAuthenticationToken authentication, #RequestParam Long id) throws EntityNotFoundException {
folderService.deleteFolder(id, authentication.getName());
return ResponseEntity.ok(HttpStatus.OK);
}
parent class:
public abstract class BaseController {
#ExceptionHandler({MissingServletRequestParameterException.class,
UnsatisfiedServletRequestParameterException.class, MethodArgumentTypeMismatchException.class,
IllegalArgumentException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<ErrorResponse> handleMissingServletRequestParameter(MissingServletRequestParameterException exception) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse(HttpStatus.BAD_REQUEST.value(), exception.getMessage()));
}
I want to handle exception, when required param is missing. But that exception handler don't want to catch that exception raised from deletFolder method.
Any others handlers works.

You have to create custom ConstraintValidator and catch ConstraintViolationException so your base controller becomes
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import javax.validation.*;
public abstract class BaseController {
#ExceptionHandler({ConstraintViolationException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<ErrorResponse> handlerFolderNotFoundException(ConstraintViolationException exception) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse(HttpStatus.BAD_REQUEST.value(), exception.getMessage()));
}
}
Validation Annotation
import javax.validation.*;
import java.lang.annotation.*;
#Constraint(validatedBy = FolderValidationConstraint.class)
#Target({ElementType.PARAMETER, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface ValidateFolder {
String message() default "Error validating folder name";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validator class
import lombok.extern.slf4j.*;
import javax.validation.*;
import java.io.*;
#Slf4j
public class FolderValidationConstraint implements ConstraintValidator<ValidateFolder, String> {
#Override
public boolean isValid(String path, ConstraintValidatorContext constraintValidatorContext) {
return new File("/home/ashish/Documents/test-delete/" + path).exists();
}
}
Controller
import org.springframework.validation.annotation.*;
import org.springframework.web.bind.annotation.*;
import java.io.*;
import java.util.*;
#Validated
#RestController
#RequestMapping(path = "/folders")
public class FolderController extends BaseController {
#GetMapping
public Map<String, Boolean> delete(#ValidateFolder #RequestParam("name") String folderName) {
final File path = new File("/home/ashish/Documents/test-delete/" + folderName);
return Map.of("exists", path.exists());
}
}
Please make sure to add dependency in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Related

Is there a way to validate fields in a Request Object that am using as a wrapper, such that one of them is optional

Controller Code:
#RequestMapping(value="/update", method=RequestMethod.POST, produces =
"application/json; charset=utf-8")
#ResponseBody
public ResponseEntityWrapper update(
HttpServletRequest request,
#RequestBody #Valid InputForm form,
HttpServletResponse response
) {
//logic
}
InputForm:
public class InputForm{
#NotNull
private String formId;
private List<Employee> addEmpList;
private List<Employee> removeEmpList;
//Other fields & getters & setters.....
}
Employee:
public Employee{
#NotNull
private String empId;
private String name;
//Other fields and getters and setters.....
}
When the request to Controller's update is method, I would like to have InputForm validated - Atleast one of the fields in it is mandatory. ie., both addEmpList, removeEmpList cannot be null. And empId in Employee should not be null.
How do I do this?
This can be achieved using javax.validation.Constraint. Annotate your controller using #Validated annotation, and you can create your constraints as below:
Controller
import com.example.model.Employee;
import com.example.model.InputForm;
import com.example.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.List;
#RestController
#Validated
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(value="/update", method= RequestMethod.POST, produces = "application/json; charset=utf-8")
#ResponseBody
public ResponseEntity<List<Employee>> update(
HttpServletRequest request,
#CheckAtLeastOnePresentConstraint #RequestBody #Valid InputForm form,
HttpServletResponse response
) {
return new ResponseEntity<List<Employee>>(myService.getEmployees(form), HttpStatus.OK);
}
}
Contraint Interface:
import java.lang.annotation.*;
import javax.validation.Constraint;
import javax.validation.Payload;
#Constraint(validatedBy = CheckOneValuePresent.class)
#Target( { ElementType.PARAMETER })
#Retention(RetentionPolicy.RUNTIME)
public #interface CheckAtLeastOnePresentConstraint {
String message() default "At least one of the values removeEmpList or addEmpList must be present";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And your CustomValidator class
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.constraintvalidation.SupportedValidationTarget;
import javax.validation.constraintvalidation.ValidationTarget;
#SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class CheckOneValuePresent implements ConstraintValidator<CheckAtLeastOnePresentConstraint, InputForm> {
#Override
public boolean isValid(InputForm inputForm,
ConstraintValidatorContext cxt) {
return inputForm.getAddEmpList() != null || inputForm.getRemoveEmpList() != null;
}
}
Hope this will help.

Spring Boot request validation

I've been reading a lot about spring request validation. I read a lot articles on how to appropriately implement that, but I have some problem. This is my code:
RestController:
#Autowired
EmployeeManager employeeManager;
#Autowired
EmployeeValidator employeeValidator;
#InitBinder("employee")
public void setupBinder(WebDataBinder binder) {
binder.addValidators(employeeValidator);
}
// -------------- CREATE EMPLOYEES --------------
#PostMapping(value = "add")
public ResponseEntity<EmployeeDTO> addEmployee(#Valid #RequestBody EmployeeDTO employee) {
boolean isCreated = employeeManager.addEmployee(employee);
if(isCreated) {
return new ResponseEntity<>(employee, HttpStatus.CREATED);
}
return new ResponseEntity(new CustomError("Unable to create, employee with email " +
employee.getEmail() + " already exist."), HttpStatus.CONFLICT);
}
Validator:
package com.employee.api.EmployeeAPI.validator;
import com.employee.api.EmployeeAPI.model.dto.EmployeeDTO;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
#Component
public class EmployeeValidator implements Validator {
private Pattern pattern;
private Matcher matcher;
private static final String STRING_PATTERN = "[a-zA-Z]+";
private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*#"
+ "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
#Override
public boolean supports(Class<?> clazz) {
return EmployeeDTO.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
EmployeeDTO employee = (EmployeeDTO) target;
if (validateInputString(employee.getFirstName(), STRING_PATTERN)) {
errors.rejectValue("firstName", "firstName.invalid");
}
if (validateInputString(employee.getLastName(), STRING_PATTERN)) {
errors.rejectValue("lastName", "lastName.invalid");
}
if (validateInputString(employee.getJob(), STRING_PATTERN)) {
errors.rejectValue("job", "job.invalid");
}
if (validateInputString(employee.getEmail(), EMAIL_PATTERN)) {
errors.rejectValue("email", "email.invalid");
}
}
private boolean validateInputString(String input, String regexPattern) {
pattern = Pattern.compile(regexPattern);
matcher = pattern.matcher(input);
return (!matcher.matches() || input == null || input.trim().length() == 0);
}
}
and in config I added bean:
#Bean
public EmployeeValidator beforeAddOrUpdateEmployeeValidator() {
return new EmployeeValidator();
}
I am not really sure of how it should be invoked right now when adding employees, because it surely does not work for now. Could you help me with the right implementation or point in the right direction?
I'm not familiar with org.springframework.validation.Validator, but will suggest you how to do the same validation as you need with javax.validation.ConstraintValidator (JSR-303). Your controller class is fine and no changes needed there.
you need to create a custom annotation #ValidEmployee and annotate your dto with it:
#ValidEmployee
public class EmployeeDto {
...
}
ValidEmployee annotation:
import javax.validation.Constraint;
import javax.validation.Payload;
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = EmployeeValidator.class)
#Documented
public #interface ValidEmployee {
String message() default "{ValidEmployee.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and implement your validation logic in isValid method:
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class EmployeeValidator implements ConstraintValidator<ValidEmployee, EmployeeDto> {
#Override
public void initialize(ValidEmployee constraintAnnotation) {
}
#Override
public boolean isValid(EmployeeDto employee, ConstraintValidatorContext context) {
// do your validation logic
}
}

Spring boot + redis

I am working demo Spring boot application with integration of Redis.
I have referred various site reference but lastly I preferred to follow this: http://www.baeldung.com/spring-data-redis-tutorial
My code is almost same as given in above link. Only change is that I have autowired StudentRepository in my RestController class.
Now when I try to do maven-install at that time it gives me error that
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'studentController': Unsatisfied dependency expressed through field 'studentRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'studentRepositoryImpl' defined in file [/home/klevu/work/Nimesh/Spring Boot Workspace/bootDemo/target/classes/com/example/demo/redis/repository/StudentRepositoryImpl.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.example.demo.redis.repository.StudentRepositoryImpl]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: No visible constructors in class com.example.demo.redis.repository.StudentRepositoryImpl
When I tried to keep constructor public, it creates build successfully. But I don't know I should do it or not here. I was thinking that rather than Autowiring constructor, I should be able to do setter injection. I also tried below:
#Autowired
private RedisTemplate<String, Student> redisTemplate;
But it is also not working.
package com.example.demo.redis.repository;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;
import com.example.demo.redis.bean.Student;
#Repository
public class StudentRepositoryImpl implements StudentRepository {
private static final String KEY = "Student";
//#Autowired
private RedisTemplate<String, Student> redisTemplate;
private HashOperations<String, String, Student> hashOps;
#Autowired
private StudentRepositoryImpl(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
#PostConstruct
private void init() {
hashOps = redisTemplate.opsForHash();
}
#Override
public void saveStudent(Student person) {
hashOps.put(KEY, person.getId(), person);
}
#Override
public void updateStudent(Student person) {
hashOps.put(KEY, person.getId(), person);
}
#Override
public Student findStudent(String id) {
return hashOps.get(KEY, id);
}
#Override
public Map<String, Student> findAllStudents() {
return hashOps.entries(KEY);
}
#Override
public void deleteStudent(String id) {
hashOps.delete(KEY, id);
}
}
RedisConfiguration are default and code as below:
package com.example.demo.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
#Configuration
public class RedisConfiguration {
#Bean
JedisConnectionFactory jedisConnectionFactory() {
return new JedisConnectionFactory();
}
#Bean
public RedisTemplate<String, Object> redisTemplate(){
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}
Spring boot main entry point is declared as below:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
#SpringBootApplication
#EnableMongoRepositories(basePackages = {"com.example.demo.mongo.repository"} )
#EnableRedisRepositories(basePackages = {"com.example.demo.redis.repository"})
public class BootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(BootDemoApplication.class, args);
}
}
Demo controller to test redis is as below:
package com.example.demo.controller;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.redis.bean.Student;
import com.example.demo.redis.repository.StudentRepository;
#RestController
#RequestMapping("/student")
public class StudentController {
#Autowired
private StudentRepository studentRepository;
#GetMapping
public ResponseEntity<Map<String, Student>> index() {
Map<String, Student> students = studentRepository.findAllStudents();
return new ResponseEntity<Map<String, Student>>(students, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.GET, value = "/{id}")
public ResponseEntity<Student> getStudentById(#PathVariable("id") String id) {
Student student = studentRepository.findStudent(id);
return new ResponseEntity<Student>(student, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Student> saveStudent(#RequestBody Student student) {
studentRepository.saveStudent(student);
return new ResponseEntity<Student>(student, HttpStatus.CREATED);
}
#RequestMapping(method = RequestMethod.PUT, value = "/{id}")
public ResponseEntity<Student> updateStudent(#RequestBody Student student) {
studentRepository.updateStudent(student);
return new ResponseEntity<Student>(student, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.DELETE, value = "/{id}")
public ResponseEntity<Student> deleteMessage(#PathVariable("id") String id) {
studentRepository.deleteStudent(id);
return new ResponseEntity<Student>(HttpStatus.OK);
}
}
You set the constructor as private... change it to public
#Autowired
public StudentRepositoryImpl(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
Change the following redis configuration:
Your original:
#Bean
public RedisTemplate<String, Object> redisTemplate() {
...
}
Change it to:
#Bean
public RedisTemplate<String, ?> redisTemplate(){
...
}
It should work now for you.
You could use Spring Data Redis
Add dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Enable Caching
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class RedisDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RedisDemoApplication.class, args);
}
}
Add Cacheable annotation at method to cache in redis
#Cacheable(value = "employee", key = "#id")
public Employee getEmployee(Integer id) {
log.info("Get Employee By Id: {}", id);
Optional<Employee> employeeOptional = employeeRepository.findById(id);
if (!employeeOptional.isPresent()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Id Not foud");
}
return employeeOptional.get();
}

unable to inject bean in simple spring mvc application

I am trying a simple application in the spring mvc using annotations only. In my service class, I am unable to inject DAO class even though I have used relevant annotations.Please let me know what mistake I am doing. Below are my class definitions.
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SpringRootConfig.class };
//return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { SpringWebConfiguration.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="com.emp.controller")
public class SpringWebConfiguration extends WebMvcConfigurerAdapter{
#Bean
public InternalResourceViewResolver jspViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/view/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="com.emp.svc,com.emp.dao")
public class SpringRootConfig {
}
package com.emp.svc;
import org.springframework.beans.factory.annotation.Autowired;
import com.emp.dao.LoginDAO;
public class LoginSvc {
#Autowired
private LoginDAO dao;
public boolean validateLogin(){
System.out.println("In the svc method");
return dao.validateLogin();
}
}
package com.emp.dao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
#Repository
public class LoginDAO {
public boolean validateLogin(){
System.out.println("In the DAO method");
return true;
}
}
package com.emp.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.emp.forms.LoginForm;
import com.emp.svc.LoginSvc;
#Controller
public class LoginController {
#RequestMapping(value = {"/login"},method = RequestMethod.GET)
public ModelAndView launchLoginPage(){
ModelAndView model = new ModelAndView();
model.setViewName("login");
return model;
}
#RequestMapping(value = {"/welcome"},method = RequestMethod.GET)
public ModelAndView validateLogin(#ModelAttribute LoginForm form){
ModelAndView model = new ModelAndView();
LoginSvc svc = new LoginSvc();
if(svc.validateLogin()){
model.setViewName("welcome");
}
else
{
model.setViewName("login");
}
return model;
}
LoginSvc is called from controller. I am expecting that in LoginSvc, LoginDAO will be injected and hence the call to dao dao.validateLogin() will be successful. But what I am getting is Null pointer at dao.validateLogin(), indicating LoginDAO is not injected.
If you can let me know what I am missing, it will be great help.
Add #Service annotation in your LoginSvc class.
package com.emp.svc;
import org.springframework.beans.factory.annotation.Autowired;
import com.emp.dao.LoginDAO;
#Service
public class LoginSvc {
#Autowired
private LoginDAO dao;
public boolean validateLogin(){
System.out.println("In the svc method");
return dao.validateLogin();
}
}
And autowire LoginSvc inside your controller. As you are creating the object yourself invoking constructor of LoginSvc, spring will not provide you autowire candidate.
#Controller
public class LoginController {
#Autowired
private LoginSvc svc;
#RequestMapping(value = {"/login"},method = RequestMethod.GET)
public ModelAndView launchLoginPage(){
ModelAndView model = new ModelAndView();
model.setViewName("login");
return model;
}
#RequestMapping(value = {"/welcome"},method = RequestMethod.GET)
public ModelAndView validateLogin(#ModelAttribute LoginForm form){
ModelAndView model = new ModelAndView();
if(svc.validateLogin()) {
model.setViewName("welcome");
} else {
model.setViewName("login");
}
return model;
}
}

how to use oval on custom validation Annotation

Here is my code for creating a custom Annotation for validating Name
ValidName.java
package custom.Annotation;
import java.lang.annotation.*;
import net.sf.oval.configuration.annotation.Constraint;
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.LOCAL_VARIABLE)
#Constraint(checkWith=NameValidator.class)
public #interface ValidName {
String message() default NameValidator.message;
}
here is my code for Constraint Class
package custom.Annotation;
import net.sf.oval.Validator;
import net.sf.oval.configuration.annotation.AbstractAnnotationCheck;
import net.sf.oval.context.OValContext;
import net.sf.oval.exception.OValException;
import play.Logger;
import java.util.regex.Pattern;
public class NameValidator extends AbstractAnnotationCheck<ValidName>
{
public final static String message="custom.message";
private static final String letter = "[a-zA-Z]";
public static final Pattern VALID_PATTERN = Pattern.compile(letter);
public static boolean isValidText(String userName) {
return VALID_PATTERN.matcher(userName).matches();
}
#Override
public void configure(ValidName annotation) {
setMessage(annotation.message());
}
#Override
public boolean isSatisfied(Object validatedObject, Object valueToValidate, OValContext context,
Validator validator) throws OValException {
try
{
if (valueToValidate == null) {
return false;
}
}catch (Exception e){
e.getMessage();
}
return` isValidText(valueToValidate.toString()`);
}
}
When I applied #ValidName to any local variable nothing happened
and I also am unable to debug the program. Any suggestions?
You need to use the oval validation by calling validate method of oval validation library.
#Autowired
#Qualifier("ovalValidator")
private Validator ovalValidator;
List<ConstraintViolation> violations = null;
violations = ovalValidator.validate(objectToValidate);

Resources