【问题标题】:How do I get Jersey Test/Client to not fill in a default Accept header?如何让 Jersey 测试/客户端不填写默认的 Accept 标头?
【发布时间】:2017-04-15 12:20:45
【问题描述】:

我正在尝试以特定方式处理没有 Accept 标头的请求,但无论我做什么,泽西岛似乎都一心想要填写一个,所以看起来请求总是有一个 @987654323 @header,即使没有。

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;

import static org.junit.Assert.assertEquals;

public class JerseyTestTest extends JerseyTest {

    @Path("hello")
    public static class HelloResource {
        @GET
        public String getHello(@Context HttpHeaders httpHeaders) {
            String acceptHeader = httpHeaders.getHeaderString(HttpHeaders.ACCEPT);
            return acceptHeader != null ? acceptHeader : "No Accept Header";
        }
    }

    @Override
    protected Application configure() {
        return new ResourceConfig(HelloResource.class);
    }

    @Test
    public void test() {
        final String hello = target("hello").request()
                .header(HttpHeaders.ACCEPT, null) // null means remove header
                .get(String.class);
        assertEquals("No Accept Header", hello);
    }
}

此测试结果:

org.junit.ComparisonFailure: 
Expected :No Accept Header
Actual   :text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

不知何故,text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 的默认 Accept 标头在某处设置。它没有记录,我很想弄清楚如何禁用它。我查看了 Jersey 的源代码,但似乎无法找到发生这种情况的位置或原因。

更新:当我使用 curl 访问没有 Accept 标头的端点时,没有生成 Accept 标头,因此问题出在 Jersey 客户端或 Jersey 测试环境中。

更新 2:使用默认的 Grizzly2 测试容器或 JDK 测试容器时会出现此错误,但不会出现在内存测试容器中。

【问题讨论】:

  • 它可能在为请求创建 UserAgent 时设置。对此有很好的见解tools.ietf.org/html/rfc7231#section-5.3.2。为了解决上述问题,您可以尝试在 Resource 类中使用 null/空值覆盖 Accept 标头吗?
  • @nullpointer 我在 Jersey 中找到了为请求添加 User-Agent 的代码,但我没有看到附近有任何添加 Accept 标头的代码,所以不,那不是它被添加的时候。我确切地知道 Accept 标头的用途,并且我已经阅读了 RFC 并经常参考它们。我不知道覆盖 Resource 类中的 Accept 标头是什么意思。 Resource 类是服务器上的内容,Jersey 文档中没有关于覆盖标头的内容,我也不想替换我收到的 Accept 标头。我想看看 Accept 标头是什么,或者是否没有。
  • 您是否尝试过使用 wget 或 curl 作为客户端? @Test 方法究竟做了什么?
  • @gsl 我用 curl 单独测试了端点,没有生成 Accept 标头。我在票证中添加了一条注释,因此生成的 Accept 标头必须发生在 Jersey 测试/客户端代码中的某处。
  • 我想你从来没有想过这个问题?我有同样的问题。

标签: java jersey jersey-client jersey-test-framework


【解决方案1】:

不确定这是否对其他人有帮助,但我们看到类似的行为在使用我们的客户端代码调用生产中的服务。我们使用的是泽西岛 2.21.1。

扩展原帖中的代码,发现以下是真的:

  • 如果Accept 标头为空,则 Jersey 添加默认值
  • 如果Accept 标头为空String,则使用空Accept 标头
  • 如果Accept 标头有一个值,则使用它

我不确定是否有办法告诉 Jersey 在使用 null 时不要添加默认值。

public class JerseyAcceptHeaderTest extends JerseyTest {

    @Path("hello")
    public static class HelloResource {
        @GET
        public String getHello(@Context HttpHeaders httpHeaders) {
            String acceptHeader = httpHeaders.getHeaderString(HttpHeaders.ACCEPT);
            System.out.println("SERVER RECEIVED:" + acceptHeader);

            if (acceptHeader == null) {
                return "Null Accept Header";
            } else if (acceptHeader.equals("")) {
                return "No Accept Header";
            } else {
                return acceptHeader;
            }
        }
    }

    @Override
    protected Application configure() {
        return new ResourceConfig(HelloResource.class);
    }

    /**
     * this seems to be a bug in Jersey
     * it overrides a null Accept header
     */
    @Test
    public void test_accept_header_with_null() {
        final String acceptHeader = target("hello").request()
                .header(HttpHeaders.ACCEPT, null)
                .get(String.class);
        assertEquals("Null Accept Header", acceptHeader);
    }

    @Test
    public void test_accept_header_with_empty_string() {
        final String acceptHeader = target("hello").request()
                .header(HttpHeaders.ACCEPT, "")
                .get(String.class);
        assertEquals("No Accept Header", acceptHeader);
    }

    @Test
    public void test_accept_header_with_spaced_string() {
        final String acceptHeader = target("hello").request()
                .header(HttpHeaders.ACCEPT, "  ")
                .get(String.class);
        assertEquals("No Accept Header", acceptHeader);
    }

    @Test
    public void test_accept_header_with_value() {
        final String acceptHeader = target("hello").request()
                .header(HttpHeaders.ACCEPT, "application/json")
                .get(String.class);
        assertEquals("application/json", acceptHeader);
    }

}

【讨论】:

    【解决方案2】:

    我现在面临同样的问题,经过一番研究,我最终找到了问题。

    WebResourceFactory.invoke(final Object proxy, final Method method, final Object[] {
        ...
        Invocation.Builder builder = newTarget.request()
                .headers(headers) // this resets all headers so do this first
                .accept(accepts); // if @Produces is defined, propagate values into Accept header; empty array is NO-OP
        ...
    }
    

    请求生成器会自动将 API 中指定的媒体类型 (@Produce) 添加到现有标头中。可悲的是,我没有找到禁用该行为的方法。 因此,我将尝试扩展用于创建我的客户端的 WebResourceFactory 类(它是最终的)并覆盖该方法调用 以不调用请求构建器上的接受

    请注意,我不认为这是一种解决方案,而是一种解决方法

    [编辑] 由于 WebResourceFactory 是最终的,我复制了它的内容并删除了请求生成器上的 accept 调用。有点丑,我知道,希望有人能找到更好的方法。

    【讨论】:

      【解决方案3】:

      我也实施了一种解决方法。如果将 Accept 标头添加到标头列表的方法的输入:

      public void accept(final String... types)

      来自ClientRequest 类,为空,我调用WebTarget.request() 而不是WebTarget.request().accept(inputValue)

      在我看来,如果输入为 null 或空字符串,则不应设置 Accept 标头。因此,我为该主题创建了一个问题@Jersey:https://github.com/eclipse-ee4j/jersey/issues/4407

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-04-28
        • 1970-01-01
        • 2015-07-20
        • 2018-12-12
        • 2018-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多