2013-08-09 17 views
13

ich einen Web-Service zu schreiben bin versucht, die die Content-Length-Header erfordert den folgenden Code festgelegt werden unter Verwendung von:Jersey 2.0 Content-Length nicht gesetzt

// EDIT: added apache connector code 
ClientConfig clientConfig = new ClientConfig(); 
ApacheConnector apache = new ApacheConnector(clientConfig); 

// setup client to log requests and responses and their entities 
client.register(new LoggingFilter(Logger.getLogger("com.example.app"), true)); 

Part part = new Part("123"); 
WebTarget target = client.target("https://api.thing.com/v1.0/thing/{thingId}"); 
Response jsonResponse = target.resolveTemplate("thingId", "abcdefg") 
       .request(MediaType.APPLICATION_JSON) 
       .header(HttpHeaders.AUTHORIZATION, "anauthcodehere") 
       .post(Entity.json(part)); 

in den Versionshinweisen https://java.net/jira/browse/JERSEY-1617 und Jersey 2.0 Dokumentation https://jersey.java.net/documentation/latest/message-body-workers.html bedeutet, dass Content-Length automatisch eingestellt wird. Ich bekomme jedoch einen 411-Antwortcode vom Server zurück, der anzeigt, dass Inhaltslänge nicht in der Anfrage vorhanden ist.

Kennt jemand den besten Weg, um den Content-Length Header gesetzt zu bekommen?

Ich habe durch Einrichten eines Protokollierers überprüft, dass der Content-Length-Header in der Anforderung nicht generiert wird.

Danke.

+0

Sie möchten möglicherweise überprüfen, ob der Anforderungsinhalt die Inhaltslänge hat oder nicht. –

+1

Aktivieren Sie die Protokollierung, um die Anforderung zu überprüfen ('client.addfilter (new LoggingFilter (System.out))'), damit Sie sicher sein können, dass das Problem an Ihrem Ende liegt. – DannyMo

+0

Ich habe vergessen zu erwähnen, dass ich einen Logger eingerichtet und überprüft habe, dass der Content-Length-Header nicht generiert wird. Ich habe meine Frage bearbeitet, um die neuen Informationen zu berücksichtigen. – Todd

Antwort

5

Ich habe einen schnellen Test mit Jersey Client 2.2 und Netcat ausgeführt, und es zeigt mir, dass Jersey den Content-Length-Header sendet, obwohl der LoggingFilter es nicht meldet.

Um diesen Test durchzuführen, habe ich zuerst netcat in einer Shell ausgeführt.

nc -l 8090 

Dann habe ich den folgenden Jersey-Code in einer anderen Shell ausgeführt.

Response response = ClientBuilder.newClient() 
    .register(new LoggingFilter(Logger.getLogger("com.example.app"), true)) 
    .target("http://localhost:8090/test") 
    .request() 
    .post(Entity.json(IOUtils.toInputStream("{key:\"value\"}"))); 

Nach dem Ausführen dieses Codes werden die folgenden Zeilen protokolliert.

INFO: 1 * LoggingFilter - Request received on thread main 
1 > POST http://localhost:8090/test 
1 > Content-Type: application/json 
{key:"value"} 

Netcat meldet jedoch mehrere Header in der Nachricht.

POST /test HTTP/1.1 
Content-Type: application/json 
User-Agent: Jersey/2.0 (HttpUrlConnection 1.7.0_17) 
Host: localhost:8090 
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 
Connection: keep-alive 
Content-Length: 13 

{key:"value"} 

Ich habe diesen Test auf OSX mit Java6 und Java7 ausgeführt, mit den gleichen Ergebnissen. Ich habe auch den Test in Jersey 2.0 mit ähnlichen Ergebnissen durchgeführt.

+0

Ich werde versuchen, dies heute zu überprüfen und dann die Prämie zu vergeben. Es klingt nicht so, das erklärt, warum das OP die 411s bekam, aber es befriedigt meine eigene Neugierde. Ich dachte nicht zu überprüfen, ob die Inhaltslänge nach dem Logging-Filter hinzugefügt wurde. Vielen Dank! – DannyMo

+0

Jersey greift diese Funktionalität zumindest in meiner Umgebung aus dem Java-HTTPUrlConnection-Objekt auf. Das OP kann einen anderen HTTP-Client konfiguriert haben, der HTTP/1.0 verwendet, wobei Content-Length-Header (oder Chunking) nicht benötigt werden. –

+0

Ich habe zum ApacheConnector gewechselt, um ein Problem zu umgehen, bei dem der Standardkonnektor eine Ausnahme für einen WWW-Authenticate-Header ausgelöst hat, der mit dem Apache-Http-Client problemlos funktioniert. Ich habe das Problem mit dem ApacheConnector gefunden und habe schließlich meinen eigenen Konnektor geschrieben, der es dem Apache HTTP-Client erlaubt, die Inhaltslänge korrekt einzustellen. – Todd

3

Nach dem Blick auf den Quellcode für die ApacheConnector-Klasse, sehe ich das Problem. Wenn eine ClientRequest in eine HttpUriRequest konvertiert wird, wird eine private Methode getHttpEntity() aufgerufen, die eine HttpEntity zurückgibt. Leider gibt dies eine HttpEntity zurück, deren getContentLength() immer eine -1 zurückgibt.

Wenn der Apache-HTTP-Client die Anforderung erstellt, wird das HttpEntity-Objekt für eine Länge konsultiert, und da es -1 zurückgibt, wird keine Content-Length-Kopfzeile festgelegt.

Ich löste mein Problem durch Erstellen eines neuen Connectors, der eine Kopie des Quellcodes für ApacheConnector ist, aber eine andere Implementierung der getHttpEntity() hat. Ich lese die Entität von der ursprünglichen ClientRequest in ein Bytearray und umgebe dann dieses Bytearray mit einem ByteArrayEntity. Wenn der Apache HTTP-Client die Anfrage erstellt, wird er die Entität konsultieren und die ByteArrayEntity wird mit der richtigen Inhaltslänge antworten, was wiederum ermöglicht, dass der Header Content-Length gesetzt wird.

Hier ist der entsprechende Code:

private HttpEntity getHttpEntity(final ClientRequest clientRequest) { 
    final Object entity = clientRequest.getEntity(); 

    if (entity == null) { 
     return null; 
    } 

    byte[] content = getEntityContent(clientRequest); 

    return new ByteArrayEntity(content); 
} 


private byte[] getEntityContent(final ClientRequest clientRequest) { 

    // buffer into which entity will be serialized 
    final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 

    // set up a mock output stream to capture the output 
    clientRequest.setStreamProvider(new OutboundMessageContext.StreamProvider() { 

     @Override 
     public OutputStream getOutputStream(int contentLength) throws IOException { 
      return baos; 
     } 
    }); 

    try { 
     clientRequest.writeEntity(); 
    } 
    catch (IOException e) { 
     LOGGER.log(Level.SEVERE, null, e); 
     // re-throw new exception 
     throw new ProcessingException(e); 
    } 

    return baos.toByteArray(); 
} 

WARNUNG: Mein Problem mit beschränktem Platzangebot und enthielt nur kleine Einheit Körper als Teil der Anforderungen.Diese oben vorgeschlagene Methode kann bei großen Entitätsgruppierungen wie Bildern problematisch sein, daher denke ich nicht, dass dies eine allgemeine Lösung für alle ist.

3

Dies wird in Jersey 2.5 (https://java.net/jira/browse/JERSEY-2224) unterstützt. Sie könnten https://jersey.java.net/apidocs/latest/jersey/org/glassfish/jersey/client/RequestEntityProcessing.html#BUFFERED verwenden, um Ihre Inhalte zu streamen. Ich stelle ein einfaches Beispiel zusammen, das sowohl Chunked- als auch Pufferinhalte mit ApacheConnector zeigt. Kasse dieses Projekt: https://github.com/aruld/sof-18157218

public class EntityStreamingTest extends JerseyTest { 

    private static final Logger LOGGER = Logger.getLogger(EntityStreamingTest.class.getName()); 

    @Path("/test") 
    public static class HttpMethodResource { 
    @POST 
    @Path("chunked") 
    public String postChunked(@HeaderParam("Transfer-Encoding") String transferEncoding, String entity) { 
     assertEquals("POST", entity); 
     assertEquals("chunked", transferEncoding); 
     return entity; 
    } 

    @POST 
    public String postBuffering(@HeaderParam("Content-Length") String contentLength, String entity) { 
     assertEquals("POST", entity); 
     assertEquals(entity.length(), Integer.parseInt(contentLength)); 
     return entity; 
    } 
    } 

    @Override 
    protected Application configure() { 
    ResourceConfig config = new ResourceConfig(HttpMethodResource.class); 
    config.register(new LoggingFilter(LOGGER, true)); 
    return config; 
    } 

    @Override 
    protected void configureClient(ClientConfig config) { 
    config.connectorProvider(new ApacheConnectorProvider()); 
    } 

    @Test 
    public void testPostChunked() { 
    Response response = target().path("test/chunked").request().post(Entity.text("POST")); 

    assertEquals(200, response.getStatus()); 
    assertTrue(response.hasEntity()); 
    } 

    @Test 
    public void testPostBuffering() { 
    ClientConfig cc = new ClientConfig(); 
    cc.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED); 
    cc.connectorProvider(new ApacheConnectorProvider()); 
    JerseyClient client = JerseyClientBuilder.createClient(cc); 
    WebTarget target = client.target(getBaseUri()); 
    Response response = target.path("test").request().post(Entity.text("POST")); 

    assertEquals(200, response.getStatus()); 
    assertTrue(response.hasEntity()); 
    } 
} 
3

ich mit Jersey 2.25.1 eine einfachere Lösung getestet haben, die bei der Einrichtung setChunkedEncodingEnabled(false) in der Jersey-Client-Konfiguration besteht. Anstatt eine Chunked-Codierung zu verwenden, wird die gesamte Entität im Speicher serialisiert und die Inhaltslänge wird in der Anforderung festgelegt.

Als Referenz hier ist ein Beispiel für eine Konfiguration, die ich verwendet habe:

private Client createJerseyClient(Environment environment) { 
    Logger logger = Logger.getLogger(getClass().getName()); 
    JerseyClientConfiguration clientConfig = new JerseyClientConfiguration(); 
    clientConfig.setProxyConfiguration(new ProxyConfiguration("localhost", 3333)); 
    clientConfig.setGzipEnabled(false); 
    clientConfig.setGzipEnabledForRequests(false); 
    clientConfig.setChunkedEncodingEnabled(false); 
    return new JerseyClientBuilder(environment) 
      .using(clientConfig) 
      .build("RestClient") 
      .register(new LoggingFeature(logger, Level.INFO, null, null)); 
} 

I mitmproxy verwendet haben die Request-Header und die Content-Length Header gesetzt wurde korrekt zu verifizieren.

Verwandte Themen