Multiple headers message using Spring Integration - spring

How to create a instance of Message with multiple headers using Spring Integration MessageBuilder class
Found the below information but it is only for single header
http://docs.spring.io/autorepo/docs/spring-integration/3.0.0.M3/reference/html/messaging-construction-chapter.html

Please use the current documentation - your link is not even to a released version, but a milestone of the 3.0.0 release. The current version is 4.2.0.RELEASE.
You can add as many headers as you want...
Message<String> message4 = MessageBuilder.withPayload("test4")
.setHeader("foo", 123)
.setHeader("bar", 456)
.build();

You can also use createMessage method. Pass MessageHeaders as second argument.
Map<String,Object> headers = new HashMap<>();
headers.put("foo", 123);
headers.put("bar", 456);
MessageBuilder.createMessage(payload, new MessageHeaders(headers));

Related

How to set multiple headers at once in Spring WebClient?

I was trying to set headers to my rest client but every time I have to write
webclient.get().uri("blah-blah")
.header("key1", "value1")
.header("key2", "value2")...
How can I set all headers at the same time using headers() method?
If those headers change on a per request basis, you can use:
webClient.get().uri("/resource").headers(httpHeaders -> {
httpHeaders.setX("");
httpHeaders.setY("");
});
This doesn't save much typing; so for the headers that don't change from one request to another, you can set those as default headers while building the client:
WebClient webClient = WebClient.builder().defaultHeader("...", "...").build();
WebClient webClient = WebClient.builder().defaultHeaders(httpHeaders -> {
httpHeaders.setX("");
httpHeaders.setY("");
}).build();
The consumer is correct, though it's hard to visualize, esp. in that you can continue with additional fluent-composition method calls in the webclient construction, after you've done your work with the headers.
....suppose you have a HttpHeaders (or MutliValue map) holding your headers in scope. here's an example, using an exchange object from spring cloud gateway:
final HttpHeaders headersFromExchangeRequest = exchange.getRequest().headers();
webclient.get().uri("blah-blah")
.headers( httpHeadersOnWebClientBeingBuilt -> {
httpHeadersOnWebClientBeingBuilt.addAll( headersFromExchangeRequest );
}
)...
the addAll can take a multivalued map. if that makes sense. if not, let your IDE be your guide.
to make the consumer clearer, let's rewrite the above as follows:
private Consumer<HttpHeaders> getHttpHeadersFromExchange(ServerWebExchange exchange) {
return httpHeaders -> {
httpHeaders.addAll(exchange.getRequest().getHeaders());
};
}
.
.
.
webclient.get().uri("blah-blah")
.headers(getHttpHeadersFromExchange(exchange))
...
I found this problem came up again for me and this time I was writing groovy directly using WebClient. Again, the example I'm trying to drive is using the Consumer as the argument to the headers method call.
In groovy, the additional problem is that groovy closure syntax and java lambda syntax both use ->
The groovy version is here:
def mvmap = new LinkedMultiValueMap<>(headersAsMap)
def consumer = { it -> it.addAll(mvmap) } as Consumer<HttpHeaders>
WebClient client = WebClient.create(baseUrlAsString)
def resultAsMono = client.post()
.uri(uriAsString).accept(MediaType.APPLICATION_JSON)
.headers(consumer)
.body(Mono.just(payload), HashMap.class)
.retrieve()
.toEntity(HashMap.class)
The java version is here:
LinkedMultiValueMap mvmap = new LinkedMultiValueMap<>(headersAsMap);
Consumer<HttpHeaders> consumer = it -> it.addAll(mvmap);
WebClient client = WebClient.create(baseUrlAsString);
Mono<ResponseEntity<HashMap>> resultAsMono = client.post()
.uri(uriAsString).accept(MediaType.APPLICATION_JSON)
.headers(consumer)
.body(Mono.just(payload), HashMap.class)
.retrieve()
.toEntity(HashMap.class);
In Spring Boot 2.7.5:
webClient
.get()
.uri("blah-blah")
.headers(
httpHeaders -> {
httpHeaders.set("key1", "value1");
httpHeaders.set("key2", "value2");
})

How to set content-type for a spring boot test case which returns PDF file

I am currently testing one of my services with Spring boot test.The service exports all user data and produces a CSV or PDF after successful completion. A file is downloade in browser.
Below is the code i have wrote in my test class
MvcResult result = MockMvc.perform(post("/api/user-accounts/export").param("query","id=='123'")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_PDF_VALUE)
.content(TestUtil.convertObjectToJsonBytes(userObjectDTO)))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_PDF_VALUE))
.andReturn();
String content = result.getResponse().getContentAsString(); // verify the response string.
Below is my resource class code (call comes to this place)-
#PostMapping("/user-accounts/export")
#Timed
public ResponseEntity<byte[]> exportAllUsers(#RequestParam Optional<String> query, #ApiParam Pageable pageable,
#RequestBody UserObjectDTO userObjectDTO) {
HttpHeaders headers = new HttpHeaders();
.
.
.
return new ResponseEntity<>(outputContents, headers, HttpStatus.OK);
}
While I debug my service, and place debug just before the exit, I get content Type as 'application/pdf' and status as 200.I have tried to replicate the same content type in my test case. Somehow it always throws below error during execution -
java.lang.AssertionError: Status
Expected :200
Actual :406
I would like to know, how should i inspect my response (ResponseEntity). Also what should be the desired content-type for response.
You have problem some where else. It appears that an exception/error occurred as noted by application/problem+json content type. This is probably set in the exception handler. As your client is only expecting application/pdf 406 is returned.
You can add a test case to read the error details to know what exactly the error is.
Something like
MvcResult result = MockMvc.perform(post("/api/user-accounts/export").param("query","id=='123'")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_PROBLEM_JSON_VALUE)
.content(TestUtil.convertObjectToJsonBytes(userObjectDTO)))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON_VALUE))
.andReturn();
String content = result.getResponse().getContentAsString(); // This should show you what the error is and you can adjust your code accordingly.
Going forward if you are expecting the error you can change the accept type to include both pdf and problem json type.
Note - This behaviors is dependent on the spring web mvc version you have.
The latest spring mvc version takes into account the content type header set in the response entity and ignores what is provided in the accept header and parses the response to format possible. So the same test you have will not return 406 code instead would return the content with application json problem content type.
I found the answer with help of #veeram and came to understand that my configuration for MappingJackson2HttpMessageConverter were lacking as per my requirement. I override its default supported Mediatype and it resolved the issue.
Default Supported -
implication/json
application*/json
Code change done to fix this case -
#Autowired
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.ALL);
jacksonMessageConverter.setSupportedMediaTypes(mediaTypes);
406 means your client is asking for a contentType (probably pdf) that the server doesn't think it can provide.
I'm guessing the reason your code is working when you debug is that your rest client is not adding the ACCEPT header that asks for a pdf like the test code is.
To fix the issue, add to your #PostMapping annotation produces = MediaType.APPLICATION_PDF_VALUE see https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/PostMapping.html#produces--

Spring4 #RequestBody required doesn't work

I've upgrade Spring version from 4.0.x to 4.2.3 (4.2.4 just now), suddenly, "required = false" property from #RequestBody annotation does not work as expected (as it did before the version change).
Server response with HttpStatus 415 - Unsupported Media Type.
The controller method (same for every Spring versions).
#RequestMapping(value = "/entity/{entityId}/{from}/{size}", method = RequestMethod.POST)
#ResponseBody
#JsonView(ToShowIn.App.class)
public ResponseEntity<?> getActiveEntityPaged(
#PathVariable int entityId, #PathVariable int from, #PathVariable int size,
#RequestBody(required = false) EntityFilterParam entityFilterParam) {
...
}
Really, it's not problem because from the client, avoiding send null, for example, setting empty EntityFilterParam instance is enough to fix it. But I would like to understand if this bothering issue is because a wrong concept from me or rather is due for a bug from the new Spring versions.
UPDATE#1
Versions
<spring-version>4.2.4.RELEASE</spring-version>
<jackson-2-version>2.3.2</jackson-2-version>
Request is from Android device, working with spring-android library (AKA RestTemplate).
Generic method, works on 4.0.x version, where EntityFilterParam can be null.
super.doRestList(
new HttpEntity<EntityFilterParam>(EntityFilterParam, this.getHttpHeaders()),
HttpMethod.POST,
urlBuilder);
On 4.2.3 and 4.2.4, to fix the issue, EntityFilterParam can't be null. -> HttpStatus 415 - Unsupported Media Type. To fix it.
super.doRestList(
new HttpEntity<EntityFilterParam>((EntityFilterParam != null) ? EntityFilterParam : new EntityFilterParam(), this.getHttpHeaders()),
HttpMethod.POST,
urlBuilder);
Inside doRestList, RestTemplate request is performed.
final ResponseEntity<E[]> responseEntity =
(ResponseEntity) super.getRestTemplate()
.exchange(urlBuilder.getURL(), httpMhetod,
requestEntity, this.entityArray.getClass());
Where, urlBuilder.getURL() (URL string, here #PathVariables are working fine) and this.getHttpHeaders() return the next HttpHeaders configuration.
RestFactory.httpHeaders = new HttpHeaders();
RestFactory.httpHeaders.setAuthorization(this.getAuthorization(user));
RestFactory.httpHeaders.set(HttpHeaders.CONNECTION, "Close"); // Switch off keep-alive.
RestFactory.httpHeaders.set(HttpHeaders.ACCEPT_LANGUAGE, "en-US,en;q=0.8");
RestFactory.httpHeaders.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
super.getRestTemplate() is the singleton method which returns the RestTemplate instance set with my own CloseableHttpClient configuration.
I know HttpHeaders.CONTENT_TYPE for application/json is missing, but is consciously, in fact, it works when the http body payload is not empty, and fails when it is.
When the error occurs, 415 is performed before the flow of the request goes inside the controller method, therefore, any operation is doesn't performed.
UPDATE#2
Another interesting point, Jackson2 serializes the null value as "null" string, then the body payload is not being null, is filled with "null".
I've tried reproducing this exact issue, without much success.
Do you confirm that with a curl command the problem still exists?
Something like curl -vvv -X POST https://example.com/entity/12/50/10 (if so, paste here the output)
I'm not 100% about this for RestTemplate in spring-android, but if no content type is provided, the template adds by default a "Content-Type: application/x-www-form-urlencoded". If this is the case, configuring a "DEBUG" log level for "org.springframework.web" should show something like
[INFO] [talledLocalContainer] org.springframework.web.HttpMediaTypeNotSupportedException:
Content type 'application/x-www-form-urlencoded' not supported
I think you should set the Content-Type anyway. You mentioned this fails when you do - could you elaborate a bit on that?

How to query SOAP based endpoint using Spring WS by passing pre generated SOAP Request instead of XML payload?

Need to query SOAP based endpoint using Sprint WS - but here instead of passing XML payload I will have pass a generated SOAP Request itself. I was using org.springframework.ws.client.core.WebServiceTemplate for this purpose right now ?
You can do the following:
ClientMessageCallBack _callBack =
new ClientMessageCallBack("yournamespaceuri/operationx");
final String _message =
"<ns1:operationx xmlns:ns1="yournamespaceuri"></ns1:operationx>";
StreamSource _source = new StreamSource(new StringReader(_message));
// the result will be output in the console
StreamResult _result = new StreamResult(System.out);
// suppose you have a reference to webServiceTemplate
webServiceTemplate.sendSourceAndReceiveToResult(_source,_callBack, _result);

Spring 3 RESTful return on POST (create)

I am new to RESTful services and their implementation on Spring 3. I would like your opinion on the best practices for returning type when a client creates a new resource in my server.
#RequestMapping(method = RequestMethod.POST,
value = "/organisation",
headers = "content-type=application/xml")
#ResponseStatus(HttpStatus.CREATED)
public ??? createOrganisation(#RequestBody String xml)
{
StreamSource source = new StreamSource(new StringReader(xml));
Organisation organisation = (Organisation) castorMarshaller.unmarshal(source);
// save
return ???;
}
A simple choice would be javax.ws.rs.core.Response, found in the Java EE's own restful services package. It - simply - tells what the web server should answer to the HTTP request.
For instance:
if (organisation != null)
return Response.ok().build();
else
return Response.serverError().build();
Custom response headers and other exotic things like that are possible with that return type too, but I don't think that would match with "best practices".
uh, I missed that #ResponseStatus(HttpStatus.CREATED)... I guess my answer was not much of help.
Maybe this will help instead: How to return generated ID in RESTful POST?
I would go for a ResponseEntity<byte[]> and you would have take care of the marshalling of your response on your controller method. Notice that you are basically scrapping the V in MVC, there is a MarshallingView on Spring but from experience I consider the previous solution much more flexible and easier to understand.
It is a good idea to return the newly created entity(with the generated id) wrapped in ResponseEntity. You can also set the HttpStatus in ResponseEntity based on the result of the operation.
#RequestMapping(method = RequestMethod.POST,
value = "/organization",
headers = "content-type=application/xml")
public ResponseEntity<Organization> createOrganisation(#RequestBody String xml) {
StreamSource source = new StreamSource(new StringReader(xml));
Organization organisation = (Organization) castorMarshaller.unmarshal(source);
// save
return new ResponseEntity<Organization>(organization, HttpStatus.OK);
}

Resources