对于其余接口,使用从控制器返回的 Spring MVC + RxJava + DeferredResult 。
我在考虑为端点添加 Hateoas 支持。自然的选择是 Spring Hateoas 。问题是 Spring Hateoas 在异步/多线程环境中不起作用,因为它使用 ThreadLocal 。
有没有办法解决这个约束?我不这么认为,但也许有人有任何建议。
是否有人使用其他API将 Hateoas 支持添加到其他端点?
谢谢。
答案 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());
在这些改变之后,一切似乎都有效,但当然看起来很乱。我希望有人可以提供更优雅的解决方案。