繁体   English   中英

无法将内容类型标头添加到SSE端点

[英]Unable to add Content-Type Header to SSE endpoint

我有一个Java服务器和一个JavaScript客户端,它们已经成功使用服务器发送事件来发送更新。 最近,我们被迫在使用内置于kubernetes中的NGINX作为服务器和客户端之间的代理之间切换,使用基于kong的代理,该代理以不同的配置扩展了NGINX。 新的代理会缓冲SSE破坏协议,并且由于其他应用程序正在使用新的代理,因此我无法关闭所有缓冲,这是以前的代理解决该问题的方式。 作为替代方案,我一直试图向SSE响应中添加3个http标头,以触发NGINX为该特定端点关闭缓冲:

"Content-Type" : "text/event-stream"
"Cache-Control", "no-cache"
"X-Accel-Buffering", "no"

我已经能够相对容易地添加“ Cache-Control”和“ X-Accel-Buffering”标头,但是我尝试了几种不同的方法来添加“ Content-Type”标头,但没有任何效果。

下面是第一次尝试,请注意,“ Content-Type”标头的设置与RestServiceImpl中的其他两个标头相同,但是从我的日志中,添加了另外两个标头,而“ Content-Type”却没有。

@Api
@Path("/service")
public interface RestService {

    @GET
    @Path("/sseconnect")
    @Produces(SseFeature.SERVER_SENT_EVENTS)
    EventOutput listenToBroadcast(@Context HttpServletResponse response);

}

@Component
public class RestServiceImpl implements RestService {

    @Autowired
    private Broadcaster broadcaster;

    public RestServiceImpl() {
    }

    @Override
    public EventOutput listenToBroadcast(HttpServletResponse response) {
        response.addHeader("Content-Type", "text/event-stream");
        response.addHeader("Cache-Control", "no-cache");
        response.addHeader("X-Accel-Buffering", "no");
        return broadcaster.add();
    }
}

public class Broadcaster extends SseBroadcaster {

    private final OutboundEvent.Builder eventBuilder =
        new OutboundEvent.Builder();

    public EventOutput add() {
        final EventOutput eventOutput = new EventOutput();
        super.add(eventOutput);
        return eventOutput;
    }

    public void sendEvents(Events events, String name) {
        final OutboundEvent outboundEvent = eventBuilder.name(name)
            .mediaType(MediaType.APPLICATION_JSON_TYPE)
            .data(Events.class, events).build();
        super.broadcast(outboundEvent);
    }
}

对于第二次尝试,我尝试修改“ Content-Type”标头的添加方式,如下所示:

@Override
public EventOutput listenToBroadcast(HttpServletResponse response) {
    response.setContentType("text/event-stream");
    response.addHeader("Cache-Control", "no-cache");
    response.addHeader("X-Accell-Buffering", no);
    return broadcaster.add();
}

这具有相同的效果,添加了“ Cache-Control”和“ X-Accell-Bufferig”,但未添加“ Content-Type”。

第三次尝试将HttpServletResponse替换为ContainerResponse

@Override
public EventOutput listenToBroadcast(ContainerResponse containerResponse)
{
    final MultivaluedMap<String, Object> headers =
        containerResponse.getHeaders();
    headers.add("Content-Type", "text/event-stream");
    headers.add("Cache-Control", "no-cache");
    headers.add("X-Accel-Buffering", "no");
    return broadcaster.add();
}

该解决方案破坏了端点并最终给我一个406错误,所以我不能确切地说它是行不通的。 可能是不好的实现,但我什么也没看到。

对于我的第四次尝试,我尝试使用ContainerResponseFilter添加标头。

@Provider
@PreMatching
public class SseResponseFilter implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext,
        ContainerResponseContext responseContext)
            throws IOException {

        final MultivaluedMap<String, Object> headers =
            responseContext.getHeaders();
        final List<Object> contentTypeValues = new ArrayList<Object>();
        contentTypeValues.add("text/event-stream");
        headers.put("Content-Type", contentTypeValues);

        final List<Object> cacheControlValues = new ArrayList<Object>();
        cacheControlValues.add("no-cache");
        headers.put("Cache-Control", cacheControlValues);

        final List<Object> xAccelBufferingValues =
            new ArrayList<Object>();
        xAccelBufferingValues.add("no");
        headers.put("X-Accel-Buffering", xAccelBufferingValues);
    }
}

该尝试将“ Cache-Control”和“ X-Accel-Buffering”标头添加到应用程序中的每个端点,但未在任何位置添加“ Content-Type”。 请注意,其中一些其他api返回的响应对象允许设置“ Content-Type”,并且这些REST端点为每个特定端点正确设置了“ Content-Type”头。 SSE库返回EventOutput,不幸的是,它不允许我像Response那样设置Content-Type标头。

对于第五次尝试,我用WriterInterceptor替换了ContainerResponseFilter

@Provider
public class SseWriterInterceptor implements WriterInterceptor {

    @Override
    public void arroundWriteTo(WriterInterceptorContext context)
        throws IOException, WebApplicationException {

        final MultivaluedMap<String, Object> headers =
            context.getHeaders();
        final List<Object> contentTypeValues = new ArrayList<Object>();
        contentTypeValues.add("text/event-stream");
        headers.put("Content-Type", contentTypeValues);

        final List<Object> cacheControlValues = new ArrayList<Object>();
        cacheControlValues.add("no-cache");
        headers.put("Cache-Control", cacheControlValues);

        final List<Object> xAccelBufferingValues =
            new ArrayList<Object>();
        xAccelBufferingValues.add("no");
        headers.put("X-Accel-Buffering", xAccelBufferingValues);
    }
}

该解决方案的工作原理与之前的类似,在该解决方案中,向应用程序的所有端点添加了“ Cache-Control”和“ X-Accel-Buffering”,但再次取消了“ Content-Type”。

综上所述,我似乎无法弄清楚是什么因素阻止了我向我的SSE端点成功添加“ Content-Type”标头。

这个问题类似于: 为什么Jersey会吞下我的“ Content-Encoding”标头,但是,给我添加“ Content-Type”到Response对象的解决方案不起作用,因为我使用的是SSE,它返回一个EventOutput对象,没有添加“ Content-Type”标头的方法。

事实证明,泽西岛不是未设置“ Content-Type”标头的罪魁祸首。 该应用程序是作为持续部署环境的一部分构建的,该环境针对日志记录和安全扫描之类的各种过滤器添加了过滤器。 这些过滤器之一是防止使用SSE库时填充“ Content-Type”标头。

与在基于kubernetes的开发环境中运行应用程序并在本地计算机上运行应用程序并看到本地第一个解决方案正确填充了“ Content-Type”标头相比,我能够确定这是原因。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM