스프링 부트 : 요청 매개 변수의 사용자 지정 유효성 검사
컨트롤러에서 요청 매개 변수 중 하나의 유효성을 확인하려고 합니다.요청 매개 변수는 지정된 값 목록 중 하나에 있어야 합니다. 그렇지 않으면 오류가 발생해야 합니다. 아래 코드에서 요청 매개 변수 순서 By는 @ValuesAllowed에 있는 값 목록에 있어야 합니다.
@RestController
@RequestMapping("/api/opportunity")
@Api(value = "Opportunity APIs")
@ValuesAllowed(propName = "orderBy", values = { "OpportunityCount", "OpportunityPublishedCount", "ApplicationCount",
"ApplicationsApprovedCount" })
public class OpportunityController {
@GetMapping("/vendors/list")
@ApiOperation(value = "Get all vendors")
public ResultWrapperDTO getVendorpage(@RequestParam(required = false) String term,
@RequestParam(required = false) Integer page, @RequestParam(required = false) Integer size,
@RequestParam(required = false) String orderBy, @RequestParam(required = false) String sortDir) {
사용자 지정 bean validator를 작성했는데 어떻게 된 일인지 작동하지 않습니다. 쿼리 매개 변수에 대한 임의 값을 전달하더라도 유효성이 검사되지 않고 오류가 발생합니다.
@Repeatable(ValuesAllowedMultiple.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {ValuesAllowedValidator.class})
public @interface ValuesAllowed {
String message() default "Field value should be from list of ";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String propName();
String[] values();
}
public class ValuesAllowedValidator implements ConstraintValidator<ValuesAllowed, Object> {
private String propName;
private String message;
private String[] values;
@Override
public void initialize(ValuesAllowed requiredIfChecked) {
propName = requiredIfChecked.propName();
message = requiredIfChecked.message();
values = requiredIfChecked.values();
}
@Override
public boolean isValid(Object object, ConstraintValidatorContext context) {
Boolean valid = true;
try {
Object checkedValue = BeanUtils.getProperty(object, propName);
if (checkedValue != null) {
valid = Arrays.asList(values).contains(checkedValue.toString().toLowerCase());
}
if (!valid) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message.concat(Arrays.toString(values)))
.addPropertyNode(propName).addConstraintViolation();
}
} catch (IllegalAccessException e) {
log.error("Accessor method is not available for class : {}, exception : {}", object.getClass().getName(), e);
return false;
} catch (NoSuchMethodException e) {
log.error("Field or method is not present on class : {}, exception : {}", object.getClass().getName(), e);
return false;
} catch (InvocationTargetException e) {
log.error("An exception occurred while accessing class : {}, exception : {}", object.getClass().getName(), e);
return false;
}
return valid;
}
}
사례 1: 주석 Values Allowed가 전혀 트리거되지 않은 경우 @Validated로 컨트롤러에 주석을 달지 않았기 때문일 수 있습니다.
@Validated
@ValuesAllowed(propName = "orderBy", values = { "OpportunityCount", "OpportunityPublishedCount", "ApplicationCount", "ApplicationsApprovedCount" })
public class OpportunityController {
@GetMapping("/vendors/list")
public String getVendorpage(@RequestParam(required = false) String term,..{
}
사례 2: 트리거되어 오류를 던진다면, 그것은 다음과 같은 이유일 수 있습니다.BeanUtils.getProperty
속성을 확인하지 않고 예외를 던집니다.
위의 솔루션이 작동하지 않으면 주석을 메서드 수준으로 이동하고 유효한 값 목록을 사용하도록 Validator를 업데이트할 수 있습니다.OrderBy
매개 변수이것은 저에게 효과가 있었습니다.아래는 샘플 코드입니다.
@RestController
@RequestMapping("/api/opportunity")
@Validated
public class OpportunityController {
@GetMapping("/vendors/list")
public String getVendorpage(@RequestParam(required = false) String term,
@RequestParam(required = false) Integer page, @RequestParam(required = false) Integer size,
@ValuesAllowed(propName = "orderBy", values = { "OpportunityCount", "OpportunityPublishedCount", "ApplicationCount",
"ApplicationsApprovedCount" }) @RequestParam(required = false) String orderBy, @RequestParam(required = false) String sortDir) {
return "success";
}
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { ValuesAllowed.Validator.class })
public @interface ValuesAllowed {
String message() default "Field value should be from list of ";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String propName();
String[] values();
class Validator implements ConstraintValidator<ValuesAllowed, String> {
private String propName;
private String message;
private List<String> allowable;
@Override
public void initialize(ValuesAllowed requiredIfChecked) {
this.propName = requiredIfChecked.propName();
this.message = requiredIfChecked.message();
this.allowable = Arrays.asList(requiredIfChecked.values());
}
public boolean isValid(String value, ConstraintValidatorContext context) {
Boolean valid = value == null || this.allowable.contains(value);
if (!valid) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message.concat(this.allowable.toString()))
.addPropertyNode(this.propName).addConstraintViolation();
}
return valid;
}
}
}
이 검증을 수행하려면 몇 가지 사항을 변경해야 합니다.
컨트롤러에 주석을 달아야 합니다.@Validated
그리고.@ValuesAllowed
메서드에서 대상 매개 변수에 주석을 달아야 합니다.
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Validated
@RestController
@RequestMapping("/api/opportunity")
public class OpportunityController {
@GetMapping("/vendors/list")
public String getVendorpage(
@RequestParam(required = false)
@ValuesAllowed(values = {
"OpportunityCount",
"OpportunityPublishedCount",
"ApplicationCount",
"ApplicationsApprovedCount"
}) String orderBy,
@RequestParam(required = false) String term,
@RequestParam(required = false) Integer page, @RequestParam(required = false) Integer size,
@RequestParam(required = false) String sortDir) {
return "OK";
}
}
@ValuesAllowed
목표로 삼아야 합니다.ElementType.PARAMETER
그리고 이 경우에는 더 이상 필요하지 않습니다.propName
Spring이 원하는 매개 변수의 유효성을 검사하기 때문에 속성입니다.
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {ValuesAllowedValidator.class})
public @interface ValuesAllowed {
String message() default "Field value should be from list of ";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] values();
}
검증자:
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.List;
public class ValuesAllowedValidator implements ConstraintValidator<ValuesAllowed, String> {
private List<String> expectedValues;
private String returnMessage;
@Override
public void initialize(ValuesAllowed requiredIfChecked) {
expectedValues = Arrays.asList(requiredIfChecked.values());
returnMessage = requiredIfChecked.message().concat(expectedValues.toString());
}
@Override
public boolean isValid(String testValue, ConstraintValidatorContext context) {
boolean valid = expectedValues.contains(testValue);
if (!valid) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(returnMessage)
.addConstraintViolation();
}
return valid;
}
}
그러나 위의 코드는 HTTP 500을 반환하고 로그를 추한 스택 추적으로 오염시킵니다.그것을 피하기 위해, 당신은 다음과 같은 것을 넣을 수 있습니다.@ExceptionHandler
컨트롤러 본체의 메서드(이 컨트롤러에만 적용됨)를 사용하면 HTTP 상태를 제어할 수 있습니다.
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
String handleConstraintViolationException(ConstraintViolationException e) {
return "Validation error: " + e.getMessage();
}
아니면 당신은 이 방법을 별도로 둘 수 있습니다.@ControllerAdvice
모든 컨트롤러에서 사용하거나 원하는 컨트롤러에서만 사용하는 것처럼 클래스를 지정하고 이 검증을 훨씬 더 잘 제어할 수 있습니다.
저는 다른 모든 것을 한 후에 제가 이 의존성을 놓치고 있다는 것을 발견했습니다.일반적인 유효성 검사 단계가 작동했지만 사용자 지정 유효성 검사기는 이를 폼에 추가하기 전까지 작동하지 않았습니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
언급URL : https://stackoverflow.com/questions/59422883/spring-boot-custom-validation-in-request-params
'programing' 카테고리의 다른 글
장고 - 파일을 만들고 모델의 파일 필드에 저장하는 방법은 무엇입니까? (0) | 2023.07.19 |
---|---|
파이썬 요청을 사용하여 브라우저 방문을 위장하고 사용자 에이전트를 생성하는 방법은 무엇입니까? (0) | 2023.07.19 |
Pandas 막대 그림에 값을 사용하여 막대 주석 달기 (0) | 2023.07.19 |
pip 캐시 폴더는 어디에 있습니까? (0) | 2023.07.19 |
혼동 행렬을 어떻게 표시할 수 있습니까? (0) | 2023.07.19 |