Spring MVC + DeferredResult添加了Hateoas的东西

时间:2015-02-16 11:27:54

标签: spring spring-mvc spring-hateoas

对于其余接口,使用从控制器返回的 Spring MVC + RxJava + DeferredResult

我在考虑为端点添加 Hateoas 支持。自然的选择是 Spring Hateoas 。问题是 Spring Hateoas 在异步/多线程环境中不起作用,因为它使用 ThreadLocal

有没有办法解决这个约束?我不这么认为,但也许有人有任何建议。

是否有人使用其他API将 Hateoas 支持添加到其他端点?

谢谢。

2 个答案:

答案 0 :(得分:4)

所以我使用的解决方案是关闭请求属性,然后将它们作为电梯运营商的一部分应用

public class RequestContextStashOperator<T> implements Observable.Operator<T, T> {

    private final RequestAttributes attributes;

    /**
     * Spring hateoas requires the request context to be set but observables may happen on other threads
     * This operator will reapply the context of the constructing thread on the execution thread of the subscriber
     */
    public RequestContextStashOperator() {
        attributes = RequestContextHolder.currentRequestAttributes();
    }
    @Override
    public Subscriber<? super T> call(Subscriber<? super T> subscriber) {
        return new Subscriber<T>() {
            @Override
            public void onCompleted() {
                subscriber.onCompleted();
            }

            @Override
            public void onError(Throwable e) {
                subscriber.onError(e);
            }

            @Override
            public void onNext(T t) {
                RequestContextHolder.setRequestAttributes(attributes);
                subscriber.onNext(t);
            }
        };
    }
}

然后你可以在像

这样的可观察对象上使用它
lift(new RequestContextStashOperator<>())

只要在与请求相同的线程中创建对象。然后,您可以在可观察链中使用地图将您的对象映射为资源并添加您的hateoas链接。

答案 1 :(得分:2)

所以答案有点晚,但可能有人会发现它很有用。 你是关于ThreadLocal的 - 如果你在不同的线程中生成hateoas链接,那么它会失败并出现异常。我找到了一些解决方法:

@RequestMapping(path = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
DeferredResult<ResponseEntity<ProductResource>> example(@PathVariable("id") final String productId, final HttpServletRequest request) {

    final DeferredResult<ResponseEntity<ProductResource>> deferredResult = new DeferredResult<>();

    request.setAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE, request.getContextPath());
    final RequestAttributes requestAttributes = new ServletRequestAttributes(request);

    productByIdQuery.byId(UUID.fromString(productId)).subscribe(productEntity -> {
        RequestContextHolder.setRequestAttributes(requestAttributes);
            deferredResult.setResult(result, HttpStatus.OK))
    }, deferredResult::setErrorResult);

    return deferredResult;
}

如您所见,我保存RequestAttributes,以便稍后在回调中设置它们。这只解决了部分问题 - 您将获得另一个异常,因为您将松开contextPath属性。为避免这种情况,请明确保存:

request.setAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE, request.getContextPath());

在这些改变之后,一切似乎都有效,但当然看起来很乱。我希望有人可以提供更优雅的解决方案。