Skip to content

Commit

Permalink
add bean validation
Browse files Browse the repository at this point in the history
exception handling for rest controller
  • Loading branch information
deepcloudlabs committed Jun 23, 2020
1 parent 1d2f884 commit 84846c6
Show file tree
Hide file tree
Showing 14 changed files with 337 additions and 1 deletion.
5 changes: 5 additions & 0 deletions hr-microservice-hexagonal/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package com.example.hr.controller;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -11,18 +18,46 @@
import com.example.hr.application.EmployeeApplication;
import com.example.hr.dto.EmployeeRequest;
import com.example.hr.dto.EmployeeResponse;
import com.example.hr.orm.EmployeeEntity;
import com.example.validation.TcKimlikNo;

@RestController
@RequestScope
@RequestMapping("/employees")
@CrossOrigin
@Validated
public class EmployeeController {
@Autowired
private EmployeeApplication employeeApplication;

@PostMapping
public EmployeeResponse hireEmployee(@RequestBody EmployeeRequest request) {
public EmployeeResponse hireEmployee(@RequestBody @Validated EmployeeRequest request) {
employeeApplication.hireEmployee(request.toEmployee());
return new EmployeeResponse("success");
}

@PutMapping("{identity}")
public void updateEmployee(@PathVariable @TcKimlikNo String identity,
@Validated @RequestBody EmployeeEntity e) {

}
@PatchMapping("{identity}")
public void patchEmployee(@PathVariable @TcKimlikNo String identity,
Map<String,Object> employee) {
EmployeeEntity employeeEntity= null;
var clazz = EmployeeEntity.class;
employee.forEach((field,value)->{
try {
clazz.getDeclaredField(field).set(employeeEntity, value);
} catch (Exception e) {}
});
}

//@GetMapping

@DeleteMapping("{identity}")
public EmployeeResponse fireEmployee(@PathVariable @TcKimlikNo String identity) {
employeeApplication.fireEmployee(null);
return null;
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
package com.example.hr.dto;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import com.example.hr.domain.Department;
import com.example.hr.domain.Employee;
import com.example.hr.domain.MoneyCurrency;
import com.example.validation.Iban;
import com.example.validation.TcKimlikNo;

public class EmployeeRequest {
@TcKimlikNo
private String identity;
@Size(min=6)
private String fullname;
@Min(3_000)
private double salary;
@Iban
private String iban;
private boolean fulltime;
@Max(2002)
private int birthYear;
@NotNull
private byte[] photo;
@NotNull
private Department department;

public EmployeeRequest() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.example.hr.dto;

public class RestErrorMessage {
private int errorId;
private String message;
private String debugId;

public RestErrorMessage(int errorId, String message, String debugId) {
this.errorId = errorId;
this.message = message;
this.debugId = debugId;
}

public int getErrorId() {
return errorId;
}

public String getMessage() {
return message;
}

public String getDebugId() {
return debugId;
}

@Override
public String toString() {
return "RestErrorMessage [errorId=" + errorId + ", message=" + message + ", debugId=" + debugId + "]";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.example.hr.handler;

import java.util.stream.Collectors;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.springframework.http.HttpStatus;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.example.hr.dto.RestErrorMessage;

@RestControllerAdvice
public class RestErrorHandler {

@ExceptionHandler(RuntimeException.class)
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
public RestErrorMessage handleException(Exception e) {
return new RestErrorMessage(100,e.getMessage(),"6b8038e5-3b8f-4230-bfe8-4de2c422469f");
}

@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
public RestErrorMessage handleConstraintViolationException(ConstraintViolationException e) {
var violations = e.getConstraintViolations()
.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.joining("|"));
return new RestErrorMessage(200,violations,"7c248b97-9aa8-46fd-b5b2-9f8ea537635c");
}


@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
public RestErrorMessage handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
var violations = e.getBindingResult()
.getAllErrors()
.stream()
.map(ObjectError::getDefaultMessage)
.collect(Collectors.joining("|"));
return new RestErrorMessage(200,violations,"7c248b97-9aa8-46fd-b5b2-9f8ea537635c");
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,31 @@
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;

import com.example.hr.domain.Department;
import com.example.validation.Iban;
import com.example.validation.TcKimlikNo;

@Entity
@Table(name = "employees")
public class EmployeeEntity {
@Id
@Column(name = "identity")
@TcKimlikNo
private String identity;
@Size(min=5)
private String fullname;
@Min(3_000)
private double salary;
@Iban
private String iban;
//@AssertTrue
private boolean fulltime;
@Max(2002)
private int birthYear;
@Lob
@Column(columnDefinition = "longblob")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.example.validation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.constraints.Pattern;

@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Pattern(regexp = "^\\+?[a-z0-9](([-+.]|[_]+)?[a-z0-9]+)*@([a-z0-9]+(\\.|\\-))+[a-z]{2,6}$", message = "{validation.email}")
@Constraint(validatedBy = {})
public @interface Email {
String message() default "{validation.email}";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.validation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IbanValidator.class)
public @interface Iban {
String message() default "{validation.iban}";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.example.validation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class IbanValidator implements ConstraintValidator<Iban, String> {
private static final long MAX = 999999999;
private static final long MODULUS = 97;

@Override
public void initialize(Iban arg0) {
}

@Override
public boolean isValid(String value, ConstraintValidatorContext ctx) {
if (value == null || value.length() < 5) {
return false;
}
try {
int modulusResult = calculateModulus(value);
return (modulusResult == 1);
} catch (Exception ex) {
return false;
}
}

private int calculateModulus(String code) throws Exception {
String reformattedCode = code.substring(4) + code.substring(0, 4);
long total = 0;
for (int i = 0; i < reformattedCode.length(); i++) {
int charValue = Character.getNumericValue(reformattedCode.charAt(i));
if (charValue < 0 || charValue > 35) {
throw new Exception("Invalid Character[" + i + "] = '" + charValue + "'");
}
total = (charValue > 9 ? total * 100 : total * 10) + charValue;
if (total > MAX) {
total = (total % MODULUS);
}
}
return (int) (total % MODULUS);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.example.validation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Pattern.List({ @Pattern(regexp = "^.*\\d+.*$", message = "{validation.strongPassword2}"),
@Pattern(regexp = "^.*[-_]+.*$", message = "{validation.strongPassword3}") })
@Size(min = 6)
@Constraint(validatedBy = {})
public @interface StrongPassword {
String message() default "{validation.strongPassword1}";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.validation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = TcKimlikNoValidator.class)
public @interface TcKimlikNo {
String message() default "{validation.identityNo}";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
Loading

0 comments on commit 84846c6

Please sign in to comment.