I'm currently writing a REST API that uses a CXF interceptor to add certain headers to each request.
The code of this interceptor is:
public class TestHeaderInterceptor extends AbstractOutDatabindingInterceptor {
public TestHeaderInterceptor() {
super(Phase.SEND);
}
#SuppressWarnings("unchecked")
#Override
public void handleMessage(Message message) {
MultivaluedMap<String, Object> headers = (MetadataMap<String, Object>) message.get(Message.PROTOCOL_HEADERS);
if (headers == null) {
headers = new MetadataMap<String, Object>();
}
headers.add("X-Test", "test");
message.put(Message.PROTOCOL_HEADERS, headers);
}
}
So, as you can see I'm adding a header called X-Test with value test. When I use a CXF REST client (proxy based), I'm using the following code to add the interceptor to the client:
Client client = WebClient.client(clientObj);
ClientConfiguration config = WebClient.getConfig(client);
List<Interceptor<? extends Message>> interceptors = new ArrayList<Interceptor<? extends Message>>();
interceptors.add(new TestHeaderInterceptor());
config.setOutInterceptors(interceptors);
My REST API only has 2 actions:
#GET
#Produces(JSON_UTF8)
#Path("test/{id}")
Test test(#PathParam("id") String id);
#POST
#Produces(JSON_UTF8)
#Path("test2/{type}")
#Consumes(JSON_UTF8)
Test test2(Test obj, #PathParam("type") Type type);
The test/{id} method works successfully and it adds the header (checked with Wireshark). However, the test2/{type} call does not add the header to the request.
The weirdest thing is that, while using debug, the interceptor code is clearly invoked, which leaves me to think that somehow Apache CXF is ommitting the headers I add.
That's also the reason why I'm using the Phase.SEND phase in stead of Phase.MARSHALL, just because I thought my headers are ommitted somewhere in the process of running through all these phases. But even now the headers are still missing.
After some debugging I found out that the CXF interceptor chain is different when I'm using the test2 call, for example:
Interceptor chain with test:
[2014-06-13 10:49:48,535] - [DEBUG] - [Default Executor-thread-1] - [PhaseInterceptorChain.java:682] - Chain org.apache.cxf.phase.PhaseInterceptorChain#1384d8d1 was modified. Current flow:
pre-logical [ClientRequestFilterInterceptor]
prepare-send [MessageSenderInterceptor]
marshal [TestHeaderInterceptor]
prepare-send-ending [MessageSenderEndingInterceptor]
Interceptor chain with test2:
[2014-06-13 10:50:02,205] - [DEBUG] - [Default Executor-thread-1] - [PhaseInterceptorChain.java:682] - Chain org.apache.cxf.phase.PhaseInterceptorChain#69a33de5 was modified. Current flow:
pre-logical [ClientRequestFilterInterceptor]
prepare-send [MessageSenderInterceptor]
write [BodyWriter]
marshal [TestHeaderInterceptor]
prepare-send-ending [MessageSenderEndingInterceptor]
As you can see there is an additional BodyWriter interceptor at the WRITE phase. I suppose that when writing the request body, you can no longer access the headers (because the body comes after the headers).
So, the fix was to actually move the TestHeaderInterceptor to a phase before the BodyWriter, so in my code I'm now using the following code in my constructor:
public TestHeaderInterceptor() {
super(Phase.SETUP);
}
Related
I'm having multiple microservices
1. MangerApp
2. ProcessApp
3. DoingStuffApp
4. .....
the "MangerApp Microservices" get an Http-Request
I'm looking for a way to transfer automatically some of the HTTP headers
in the call, while I don't want to go over each place and do - add Headers, my HTTP headers are stored as a thread-local Map.
since I call to other microservices, with RestTemplate I have many different calls some get/post/put/etc...
changing each one them and passing the header manually is not that efficient.
I'm looking for a way to manage it, other than extending the RestTemplate Class now.
You can use a ClientHttpRequestInterceptor to achieve what you need.
1) Create a HeaderInterceptor implementing ClientHttpRequestInterceptor. In this example it gets the Authorization and Accept headers from a ThreadLocal and propagates them:
public class HeaderInterceptor implements ClientHttpRequestInterceptor{
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
List<String> authorization = HeaderThreadLocal.getAuthorization()
List<String> accept = HeaderThreadLocal.getAuthorization();
headers.addAll("Authorization", authorization);
headers.addAll("Accept", accept);
return execution.execute(request, body);
}
}
2) Configure your RestTemplate bean adding the header interceptor:
restTemplate.getInterceptors().add(new HeaderInterceptor());
In my organisation, when I want to expose an API, I have to declare it with a swagger contract, same for any update, and it can take multiple weeks before the creation or change is taken into account.
That's why we've come with the idea to declare only one contract for all the APIs we need to expose, and manage the routing in an applicative reverse proxy (the request would include the necessary metadata to allow to route to the appropriate endpoint) :
{
"genericHttpRequest" : base64encodedByteArrayOfAnyHttpRequest
}
Now the question is :
how to manage this request without reimplementing HTTP ? Is it possible to put back the array of byte into a structured HttpServletRequest ?
/**
* Manage a generic request
*/
#RequestMapping(value = "/genericRequest", method = RequestMethod.POST)
public #ResponseBody void manageGenericRequest(#RequestBody GenericHttpRequestDto body) {
byte[] genericHttpRequest = body.getGenericHttpRequest();
//(...)
}
Spring will inject a HttpServletRequest if it is set as a method parameter. Furthermore, wildcard path mappings will enable the methods to be matched to every request:
#RestController
#RequestMapping("/generic-endpoint/**")
public class DemoController {
#RequestMapping
public ResponseEntity<Object> genericGetRequest(HttpServletRequest httpServletRequest) {
return ResponseEntity.ok().body(httpServletRequest.getMethod());
}
}
Optionally, you could return a ResponseEntity to gain more control over your HTTP response.
I have encountered a puzzling behavior between production and development environments.
My Spring controller is as follows (the same between the two environments)
#RequestMapping(value = "/find")
#ResponseBody
public List<D> find(#RequestBody P params) throws Exception {
// omitted
}
When a request is done by Chrome on production environment, the browser receives correct response:
However, when the request is done by Chrome on development environment, Spring prints a warning (warning points to missing request body)
WARN [qtp543254421-52] AbstractHandlerExceptionResolver.java:197 -
Resolved
[org.springframework.http.converter.HttpMessageNotReadableException:
Required request body is missing: public java.util.List<D>
pl.kg.ut.postgres.controller.ReadPostgresController.find(P) throws java.lang.Exception]
Here's a screenshot of that request from Chrome.
When we compare the two request (from dev and prod) there are some similarities and differences. Request URL, Request Method, and Request Payload are the same. (What is striking is that Request Payload is null but the controller needs a payload.) What is different is request headers: there are more of them when request is made from production.
You definitely shouldn't GET HTTP method request with body for parsing.
Data enclosure is done with HTTP POST for stored request' body.
Please, try to defined code below for posting valid payload.
#RequestMapping(value = "/find", method=RequestMethod.POST)
#ResponseBody
public List<D> find(#RequestBody P params) throws Exception {
// omitted
}
I have an API created with Jersey
There's currently an endpoint to which users can make POST requests. There is no body required, as all the information is in the url.
#POST
#Path("entries")
#Produces(MEDIATYPE_JSON_AND_XML)
public Response createEntry(){
...
}
A new, empty, entry is created and the id is returned.
Content-Type of the request doesn't matter, as there is no request body data.
Now it should also be possible to set specific fields of the new entry during the request, using FormData. For this request a body is necessary, and the Content-Type must be multipart/form-data.
So I've created a second function:
#POST
#Path("entries")
#Consumes("multipart/form-data");
#Produces(MEDIATYPE_JSON_AND_XML)
public Response createEntryWithParam(#FormDataParam('param') String param){
...
}
This second function works te send the parameter in the request. But by adding it, the first stops working.
Sending a request without Content-Type will throw a NullPointerException. Probably because the #Consumes triggers some kind of Content-Type-check, which fails.
Is there a way to have one endpoint accepting POST requests with or without request-body?
edit So, I would like to receive all multipart/form-data requests in the seconds function, and use the first as a kind of catch-all for other POST requests to that endpoint
Currently I have a work-around in place.
If a POST request comes in without MediaType (Content-Type) or request-body, I automatically add an empty JSON object and set the Content-Type accordingly.
#Provider
public class ContentTypeRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext crc) throws IOException {
if (crc.getMethod().equals("POST") && crc.getMediaType() == null && crc.getLength() == -1){
crc.getHeaders().add("content-type", MediaType.APPLICATION_JSON);
InputStream in = IOUtils.toInputStream("{}");
crc.setEntityStream(in);
}
}
}
This works, but is kinda hacky in my opinion. I'm interested to know if there are better ways to achieve my desired result.
I have a basic spring websocket application which currently sends basic data to subscribers.
Currently the system uses the SimpMessageSendingOperations class as the message handler.
If I call SimpMessageSendingOperations.convertAndSend(destination, object) then the object is converted and received by the subscribed clients.
I would like to be able to send a custom header to the clients.
I have tried using the SimpMessageSendingOperations.convertAndSend(destination, object, headers) method to do this. However the custom header is not included in the stomp message.
Debugging through the code it looks like StompHeaderAccessor.toStompHeaderMap() method calls
toNativeHeaderMap() which uses the native header and the original native header maps to build up the stomp headers.
Is there a way to get a custom header added to stomp message?
StompHeaderAccessor extends NativeMessageHeaderAccessor which seems to be where the non-stomp headers live, except they are all stored in a single header called nativeHeaders - which itself is a map.
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public GenericMessage<Greeting> greeting(HelloMessage message) throws Exception {
Map<String, List<String>> nativeHeaders = new HashMap<>();
nativeHeaders.put("hello", Collections.singletonList("world"));
Map<String,Object> headers = new HashMap<>();
headers.put(NativeMessageHeaderAccessor.NATIVE_HEADERS, nativeHeaders);
return new GenericMessage<Greeting>(new Greeting("Hello, " + message.getName() + "!"), headers);
}
A simple interceptor server-side to wrap your custom headers to the nativeHeaders header should be enough to expose them client-side where they would be available as a map message.headers.nativeHeaders. Simmilarly, you could write a client-side interceptor to move the nativeHeaders into the regular headers - so before your client is aware of the message, all the expected headers are simply in the message.headers.