简体   繁体   English

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

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

I have a Java server and a JavaScript client that have successfully been using Server Sent-Events to send updates. 我有一个Java服务器和一个JavaScript客户端,它们已经成功使用服务器发送事件来发送更新。 Recently we have been forced to switch between using NGINX built into kubernetes as a proxy between the server and the client to using a kong based proxy that extends NGINX with a different configuration. 最近,我们被迫在使用内置于kubernetes中的NGINX作为服务器和客户端之间的代理之间切换,使用基于kong的代理,该代理以不同的配置扩展了NGINX。 The new proxy buffers the SSE going through it breaking the protocol and because other applications are using the new proxy I am not allowed to turn off all buffering which was how the previous proxy got around the issue. 新的代理会缓冲SSE破坏协议,并且由于其他应用程序正在使用新的代理,因此我无法关闭所有缓冲,这是以前的代理解决该问题的方式。 As an alternative, I have been attempting to add 3 http headers to the SSE response to trigger NGINX to turn off buffering for this particular endpoint: 作为替代方案,我一直试图向SSE响应中添加3个http标头,以触发NGINX为该特定端点关闭缓冲:

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

I have been able to add the "Cache-Control" and "X-Accel-Buffering" headers relatively easily, but I have tried several different approaches to add the "Content-Type" header and nothing is working. 我已经能够相对容易地添加“ Cache-Control”和“ X-Accel-Buffering”标头,但是我尝试了几种不同的方法来添加“ Content-Type”标头,但没有任何效果。

Below is the first attempt, notice the "Content-Type" header is set identically to the other two headers in RestServiceImpl, but from my logs the other two headers are added while "Content-Type" is not. 下面是第一次尝试,请注意,“ 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);
    }
}

For my second attempt I tried modifying how the "Content-Type" header was added as follows: 对于第二次尝试,我尝试修改“ 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();
}

This had the same effect, "Cache-Control" and "X-Accell-Bufferig" were added, but "Content-Type" was not. 这具有相同的效果,添加了“ Cache-Control”和“ X-Accell-Bufferig”,但未添加“ Content-Type”。

For the third attempt I replaced HttpServletResponse with ContainerResponse 第三次尝试将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();
}

This solution broke the endpoint and ended up giving me a 406 error, so I can't say definitively that it wouldn't have worked. 该解决方案破坏了端点并最终给我一个406错误,所以我不能确切地说它是行不通的。 Could be a bad implementation but I am not seeing anything. 可能是不好的实现,但我什么也没看到。

For my fourth attempt I tried adding the headers with a ContainerResponseFilter. 对于我的第四次尝试,我尝试使用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);
    }
}

This attempt added the "Cache-Control" and "X-Accel-Buffering" headers to every endpoint in the application, but didn't add "Content-Type" anywhere. 该尝试将“ Cache-Control”和“ X-Accel-Buffering”标头添加到应用程序中的每个端点,但未在任何位置添加“ Content-Type”。 Note, some of these other api's were returning a Response object that allowed the "Content-Type" to be set and these REST endpoints were getting the "Content-Type" header set correctly for each particular endpoint. 请注意,其中一些其他api返回的响应对象允许设置“ Content-Type”,并且这些REST端点为每个特定端点正确设置了“ Content-Type”头。 The SSE library returns EventOutput which unfortunately doesn't let me set the Content-Type header like Response does. SSE库返回EventOutput,不幸的是,它不允许我像Response那样设置Content-Type标头。

For the fifth attempt I replaced the ContainerResponseFilter with a WriterInterceptor 对于第五次尝试,我用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);
    }
}

This solution worked like the previous where it added "Cache-Control" and "X-Accel-Buffering" to all endpoints in the application but left off "Content-Type" again. 该解决方案的工作原理与之前的类似,在该解决方案中,向应用程序的所有端点添加了“ Cache-Control”和“ X-Accel-Buffering”,但再次取消了“ Content-Type”。

To sum it all up I can't seem to figure out what is preventing me from successfully adding the "Content-Type" header to my SSE endpoint. 综上所述,我似乎无法弄清楚是什么因素阻止了我向我的SSE端点成功添加“ Content-Type”标头。

This question is similar to: Why does Jersey swallow my "Content-Encoding" header However, the given solution of adding the "Content-Type" to the Response object doesn't work because I am using SSE which returns a EventOutput object that does not have a way to add the "Content-Type" header. 这个问题类似于: 为什么Jersey会吞下我的“ Content-Encoding”标头,但是,给我添加“ Content-Type”到Response对象的解决方案不起作用,因为我使用的是SSE,它返回一个EventOutput对象,没有添加“ Content-Type”标头的方法。

It turns out Jersey was not the culprit behind the "Content-Type" header not being set. 事实证明,泽西岛不是未设置“ Content-Type”标头的罪魁祸首。 The application is built as part of a continuous deployment environment that tacks on a number of filters for things like logging and security scanning. 该应用程序是作为持续部署环境的一部分构建的,该环境针对日志记录和安全扫描之类的各种过滤器添加了过滤器。 One of these filters was preventing the "Content-Type" header from being populated when using the SSE library. 这些过滤器之一是防止使用SSE库时填充“ Content-Type”标头。

I was able to determine this was the cause after running the application on my local machine vs running it in its kubernetes based development environment and seeing the "Content-Type" header populate properly with the first solution in local. 与在基于kubernetes的开发环境中运行应用程序并在本地计算机上运行应用程序并看到本地第一个解决方案正确填充了“ Content-Type”标头相比,我能够确定这是原因。

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

相关问题 如何添加内容类型 header application/json 以取消映射后端点? - How to add content-type header application/json to void postmapping endpoint? 如何在Soap Web服务中添加“内容类型”标头 - How to add “content-type” header in soap webservice Spring是否更改Content-Type的标题? - Spring Changes Header for Content-Type? 如何使Content-Type标头为可选? - How to make Content-Type header optional? Camel Content-Type header 中的引号 - Quotation marks in Camel Content-Type header Header axios 中定义的内容类型不起作用 - Header Content-Type defined in axios not working Spring-更改内容类型HTTP标头 - Spring - changing Content-type HTTP header 即使不是文本MIME类型,Undertow也会始终向Content-Type标头添加charset - Undertow always add charset to Content-Type header even if it is not a text MIME type org.glassfish.jersey.message.internal.HeaderValueException:无法解析“Content-Type”标头值:“multipart/byteranges” - org.glassfish.jersey.message.internal.HeaderValueException: Unable to parse “Content-Type” header value: “multipart/byteranges” 如何在不指定内容类型的情况下将 JSON POST/PUT 到 REST API 端点:application/json header - How do I POST/PUT JSON to a REST API endpoint without specifying Content-Type: application/json header
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM