泽西客户:向请求添加Cookie

我正在尝试编写一个使用Jersey Client API访问RESTful Web服务的库。 该服务需要一个设置cookie的登录请求,然后后续请求必须将该cookie设置为成功。 登录请求按预期工作,我能够从登录中检索响应中的cookie,但似乎无法在后续请求中添加cookie。 任何人都可以告诉我可能做错了什么。 以下是发出请求的代码:

MultivaluedMap qs = new MultivaluedMapImpl(); qs.add( "xml", this.toXmlString() ); WebResource wr = client.resource( Constants.ServiceURL ); if ( CookieJar.Cookies != null ) { for ( NewCookie c : CookieJar.Cookies ) { logger.debug( "Setting cookie " + c.getName() ); wr.cookie( c ); } } ClientResponse response = wr.queryParams( qs ).get( ClientResponse.class ); 

虽然请求不会失败,但服务会响应应用程序错误“No Session”。 以下是请求序列的日志跟踪:

 Jul 15, 2011 5:20:33 PM com.sun.jersey.api.client.filter.LoggingFilter log INFO: 1 * Client out-bound request 1 > GET https://www.company.com/TrackerXMLInterface.asp?xml=%3Cxml%3E%3CTRANTYPE%3ELOGIN%3C/TRANTYPE%3E%3CTRANPARMS%3E%3CLOGINID%3Emylogin%3C/LOGINID%3E%3CPASSPHRASE%3EBa1b2c3%3C/PASSPHRASE%3E%3C/TRANPARMS%3E%3C/xml%3E Jul 15, 2011 5:20:35 PM com.sun.jersey.api.client.filter.LoggingFilter log INFO: 1 * Client in-bound response 1 < 200 1 < Date: Fri, 15 Jul 2011 22:20:35 GMT 1 < Content-Length: 150 1 < Set-Cookie: ASPSESSIONIDSUBSBSRR=GBGOKGJDAAHCNDDHPBFICFIH; secure; path=/ 1 < Content-Type: text/html 1 < Server: Microsoft-IIS/7.0 1 < X-Powered-By: ASP.NET 1 < Cache-Control: private 1 < L00Valid Login  [continued below] 

我认为以下请求应该在标题中包含cookie:

 Jul 15, 2011 5:20:35 PM com.sun.jersey.api.client.filter.LoggingFilter log INFO: 1 * Client out-bound request 1 > GET https://www.company.com/TrackerXMLInterface.asp?xml=%3Cxml%3E%3CTRANTYPE%3ESSNLAST%3C/TRANTYPE%3E%3CTRANPARMS%3E%3CSSN%3E123456789%3C/SSN%3E%3CLASTNAME%3ESchmoe%3C/LASTNAME%3E%3C/TRANPARMS%3E%3C/xml%3E Jul 15, 2011 5:20:35 PM com.sun.jersey.api.client.filter.LoggingFilter log INFO: 1 * Client in-bound response 1 < 200 1 < Date: Fri, 15 Jul 2011 22:20:35 GMT 1 < Content-Length: 150 1 < Set-Cookie: ASPSESSIONIDSUBSBSRR=HBGOKGJDIAPBBEIGLOILDJDN; secure; path=/ 1 < Content-Type: text/html 1 < Server: Microsoft-IIS/7.0 1 < X-Powered-By: ASP.NET 1 < Cache-Control: private 1 < R04No Session  

对此的任何指导都非常感谢。

问题是WebResource是不可变的 – cookie()方法返回WebResource.Builder。 因此,每次调用cookie时,执行以下操作只会创建WebResource.Builder的新实例(并且根本不会修改原始WebResource)。 您忽略这些Builder实例并仍然在原始WebResource上执行请求:

 for ( NewCookie c : CookieJar.Cookies ) { logger.debug( "Setting cookie " + c.getName() ); wr.cookie( c ); } 

您应该执行以下操作:

 WebResource.Builder builder = wr.getRequestBuilder(); for (NewCookie c : CookieJar.Cookies) { builder = builder.cookie(c); } 

然后你可以通过以下方式提出请求:

 ClientResponse response = builder.queryParams(qs).get(ClientResponse.class); 

另外,为了避免在所有资源方法中复制此代码,您可能需要考虑编写一个客户端filter,它将为您执行所有请求。 例如,以下代码将确保为每个响应设置从服务器发送的cookie:

 client.addFilter(new ClientFilter() { private ArrayList cookies; @Override public ClientResponse handle(ClientRequest request) throws ClientHandlerException { if (cookies != null) { request.getHeaders().put("Cookie", cookies); } ClientResponse response = getNext().handle(request); if (response.getCookies() != null) { if (cookies == null) { cookies = new ArrayList(); } // simple addAll just for illustration (should probably check for duplicates and expired cookies) cookies.addAll(response.getCookies()); } return response; } }); 

注意:这仅在您不共享具有多个线程的客户端实例时才有效!

我发现确保cookie被发送回的一种更简单的方法是使用jersey-client的Apache HTTP客户端集成。 它可以在maven包中找到jersey-apache-client:

  com.sun.jersey.contribs jersey-apache-client 1.13 jar  

然后你可以这样做:

 ApacheHttpClientConfig config = new DefaultApacheHttpClientConfig(); config.getProperties().put(ApacheHttpClientConfig.PROPERTY_HANDLE_COOKIES, true); ApacheHttpClient client = ApacheHttpClient.create(config); 

从那时起,只需在请求中继续使用相同的客户端,cookie将被收集并按预期发送回服务器。

也许您需要将Cookie放入之前没有它们的WebResource调用中。 然后,您可能会发现自己打破了可以与构建器一起使用的代码行。 要包含Cookie,您的代码可能来自:

 clientResponse = webResource.queryParams(parameters).type(httpContentType).accept(httpAcceptType).post(ClientResponse.class, requestBody); 

至:

 builder = webResource.queryParams(parameters).type(httpContentType); if (cookieJar != null) { for (Cookie c : cookieJar) builder = builder.cookie(c); } clientResponse = builder.accept(httpAcceptType).post(ClientResponse.class, requestBody);