JAX-RS AsyncResponse.resume() 位置 header

JAX-RS AsyncResponse.resume() with Location header

更新

更多的挖掘表明抛出的异常被丢弃,实际问题是注入的 UriInfo 无法在 AsyncResponse 的线程中解决!

AsyncResponse.resume() 期间访问 @Context UriInfo uriInfo; 会得到以下 LoggableFailure 的消息:

Unable to find contextual data of type: javax.ws.rs.core.UriInfo

原创

根据 RFC 7231 HTTP/1.1 Semantics and ControlPOST 应该 return 201 CREATED 并在 header:

中提供新资源的位置

the origin server SHOULD send a 201 (Created) response containing a Location header field that provides an identifier for the primary resource created (Section 7.1.2) and a representation that describes the status of the request while referring to the new resource(s).

编写同步 REST 服务器时,javax.ws.rs.core.Response 提供 Response.created() shorthand 正是这样做的。

我会保存新实体,构建一个 URI 和 return

return Response.created(createURL(created)).build();

但是,当我切换到使用

的异步方法时

@Suspended javax.ws.rs.container.AsyncResponse

客户端的 HTTP 请求将无限挂起:

@POST
public void createUser(@Valid User user, @Suspended AsyncResponse asyncResponse) {
    executorService.submit(() -> {
        User created = userService.create(user);
        asyncResponse.resume(
                Response.created(createURL(created)).build()
        );
    });
}

通过trial-and-error发现修改位置header负责。 如果我 return 我的实体并设置 201 Created,而不触及 header,请求最终将解析:

@POST
public void createUser(@Valid User user, @Suspended AsyncResponse asyncResponse) {
    executorService.submit(() -> {
        User created = userService.create(user);
        asyncResponse.resume(
                Response.status(Status.CREATED).entity(created).build()  //this works
                //Response.created(createURL(created)).build()
        );
    });
}

那么问题出在哪里呢?我是不是理解错了?

我是 运行 GlassFish4.1 上的 RestEasy 如果您需要更多信息,请发表评论!

编辑

只要我更改任何 link 或 header,请求就会挂起。

如果有人遇到同样的问题:

问题是我通过使用 .getAbsolutePathBuilder().

的注入 @Context UriInfo uriInfo 创建了位置 header

该方法适用于同步服务器,因为访问 UriInfo 的线程仍然具有相同的请求上下文。

但是,当我切换到异步方法时,最终必须访问 uriInfo.getAbsolutePathBuilder() 的底层 Runnable 不在任何上下文中 - 因此抛出一个停止进一步执行的异常。

解决方法:

在任何应该 return 位置 header 的异步方法中,我 .getAbsolutePathBuilder() 仍在上下文中。然后可以在异步 运行:

中使用 UriBuilder 实现
@POST
public void createUser(@Valid User user, @Suspended AsyncResponse asyncResponse) {
    UriBuilder ub = uriInfo.getAbsolutePathBuilder();

    executorService.submit(() -> {
        User created = userService.create(user);
        asyncResponse.resume(
                Response.created(createURL(ub, created)).build()
        );
    });
}

private URI createURL(UriBuilder builder, ApiRepresentation entity) {
    return builder.path(entity.getId().toString()).build();
}