当前位置: 首页 > 知识库问答 >
问题:

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

桂和同
2023-03-14

我有一个Java服务器和一个JavaScript客户端,成功地使用服务器发送事件来发送更新。最近,我们被迫在使用内置在kubernetes中的NGINX作为服务器和客户端之间的代理与使用基于Kong的代理之间进行切换,该代理以不同的配置扩展NGINX。新的代理缓冲上交所通过它打破了协议,因为其他应用程序正在使用新的代理,我不允许关闭所有缓冲,这是以前的代理如何绕过这个问题。作为一种选择,我一直试图向SSE响应添加3个超文本传输协议标头,以触发NGINX关闭此特定endpoint的缓冲:

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

我已经能够相对容易地添加“缓存控制”和“X-加速缓冲”标头,但是我尝试了几种不同的方法来添加“内容类型”标头,没有任何效果。

下面是第一次尝试,请注意,“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();
}

这具有相同的效果,添加了“缓存控制”和“X-Accell-Bufferig”,但没有添加“内容类型”。

对于第三次尝试,我将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();
}

这个解决方案破坏了endpoint,最后给了我一个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);
    }
}

此尝试将“缓存控制”和“X-Accel-Buffering”头添加到应用程序中的每个endpoint,但没有在任何位置添加“内容类型”。注意,这些其他api中的一些正在返回允许设置“内容类型”的响应对象,而这些剩余endpoint正在为每个特定endpoint正确设置“内容类型”头。SSE库返回EventOutput,很遗憾,它不允许我像Response一样设置内容类型头。

在第五次尝试中,我将ContainerResponseFilter替换为WriterInterceptor

@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);
    }
}

此解决方案与之前的解决方案类似,在之前的解决方案中,它向应用程序中的所有endpoint添加了“缓存控制”和“X-加速缓冲”,但再次放弃了“内容类型”。

总而言之,我似乎无法找出是什么阻止我成功地将“Content-Type”头添加到SSEendpoint。

这个问题类似于:Jersey为什么要吞下我的“Content Encoding”头?然而,将“Content Type”添加到响应对象的给定解决方案不起作用,因为我使用的是SSE,它返回的EventOutput对象无法添加“Content Type”头。

共有1个答案

弘涛
2023-03-14

事实证明,泽西并不是未设置“内容类型”标题的罪魁祸首。该应用程序是作为连续部署环境的一部分构建的,该环境为日志记录和安全扫描等事项添加了大量过滤器。其中一个过滤器是防止在使用SSE库时填充“Content Type”标题。

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

 类似资料:
  • 我无法在HttpClient上设置内容类型。我接着问了一个问题:如何为HttpClient请求设置Content-Type头?但还是没有运气。 所以我怎么能强迫它,这样它就真的添加了它呢?提前道谢。

  • 以下代码使用CORS协议完美地发送请求并接收响应。我想将内容类型标头更改为“应用程序/json”而不是“应用程序/x-www-form-urlencoded”。但是,当我将内容类型替换为“应用程序/json”时,请求失败。当我在浏览器中检查请求时,我可以看到内容类型标头已被删除而不是更改。 来自以上代码的请求 当“应用程序/x-web-form-urlencoded”被替换为“应用程序/json”

  • 我试图配置标题'Content-Type'从空手道,我不能这样做。我尝试了*configure header={'Content-Type':'application/json;charset=utf-8'}和*header Content-Type='application/json;charset=utf-8'。但是在我打的post电话中,我总是得到content-type:text/plai

  • 我使用GraphQL. Client Nuget包调用需要Content-Type头的Graphql API。以下是我正在做的 设置GraphQL选项。注:我已经设置了选项。媒体类型 初始化客户端和授权标头 GraphQL查询 调用API并检索响应结果 但是,我收到错误的请求,错误为“{”errors:[{”message:“必须提供查询字符串。”}]}” 我做错了什么?如何正确设置内容类型标题。

  • 我正在尝试生成一个带有授权标头的HTTP请求: 但此代码生成的请求不包含授权头: Cache-Control:无缓存 access-control-request-method: 来源:http://localhost:3021 接受语言:en-US,en;q=0.8,he;q=0.6 我做错了什么?

  • 问题内容: 为了避免未知,我一直试图避免使用大多数HTTP协议的属性。 但是,我对自己说,今天我将面对恐惧,开始有目的地使用标题。我一直在尝试将数据发送到浏览器并立即使用。例如,如果我有一个处于就绪状态4的Ajax处理程序函数,如下所示: 并且我在PHP代码中设置了content-type标头: 当清楚地告诉浏览器传入数据为时,为什么不能直接从处理程序函数访问该属性? 问题答案: 该头只是作为您的