【问题标题】:Spring MVC (RESTful API): Validating payload dependent on a path variableSpring MVC(RESTful API):根据路径变量验证有效负载
【发布时间】:2016-01-28 20:55:11
【问题描述】:

用例:

  • 让我们使用 POST HTTP 动词设计一个 RESTful 创建操作 - 创建票证,其中创建者(分配者)指定票证受让人
  • 我们正在以下位置创建新的“票证”:/companyId/userId/ticket
  • 我们正在提供包含 assigneeId:

    的票证正文

    { “assigneeId”:10 }

  • 我们需要验证 assigneeId 属于 URL 中的公司 - 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 的公司。

期望的输出:

  • 能够在自定义验证器中访问路径变量

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

【问题讨论】:

  • "我们需要在 URL 中验证 assigneeId 属于公司 - companyId 路径变量" => 所以Ticket 具有assigneeId 属性,我们需要询问数据库是否assigneeId 属于 companyId 的公司。所以我们需要TicketcompanyId。有意义吗?
  • 当然。我错过了。
  • 别担心,我更新了原来的 Q(希望现在更清楚了)。

标签: java spring rest validation bean-validation


【解决方案1】:

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

方法一:

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 代码虽然如果验证不会通过,所以我想这不是你想要的。

【讨论】:

    【解决方案2】:

    你总是可以这样做的:

    @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 时,独立于companyId 验证Ticket 是没有意义的。

    如果您无法使用上述解决方案,请考虑在 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
    
    }
    

    【讨论】:

    • 当然,但我试图减少控制器的职责,并让框架自动调用这些独立的验证器。
    • @Xorty 我已经用另一个选项编辑了答案,这减少了控制器的责任。
    • 谢谢,我已经想到了 - 但这会破坏我的 API 客户端,目前不在桌面上:/(合同依赖于使用 Ticket,而不是 TicketDTO
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-08
    • 2011-06-09
    • 2015-01-25
    • 2019-04-23
    • 2013-09-24
    • 1970-01-01
    • 2020-03-04
    相关资源
    最近更新 更多