I'm using Apache HttpComponents (core - 4.1.3, httpclient 4.1.1) to make http requests in a REST client I've written. The web service requires OAuth, which I've implemented using signpost. Recently, the webservice has introduced 301 redirects to endpoints that also require oauth. So, I've implemented a custom RedirectStrategy which builds a new request and signs it with signpost, just like I would do normally. However, the first two lines (in DefaultRequestDirector) immediately after I return my new request are setting all the headers to those that were sent in the initial request, effectively wiping my new Authorization header and causing all redirect requests to fail.
Does anyone know a way around this? I've considered always returning false in my custom RedirectStrategy and handing this in the ResponseHandler that I've attached to my request, but there's no trivial way of reconstructing the request and submitting it back through the proper client.
These are lines 1021-1023 in the DefaultRequestDirector where it calls to my custom RedirectStrategy and then wipes my headers (I'll try to link to source, shortly):
HttpUriRequest redirect = redirectStrategy.getRedirect(request, response, context);
HttpRequest orig = request.getOriginal();
redirect.setHeaders(orig.getAllHeaders());
I'm guessing a bit here, but if the code that you are referring to is line 349 of DefaultRequestDirector, then a bit further down on line 452 there is a call to requestExec.preProcess(wrapper, httpProcessor, context).
Would it be possible to register a processor here that signs the request?
If not, the BasicHttpProcessor (which is probably the processor used here) allows you to register interceptors that allow you to participate in the pre-processing of the request.
This might be another option to sign the request.
Alternatively, there is a HttpContext object. The Javadoc for this object suggests that it can be used to hold contextual data for the request, and your data would fit this description. So park it there and regather when you need it later.
Related
I know sending a body with a GET request isn't the best idea but I'm trying to consume an existing API which requires it.
Sending a body with POST is straight-forward:
webClient.post()
.uri("/employees")
.body(Mono.just(empl), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
It won't work with webClient.get() though, because while the post() method returns a WebClient.RequestBodyUriSpec, the get() method returns WebClient.RequestHeadersUriSpec<?>, which doesn't seem to allow any body definitions.
I've found a workaround for Spring RestTemplate here: RestTemplate get with body,
but had no luck finding any for the new WebClient.
While the other responses are correct that you shouldn't use a body with a GET request, that is not helpful when you do not own, or cannot change the already existing method you are calling.
The problems is WebClient#get returns a WebClient.RequestHeadersUriSpec which does not provide a way for us to set the body.
WebClient#post returns a WebClient.RequestBodyUriSpec which does provide us a way to set the body but will cause us to use the wrong HTTP method, POST instead of GET.
Thankfully for us stuck in this situation there is WebClient#method which returns a WebClient.RequestBodyUriSpec and allows us to set the HTTP method.
webClient.method(HttpMethod.GET)
.uri("/employees")
.body(Mono.just(empl), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
You may still run into issues in your testing libraries though...
A GET reques has no body. It is forbidden (well, not forbidden, but not used at all) by the HTTP specification. You have two approaches here:
Do a POST. It is there just for that.
Use a query string and pass the data in that part of the URL.
Of course, you can attach the needed fields and pass a payload to the GET request, but it will probably be ignored, or worse, identified as an error and rejected by the server, before your served code has access to it. But if you are passing data to the server to do some processing with it, then POST is what you need to use.
Extracted from RFC-7231. HTTP 1.1. Semantics and code:
A payload within a GET request message has no defined semantics;
sending a payload body on a GET request might cause some existing
implementations to reject the request.
(markup is mine)
Reasons for this are, mainly, that a GET method must be idempotent, producing the same output for the same URL, if repeated. POST doesn't have these requirements, so POST is your friend.
I am trying to intercept all incoming HTTP requests and process the body attached to these requests in my Spring MVC (not Spring Boot) app. To implement this "inbound-interceptor", I am using Spring's HandlerInterceptor interface. Once the request is intercepted, I am trying to retrieve the body as follows:
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
Map<String, String[]> params = requestWrapper.getParameterMap();
byte[] body = requestWrapper.getContentAsByteArray();
Referring to this article, the limitations of trying to extract the body this way are:
Content-type of the request must be x-www-form-urlencoded
Method-type must be POST
For the application I am building, I cannot enforce either of these constraints as the calls come from heterogeneous sources beyond my control. Is there some way to override this behavior to allow extraction of the body for requests not supported by default? Or, alternatively, is there another approach to performing this task?
P.S. I am performing logging + some custom processing on the body. So solutions such as the ones mentioned in this answer are not too helpful
Have you tried Logbook? https://github.com/zalando/logbook Works with pure Spring.
Their Default Log Writer looks promising: https://github.com/zalando/logbook/blob/main/logbook-core/src/main/java/org/zalando/logbook/DefaultHttpLogWriter.java
And you may just want to extend this class to log to all Loggers you want.
You can even do something completely different with the request besides logging.
I want to set a dynamically generated HTTP header on each SOAP JAX-WS request.
If I wanted to set the same HTTP header on each JAX-WS request I could use the technique here, i.e.
public class MyApplicationClass {
// Inject an instance of the service's port-type.
#WebServiceRef(EchoService.class)
private EchoPortType port;
// This method will invoke the web service operation and send transport headers on the request.
public void invokeService() {
// Set up the Map that will contain the request headers.
Map<String, Object> requestHeaders = new HashMap<String, Object>();
requestHeaders.put(“MyHeader1”, “This is a string value”);
requestHeaders.put(“MyHeader2”, new Integer(33));
requestHeaders.put(“MyHeader3”, new Boolean(true));
// Set the Map as a property on the RequestContext.
BindingProvider bp = (BindingProvider) port;
bp.getRequestContext().put(com.ibm.websphere.webservices.Constants.REQUEST_TRANSPORT_PROPERTIES, requestHeaders);
// Invoke the web services operation.
String result = port.echoString(“Hello, world!”);
}
}
However, here I want to use a different HTTP header for each request. Essentially I want to include a X-RequestId header or similar with a random value, so the receiving server can distinguish between requests duplicated on a timeout either by the Java client or (worse) an inline HTTP proxy.
Moreover, it JAX-WS retries the same call, I don't want it to regenerate the header (obviously).
Note that my application is already covered in the equivalent of port.echoString (lots of calls to the web service). I can't manually change the header in front of each such call because:
they share the same binding provider, and this would not be thread-safe (i.e. user A could change the header, user B could change the header, then user A could call, then user B could call, and the same header be passed)
this would require modification all over the code.
What I want to do is add something to the class that serialises each request, to add the header at serialisation time.
Questions that are related but are not duplicates:
java web service client, adding http headers - does not use JAX-WS binding, i.e. each call has to be made manually
How to add header to SOAP request? - SOAP headers not HTTP headers
As for the unique value aspect, you can use the JDK's UUID class to create a GUID:
requestHeaders.put("X-RequestId", java.util.UUID.randomUUID().toString());
As for the clarified thread safety concern, based on the JAX-WS specification (JSR-224) section 9.3 I'd suggest using a JAX-WS client handler to do this as the handler spec identifies a thread safe mechanism: MessageContext:
9.3.3 Handler Implementation Considerations
Handler instances may be pooled by a JAX-WS runtime system. All
instances of a specific handler are considered equivalent by a JAX-WS
runtime system and any instance may be chosen to handle a particular
message. Different handler instances may be used to handle each
message of an MEP. Different threads may be used for each handler in a
handler chain, for each message in an MEP or any combination of the
two. Handlers should not rely on thread local state to share
information. Handlers should instead use the message context, see section 9.4.
You can write one central handler class and attach it to the BindingProvider's handler chain to avoid changing all the places you invoke the service operation across the application. You can add a handler to the handler chain programmatically or via the #HandlerChain annotation companion to #WebServiceRef
This post describes using the handler framework's MessageContext to set outbound http headers like you want. However in your case you want to set the X-RequestId with the UUID value discussed above.
I want to understand how a RESTful web service identifies if a correct request method is called.
For example,
I have a REST service it exposes one operation which is of type GET.
Assume a REST client has invoked the operation using a wrong request method(PUT).
In this scenario, how the service/framework identifies a correct request method is invoked?
I have gone through various posts to understand the scenario but I don't find any information.
Please let me know your comments.
The first line sent in an HTTP request looks like this:
GET /index.html HTTP/1.1
The HTTP request thus contains the HTTP method (POST, PUT, GET, etc.). The framework reads this method, and invokes the Java method that is mapped (thanks to annotations, or XML configuration, or whatever) to the URL (also contained in the HTTP request, as shown above) and the HTTP method. If none is found, then an error response is sent back (405 Method Not Allowed, if the resource is found, but with another method, or 404 if the resource is not found).
It's the http protocol not REST that checks headers, and reports back with an error code.
REST is sort of a strategy, not an implementation.
Hope this helps.
I have a JAX-RS web service for which I would like to disable the same-origin policy via the new CORS HTTP headers. (I am fully aware of the security implications.)
I'd like to have a custom annotation that lets me set HTTP response headers. For example,
#ResponseHeaders({"Access-Control-Allow-Origin: *",
"Access-Control-Allow-Methods: GET"})
// Or, alternatively:
#AllowOrigins({"*"})
public String resourceMethod() { ... }
This approach minimizes boilerplate code, but I'm not sure if there's a subtle technical limitation; JAX-RS provides many annotations to handle the HTTP request but not the response, with #Produces seeming to be the sole exception.
I also prefer to stay away from too much web.xml configuration, if possible. Without explicitly needing to use a ResponseBuilder (it's OK if an annotation uses one), is there a clean way to set custom HTTP response headers?
To clarify, I'm looking for annotations that integrate with the various ways of setting HTTP response headers in order to minimize boilerplate code.
Perhaps the only spec driven approach is to use a custom MessageBodyWriter. In the writeTo() method, you are passed in a MultivaluedMap which you can set response headers on. You are also passed the annotations on the resource method invoked (so you can get whatever custom annotation you want). So read the annotations, set the headers via MultivaluedMap, and then use the OutputStream passed in to write the message body.
In Apache Wink and possibly other JAX-RS frameworks, you can create custom server side handlers that can also read the annotations on the resource method and do whatever you want (like setting response headers by default).