Spring-Data-Rest 验证器

Posted

技术标签:

【中文标题】Spring-Data-Rest 验证器【英文标题】:Spring-Data-Rest Validator 【发布时间】:2014-08-10 16:31:13 【问题描述】:

我一直在尝试将 spring 验证器添加到 spring-data-rest 项目中。

我跟随并通过此链接设置“入门”应用程序:http://spring.io/guides/gs/accessing-data-rest/

...现在我正在尝试按照此处的文档添加自定义 PeopleValidator: http://docs.spring.io/spring-data/rest/docs/2.1.0.RELEASE/reference/html/validation-chapter.html

我的自定义 PeopleValidator 看起来像

package hello;

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

public class PeopleValidator implements Validator 
    @Override
    public boolean supports(Class<?> clazz) 
        return true;
    

    @Override
    public void validate(Object target, Errors errors) 
        errors.reject("DIE");
    

...我的 Application.java 类现在看起来像这样

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;

@Configuration
@EnableJpaRepositories
@Import(RepositoryRestMvcConfiguration.class)
@EnableAutoConfiguration
public class Application 

    public static void main(String[] args) 
        SpringApplication.run(Application.class, args);
    

    @Bean
    public PeopleValidator beforeCreatePeopleValidator() 
        return new PeopleValidator();
    

我希望发布到http://localhost:8080/people URL 会导致某种错误,因为 PeopleValidator 拒绝所有内容。但是,不会抛出任何错误,也不会调用验证器。

我也尝试过手动设置验证器,如 spring-data-rest 文档的第 5.1 节所示。

我错过了什么?

【问题讨论】:

【参考方案1】:

有点摸不着头脑 - 我没用过spring-data-rest。但是,在阅读了您正在遵循的教程之后,我认为问题在于您需要 PersonValidator 而不是 PeopleValidator。相应地重命名所有内容:

PersonValidator

package hello;

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

public class PersonValidator implements Validator 
    @Override
    public boolean supports(Class<?> clazz) 
        return true;
    

    @Override
    public void validate(Object target, Errors errors) 
        errors.reject("DIE");
    

应用程序

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;

@Configuration
@EnableJpaRepositories
@Import(RepositoryRestMvcConfiguration.class)
@EnableAutoConfiguration
public class Application 

    public static void main(String[] args) 
        SpringApplication.run(Application.class, args);
    

    @Bean
    public PersonValidator beforeCreatePersonValidator() 
        return new PersonValidator();
    

【讨论】:

感谢您的建议。但这似乎并没有什么不同。我以手动方式重试了它,它起作用了。当我开始工作时,我将不得不在我的项目上再试一次。 @Muel - 你是如何测试它的?【参考方案2】:

因此,“保存”之前/之后的事件似乎只在 PUT 和 PATCH 上触发。发布时,会触发之前/之后的“创建”事件。

我使用configureValidatingRepositoryEventListener 覆盖再次尝试手动方式,它成功了。我不确定我在工作中所做的与在家中所做的不同。我明天得看看。

如果其他人对它为什么行不通有什么建议,我当然很想听。

为了记录,下面是新的 Application.java 类的样子。

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;

@Configuration
@EnableJpaRepositories
@Import(RepositoryRestMvcConfiguration.class)
@EnableAutoConfiguration
public class Application extends RepositoryRestMvcConfiguration 

    public static void main(String[] args) 
        SpringApplication.run(Application.class, args);
    

    @Override
    protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) 
        validatingListener.addValidator("beforeCreate", new PeopleValidator());
    

【讨论】:

显然“beforeSave”事件不会触发,但“beforeCreate”事件会。我仍然必须以“手动”方式连接它,而不是使用名为“beforeCreatePeopleValidator”的 bean。 我发现同样的情况,自动发现不起作用。 我也有类似的问题 - 手动连接事件工作正常,自动发现失败。 我遇到了同样的问题,它没有被自动发现。我在上面创建了一个 JIRA:jira.spring.io/browse/DATAREST-524 现在,您必须扩展 RepositoryRestConfigurerAdapter,因为 RepositoryRestMvcConfiguration 版本已弃用。【参考方案3】:

看起来该功能目前尚未实现(2.3.0),不幸的是事件名称没有常量,否则下面的解决方案不会那么脆弱。

Configuration 使用正确的事件将所有正确命名的 Validator bean 添加到 ValidatingRepositoryEventListener

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
import org.springframework.validation.Validator;

@Configuration
public class ValidatorRegistrar implements InitializingBean 

    private static final List<String> EVENTS;
    static 
        List<String> events = new ArrayList<String>();
        events.add("beforeCreate");
        events.add("afterCreate");
        events.add("beforeSave");
        events.add("afterSave");
        events.add("beforeLinkSave");
        events.add("afterLinkSave");
        events.add("beforeDelete");
        events.add("afterDelete");
        EVENTS = Collections.unmodifiableList(events);
    

    @Autowired
    ListableBeanFactory beanFactory;

    @Autowired
    ValidatingRepositoryEventListener validatingRepositoryEventListener;

    @Override
    public void afterPropertiesSet() throws Exception 
        Map<String, Validator> validators = beanFactory.getBeansOfType(Validator.class);
        for (Map.Entry<String, Validator> entry : validators.entrySet()) 
            EVENTS.stream().filter(p -> entry.getKey().startsWith(p)).findFirst()
                    .ifPresent(p -> validatingRepositoryEventListener.addValidator(p, entry.getValue()));
        
    

【讨论】:

【参考方案4】:

另一种方法是使用此处指定的带注释的处理程序 http://docs.spring.io/spring-data/rest/docs/2.1.0.RELEASE/reference/html/events-chapter.html#d5e443

这是一个如何使用带注释的处理程序的示例:

import gr.bytecode.restapp.model.Agent;
import org.springframework.data.rest.core.annotation.HandleBeforeCreate;
import org.springframework.data.rest.core.annotation.HandleBeforeSave;
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
import org.springframework.stereotype.Component;

@Component
@RepositoryEventHandler(Agent.class)
public class AgentEventHandler 

    public static final String NEW_NAME = "**modified**";

    @HandleBeforeCreate
    public void handleBeforeCreates(Agent agent) 
            agent.setName(NEW_NAME);
    

    @HandleBeforeSave
    public void handleBeforeSave(Agent agent) 
        agent.setName(NEW_NAME + "..update");
    

示例来自github,为简洁起见进行了编辑。

【讨论】:

使用验证器比使用处理程序更好。

以上是关于Spring-Data-Rest 验证器的主要内容,如果未能解决你的问题,请参考以下文章

使用 spring-data-rest 将资源添加到集合

排除 Spring-data-rest 资源的部分字段

将业务逻辑添加到 spring-data-rest 应用程序

spring-data-rest 集成测试因简单的 json 请求而失败

处理事务中的 spring-data-rest 应用程序事件

如何配置 Spring-Data-Rest 以仅返回登录用户拥有的数据