jax-ws change Content-type to Content-Type because

2020-02-25 09:16发布

问题:

I have to connect to a poorly implemented server that only understands Content-Type (capital-T) and not Content-type. How can I ask my JAX-WS client to send Content-Type?

I've tried:

Map<String, List<String>> headers = (Map<String, List<String>>)
((BindingProvider)port).getRequestContext().get(MessageContext.HTTP_REQUEST_HEADERS);

But headers is null. What am I doing wrong?

回答1:

I have to connect to a poorly implemented server that only understands Content-Type(capital-T) and not Content-type. How can I ask my jax-ws client to send Content-Type?

I've dug this question a bit more and, sadly, I'm afraid the answer is: you can't. Let me share my findings.

First, the code that you'll find in https://jax-ws.dev.java.net/guide/HTTP_headers.html does not give you access to the HTTP headers of the future HTTP request (that hasn't been created at this point), it allows you to set additional HTTP headers for making a request (that will be added to the HTTP request later).

So, don't expect the following code to not return null if you don't put anything before (and actually, you'll only get what you put in there):

((BindingProvider)port).getRequestContext().get(MessageContext.HTTP_REQUEST_HEADERS);

Then, I did a little test based on the code provided in the same link:

AddNumbersImplService service = new AddNumbersImplService();
AddNumbersImpl port = service.getAddNumbersImplPort();

((BindingProvider)port).getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS,
    Collections.singletonMap("X-Client-Version",Collections.singletonList("1.0-RC")));

port.addNumbers(3, 5);

And this is what I see in the HTTP request when running the client code:

POST /q2372336/addnumbers HTTP/1.1
Content-type: text/xml;charset="utf-8"
X-client-version: 1.0-RC
Soapaction: ""
Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
User-Agent: JAX-WS RI 2.1.6 in JDK 6
Host: localhost:8080
Connection: keep-alive
Content-Length: 249

Do you notice the difference: only the first char of the X-Client-Version header is kept upper cased, the rest is lowered!

And indeed, if you check the class c.s.x.w.t.Headers that is used to represent HTTP request (and response) headers, you'll see that it "normalizes" keys when they are added (in normalize(String)):

/* Normalize the key by converting to following form.
 * First char upper case, rest lower case.
 * key is presumed to be ASCII 
 */
 private String normalize (String key) {
     ...
 }

So, while the c.s.x.w.t.h.c.HttpTransportPipe class (my understanding is that this is where the HTTP request is created, this is also where previously added headers will be added to the HTTP request headers) actually adds "Content-Type" as key in a c.s.x.w.t.Headers instance, the key will be modified because of the previously mentioned implementation detail.

I may be wrong but I don't see how this could be changed without patching the code. And the odd part is that I don't think that this "normalizing" stuff is really RFCs compliant (didn't check what RFCs say about headers case though). I'm surprised. Actually, you should raise an issue.

So I see three options here (since waiting for a fix might not be an option):

  • Patch the code yourself and rebuild JAX-WS RI (with all the drawbacks of this approach).
  • Try another JAX-WS implementation like CFX for your client.
  • Let the request go through some kind of custom proxy to modify the header on the fly.


回答2:

You can modify the HTTP headers from the RequestContext. If you have access to the port object you can cast it to a javax.xml.ws.BindingProvider, which will give you access to the RequestContext.

You might also want to remove the unaccepted "Content-type" header.

This page shows how to do it in a bit more detail: https://jax-ws.dev.java.net/guide/HTTP_headers.html

Let me know if you need more code samples, or if you paste some of your code I can show you how to modify it.