【问题标题】:SpringBoot DTO ValidationSpring Boot DTO 验证
【发布时间】:2020-05-17 03:47:01
【问题描述】:

我是 spring-boot 的新手,我正在尝试向我的 DTO 类添加验证,如下所示。

import javax.validation.constraints.NotBlank;

@Getter
@Setter
public class EmployeeDto {
    private Long id;

    @NotBlank(message = "Employee first name is required")
    private String firstName;

    private String lastName;

    @NotBlank(message = "EmployeeNUM  is required")
    private String employeeNum;

}

下面是我用来保存员工的 REST 端点。

import javax.validation.Valid;
 @PostMapping("/employee")
    public ResponseEntity<?> addEmployee(@Valid @RequestBody EmployeeDto employeeDto) throws ClassNotFoundException {
        return   ResponseEntity.ok(employeeService.saveEmployee(deptId,employeeDto));

    }

我创建了一个如下所示的 Validation 类来验证 DTO 字段。

@ControllerAdvice
@RestController
public class Validation {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, String> handleValidationExceptions(
            MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return errors;

    }
}

预期输出是

{ "firstName":"员工的名字是必填项", "employeeNum":"EmployeeNUM 是必需的" }

但是当通过邮递员到达端点时,我只收到 400 错误请求。 我的代码有什么问题?如上所述,如何修复并获得预期的输出?

【问题讨论】:

  • 您是否通过请求传递了值?
  • 是的。我为文件传递了空字符串
  • @SupunWijerathne 是的。我得到了正确的状态,但没有得到响应正文。
  • @SupunWijerathne 仍然无法正常工作。我只是按照下面的文档,对用实体注释的实体类做同样的事情,它的工作我认为问题是 DTO 没有实体注释。我们不能在 DTO 类上添加实体,那么还有另一种方法来处理 DTO 类的验证吗? baeldung.com/spring-boot-bean-validation
  • 我发布了答案。找到了不同的方式。

标签: java spring hibernate spring-boot


【解决方案1】:

尝试像这样扩展ResponseEntityExceptionHandler 类:


import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.validation.ConstraintViolationException;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * * Handle all exceptions and java bean validation errors for all endpoints income data that use the @Valid annotation
 *
 * @author Ehab Qadah
 */
@ControllerAdvice
public class GeneralExceptionHandler extends ResponseEntityExceptionHandler {


    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception, HttpHeaders headers,
                                                                  HttpStatus status, WebRequest request) {
        List<String> validationErrors = exception.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(error -> error.getField() + ": " + error.getDefaultMessage())
                .collect(Collectors.toList());
        return getExceptionResponseEntity(HttpStatus.BAD_REQUEST, request, validationErrors);
    }


    @ExceptionHandler({ConstraintViolationException.class})
    public ResponseEntity<Object> handleConstraintViolation(
            ConstraintViolationException exception, WebRequest request) {
        List<String> validationErrors = exception.getConstraintViolations().stream().
                map(violation -> violation.getPropertyPath() + ": " + violation.getMessage())
                .collect(Collectors.toList());
        return getExceptionResponseEntity(HttpStatus.BAD_REQUEST, request, validationErrors);
    }

    private ResponseEntity<Object> getExceptionResponseEntity(final HttpStatus status, WebRequest request, List<String> errors) {
        final Map<String, Object> body = new LinkedHashMap<>();
        final String errorsMessage = CollectionUtils.isNotEmpty(errors) ? errors.stream().filter(StringUtils::isNotEmpty).collect(Collectors.joining(",")):status.getReasonPhrase();
        final String path = request.getDescription(false);
        body.put("TIMESTAMP", Instant.now());
        body.put("STATUS", status.value());
        body.put("ERRORS", errorsMessage);
        body.put("PATH", path);
        body.put("MESSAGE", status.getReasonPhrase());
        return new ResponseEntity<>(body, status);
    }
}

【讨论】:

    【解决方案2】:

    我已经复制了@Ehab Qadah 的答案。 只有一个修改是在字符串映射中给出错误字段,而不是字符串(fieldErrors 变量)

        @ExceptionHandler({ConstraintViolationException.class})
    public ResponseEntity<Object> handleConstraintViolation(
            ConstraintViolationException exception, WebRequest request) {
            
        Map<String, String> fieldErrors = ex.getConstraintViolations().stream()
                .collect(Collectors.toMap(cv -> cv.getPropertyPath().toString(), ConstraintViolation::getMessage));
            
        return getExceptionResponseEntity(HttpStatus.BAD_REQUEST, request, fieldErrors);
    }
    
    private ResponseEntity<Object> getExceptionResponseEntity(final HttpStatus status, WebRequest request, List<String> errors) {
        final Map<String, Object> body = new LinkedHashMap<>();
        final String errorsMessage = CollectionUtils.isNotEmpty(errors) ? errors.stream().filter(StringUtils::isNotEmpty).collect(Collectors.joining(",")):status.getReasonPhrase();
        final String path = request.getDescription(false);
        body.put("TIMESTAMP", Instant.now());
        body.put("STATUS", status.value());
        body.put("ERRORS", errorsMessage);
        body.put("PATH", path);
        body.put("MESSAGE", status.getReasonPhrase());
        return new ResponseEntity<>(body, status);
    }   
    

    【讨论】:

      【解决方案3】:

      我使用了下面的类,现在它可以正常工作了。

      @Service
      public class MapValidationErrorService {
          public ResponseEntity<?> MapValidationService(BindingResult result){
      
              if(result.hasErrors()){
                  Map<String, String> errorMap = new HashMap<>();
      
                  for(FieldError error: result.getFieldErrors()){
                      errorMap.put(error.getField(), error.getDefaultMessage());
                  }
                  return new ResponseEntity<Map<String, String>>(errorMap, HttpStatus.BAD_REQUEST);
              }
      
              return null;
      
          }
      
      }
      

      在控制器中

       @Autowired
          private MapValidationErrorService mapValidationErrorService;
      
          @PostMapping("/employee/{deptId}")
          public ResponseEntity<?> addEmployee(@PathVariable(name = "deptId") String deptId,@Valid @RequestBody EmployeeDto employeeDto, BindingResult result) throws ClassNotFoundException {
              ResponseEntity<?> errorMap = mapValidationErrorService.MapValidationService(result);
              if(errorMap != null)return errorMap;
              return   ResponseEntity.ok(employeeService.saveEmployee(deptId,employeeDto));
      
          }
      

      【讨论】:

      • 这几乎是不必要的手动工作。我确定您的实际问题是其他问题。
      • 您不需要手动验证,Sprint Boot 会在您提及@valid 时验证请求正文。不要忘记在依赖项中包含 spring-boot-starter-validation。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-10
      • 2021-01-30
      • 2021-02-07
      • 2016-07-24
      • 1970-01-01
      • 2019-10-16
      • 2016-03-08
      相关资源
      最近更新 更多