将Spring @ModelAttribute与@RestController

时间:2019-06-13 21:49:13

标签: java spring spring-boot spring-validator

我正在构建一个REST API,它将@PathParameter用于父PK,将@RequestBody用于子表单参数。接下来,我需要使用@RequestBody中的param键和@PathParameter中的父pk来针对存储在数据库中的正则表达式值验证@RequestBody值,但是我一直无法找出添加@PathParameter的好方法在不使用@ModelAttribute的情况下调用@Valid之前,将pk id传递给@RequestBody子对象。

使用@ModelAttribute,我已经能够将@PathParameter添加到@RequestBody对象,然后使用@Valid验证@RequestBody对象。但是我发现在使用@ModelAttribute时,Spring不再抛出MethodArgumentNotValidException,因此消除了使用全局异常处理程序的能力。

我发现将BindingResult添加到控制器处理程序中,然后在存在错误时抛出新的MethodArgumentNotValidException可能触发全局异常处理程序。

我的理解是ModelAttribute是针对Spring MVC的,而不是RestController的,因为我没有使用视图,所以我想知道这是否是正确的方法,或者是否有更好的解决方案。这是我的代码示例。

HTTP发布

localhost:8072/api/clover/graph_run/2

[
    {
        "graphKey" : "DATA_DIRECTORY",
        "graphValue" : "/data/clover-prod"
    },
    {

        "graphKey" : "DATAOUT_DIR",
        "graphValue" : "/data/clover-prod/94l"
    },
    {
        "graphKey" : "DELAY_MS",
        "graphValue" : "0"
    }
]

RestController

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/clover")
public class GraphRunController {

    final CloverServerService cloverServerService;

    @PostMapping("/graph_run/{graphId}")
    public String graphRun(@ModelAttribute("graphId") @Valid GraphJobDTO graphJobDTO,
                           BindingResult  bindingResult ) throws MethodArgumentNotValidException {
        log.debug("GraphRun {}", graphJobDTO);

        if (bindingResult.hasErrors()) {
            throw new MethodArgumentNotValidException(null, bindingResult);
        }

        return cloverServerService.runGraph(graphJobDTO);
    }

}

ControllerAdvise中的ModelAttribute

@Slf4j
@RequiredArgsConstructor
@ControllerAdvice(  assignableTypes = {GraphRunController.class})
public class GraphRunControllerAdvise {

    final GraphJobRepository graphJobRepository;

    @ModelAttribute("graphId")
    public GraphJobDTO addGraphId(@PathVariable(value = "graphId") Long graphId,
                                  @RequestBody List<GraphJobPropertyDTO> graphProperties) {
        log.debug("ModelAttribute graphId {}", graphId);

        //Query database for the graph job and all it's parameters
        GraphJob graphJob = graphJobRepository.findById(graphId)
                .orElseThrow(() -> new HRINotFoundException("No such job execution."
                + graphId));

        GraphJobDTO graphJobDTO = new GraphJobDTO();
        graphJobDTO.setGraph(graphJob.getGraph());
        graphJobDTO.setGraphProperties(graphProperties);

        //Create a map from the database with the key being the GraphKey contained in both the request and the database
        Map<String, GraphJobProperty> jobMap = graphJob.getJobProperties().stream().collect(
                Collectors.toMap(s -> s.getGraphKey().toUpperCase(), Function.identity()));

        graphProperties.forEach(v -> {
            String graphKey = v.getGraphKey();

            //If the graphKey in the request cannot be found in the database, throw an exception. We will handle the exception
            //in th exception handler
            if(!jobMap.containsKey(graphKey)) {
                throw new HRINotFoundException(String.format("%s is an invalid job parameter.", v.getGraphKey()));
            }

            //Within the database record is the validation message as well as the regex used to validate the incoming value,
            //we set the message and regex in the GraphPropertyDTO object for cross validation later on in the validator.
            GraphJobProperty jobProperty = jobMap.get(graphKey);
            v.setValidationMessage(jobProperty.getValidationMessage());
            v.setValidationRegex(jobProperty.getValidationRegex());
        });

        //Return the graphJobDTO object for validation
        return graphJobDTO;
    }

}

约束验证器

@Slf4j
public class GraphRegexValidator implements ConstraintValidator<GraphRegex, GraphJobPropertyDTO> {

    @Override
    public void initialize(final GraphRegex graphRegex) {
    }

    @Override
    public boolean isValid(final GraphJobPropertyDTO dto, final ConstraintValidatorContext context) {
        String regex = dto.getValidationRegex();
        String value = dto.getGraphValue();

        if(regex != null && value != null && !Pattern.matches(regex, value)) {
            log.debug("isValid {} - {}", false, dto);
            addConstraintViolation(context, getMessage(dto, context));
            return false;
        }
        log.debug("isValid {} - {}", true, dto);
        return true;
    }

    private void addConstraintViolation(ConstraintValidatorContext context, String message) {
        context.disableDefaultConstraintViolation();
        context.buildConstraintViolationWithTemplate(message).addPropertyNode("graphKey").addConstraintViolation();
    }

    private String getMessage(GraphJobPropertyDTO dto, ConstraintValidatorContext context) {
        return dto.getValidationMessage() != null ? String.format(dto.getValidationMessage(), dto.getGraphValue()) :
                context.getDefaultConstraintMessageTemplate();
    }

}

DTO

@Data
public class GraphJobDTO {

    @NotNull
    Long graphId;
    @NotNull
    String graph;
    @Valid
    List<GraphJobPropertyDTO> graphProperties;

}

@Data
@GraphRegex
public class GraphJobPropertyDTO {

    String graphKey;
    String graphValue;
    String validationMessage;
    String validationRegex;

}

0 个答案:

没有答案