Spring MVC(RESTful API):验证依赖于路径变量的有效负载

时间:2015-10-28 21:33:28

标签: java spring rest validation bean-validation

使用案例:

  • 让我们使用POST HTTP动词设计RESTful创建操作 - 创建创建者(分配者)指定故障单受让人的故障单
  • 我们正在创建一张新的"机票"在以下位置:/companyId/userId/ticket
  • 我们提供包含assigneeId的票证正文:

    {   " assigneeId":10 }

  • 我们需要验证assigneeId属于公司网址 - companyId路径变量

到目前为止:

@RequestMapping(value="/{companyId}/{userId}/ticket", method=POST)
public void createTicket(@Valid @RequestBody Ticket newTicket, @PathVariable Long companyId, @PathVariable Long userId) {
  ...
}
  • 我们可以轻松指定自定义验证程序(TicketValidator)(即使是依赖项)并验证Ticket实例
  • 我们可以轻松地将companyId传递给此验证器!我们需要验证ticket.assigneeId属于公司companyId

期望的输出:

  • 在自定义验证程序中访问路径变量的能力

任何想法如何在这里实现所需的输出?

2 个答案:

答案 0 :(得分:2)

如果我们假设我们的自定义验证器知道所需的属性名称,那么我们可以这样做:

接近一个方法:

1)我们可以将这个获取路径变量逻辑移动到某种基本验证器:

public abstract class BaseValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz)
    {
        // supports logic
    }

    @Override
    public void validate(Object target, Errors errors)
    {
        // some base validation logic or empty if there isn't any
    }

    protected String getPathVariable(String name) {
        // Getting current request (Can be autowired - depends on your implementation)
        HttpServletRequest req = HttpServletRequest((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (req != null) {
            // getting variables map from current request
            Map<String, String> variables = req.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            return variables.get(name);
        }
        return null;
    }
}

2)使用TicketValidator实施进行扩展:

public class TicketValidator extends BaseValidator {

    @Override
    public void validate(Object target, Errors errors)
    {
        // Getting our companyId var
        String companyId = getPathVariable("companyId");
        ...
        // proceed with your validation logic. Note, that all path variables
        // is `String`, so you're going to have to cast them (you can do 
        // this in `BaseValidator` though, by passing `Class` to which you 
        // want to cast it as a method param). You can also get `null` from 
        // `getPathVariable` method - you might want to handle it too somehow
    }
}

方法二:

我认为值得一提的是,您可以使用@PreAuthorize注释与SpEL进行此类验证(您可以传递路径变量并向其请求正文)。如果验证通过,您将获得HTTP 403代码,因此我认为它并不是您想要的。

答案 1 :(得分:1)

您可以随时执行此操作:

@Controller
public class MyController {

    @Autowired
    private TicketValidator ticketValidator;

    @RequestMapping(value="/{companyId}/{userId}/ticket", method=POST)
    public void createTicket(@RequestBody Ticket newTicket,
            @PathVariable Long companyId, @PathVariable Long userId) {

        ticketValidator.validate(newTicket, companyId, userId);
        // do whatever

    }

}

修改以回应评论:

Ticket的有效性取决于companyId时,独立于Ticket验证companyId是没有意义的。

如果您无法使用上述解决方案,请考虑在DTO中将TicketcompanyId分组,并更改映射,如下所示:

@Controller
public class MyController {

    @RequestMapping(value="/{userId}/ticket", method=POST)
    public void createTicket(@Valid @RequestBody TicketDTO ticketDto,
            @PathVariable Long userId) {

        // do whatever
    }

}

public class TicketDTO {

    private Ticket ticket;

    private Long companyId;

    // setters & getters

}