【问题标题】:How to add custom headers to a specific resource in Spring Data Rest如何将自定义标头添加到 Spring Data Rest 中的特定资源
【发布时间】:2017-10-30 14:51:15
【问题描述】:

我在 Spring Data Rest 中的一个实体有一个属性值,应该添加到响应的标头中。如何做到这一点?

我已经找到了使用处理程序拦截器更改 default 标头(适用于所有响应)的方法。我还发现了关于版本、etag 和修改的东西。 这些不是我想要的。

标头必须设置在 1 个 REST 资源上,并且值取决于实例/记录。

例如:Person 具有属性age。对 Person REST 资源的每个请求(例如 GET /person/{id})都必须将 age 的值作为 http 标头返回(例如:age:32)。

提前致谢。

【问题讨论】:

标签: spring-mvc header spring-data-rest


【解决方案1】:

根据我之前的回答,我可以看到最新版本的 SDR API 有一个方法 RepositoryRestMvcConfiguration#httpHeadersPreparer()

我可以看到这种方法在 2.6.8 中可用,但在我当前使用的 2.5.10 版本中没有。

所以看起来你可以这样做:

@Configuration
public class MyConfiguration extends RepositoryRestMvcConfiguration
{
  @Override
  public HttpHeadersPreparer httpHeadersPreparer()
  {
    HttpHeadersPreparer preparer = new HttpHeadersPreparer(){
      @Override
      public HttpHeaders prepareHeaders(PersistentEntityResource resource, Object value)
      {
        org.springframework.http.HttpHeaders headers = super.prepareHeaders(resource);

        if(value instanceof Person){
          headers.add("age", ((Person)value).getAge());
        }
      }

    };

    return preparer;
  }
}

【讨论】:

  • 艾伦,非常感谢。这看起来是一个很好的解决方案。但是,通过扩展 RepositoryRestMvcConfiguration 其他配置似乎消失了。
  • 这取决于您现有的配置。您需要使用解决方案更新该内联内容。
  • 谢谢艾伦,它现在可以工作了。知道如何为资源集合实现这一点吗? (人员名单/person
  • 我猜在这种情况下,值参数将是Collection<Person>,所以你可以随心所欲地使用它。
【解决方案2】:

首先,您可以将拦截器绑定到特定路径,这样就解决了一半的问题。

第二个问题是获取对已请求的 Person 资源的引用。您可以在这里查看使用 ThreadLocal:

public class PersonContext{
    private static ThreadLocal<Person> context  = new ThreadLocal<Person>();

    public static Person getPerson(Person person){
        return context.get();
    }

    public static void setPerson(Person person){
        context.set(person);
    }

    public static void cleanUp(){
        context.remove();
    }
}

标准的 JPA 实体监听器可用于绑定对加载的 Person 的引用:

public class PersonListener{

    @PostLoad
    public void setContext(Person person){
        PersonContext.set(person);
    }
}

Entity监听器可以注册到Entity上:

@Entity
@EntityListeners(PersonListener.class)
public class Person(){

}

在拦截器中,您可以获得对绑定到当前线程上下文的 Person 的引用,并相应地设置标头:

public class PersonInterceptor extends HandlerInterceptorAdapter {

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
        Object handler, ModelAndView modelAndView) throws Exception {

        if(request.getMethod().equals("GET")){
            Person person = PersonContext.getPerson();
            response.addHeader("age",  person.getAge());
        }

        PersonContext.cleanUp();
    }
}

最后用 Spring Data Rest 注册你的拦截器:

@Bean
public MappedInterceptor myMappedInterceptor() {
    return new MappedInterceptor(new String[]{"person/**"}, new PersonInterceptor());
}

问题:

  • 加载后侦听器将在 Person 的每次加载时执行,即使在其他路径上也是如此,因此清理工作需要更好。在所有路径上注册第二个拦截器来处理清理??

另一种方法是在拦截器中重新查询数据库。所以上面可以用拦截器代替:

public class PersonInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private PersonRepository repository;

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {

        super.postHandle(request, response, handler, modelAndView);

        if(request.getMethod().equals("GET")){
           Long personID = // Extract id by examining request.getServletPath();
           Person person = repository.findOne(personID);
           response.addHeader("age",  person.getAge());
        }
    }
}

这会产生额外查询的开销,但可以通过配置 OpenEntityManagerInViewFilter 来避免这种情况,这意味着它是从一级缓存中获取的。

【讨论】:

    猜你喜欢
    • 2016-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 2016-08-27
    • 1970-01-01
    相关资源
    最近更新 更多