Apache Camel HTTP display request and response - https

I am using Apache Camel to load data from CSV file to a webservice. Is there anyway I can display request and response. Below is the route configuration..
I split and aggregate 100 items from array to be sent as POST body.
from(fileLocation)
.unmarshal().csv().bean(new CSVConverter(), "process")
.split(body())
.aggregate(constant(true), new GroupedBodyAggregationStrategy())
.completionSize(100)
.completionTimeout(1000)
.marshal().json(JsonLibrary.Jackson)
.setHeader("Authorization", simple(apiKEY))
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader(Exchange.HTTP_URI, simple(apiURL))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.to("https://serivceurl.com/abc");
Please let me know how can I display request and response with above route?

You can use camel log component to log headers; properties and body
ex:
.to("log:DEBUG?showBody=true&showHeaders=true")
.to("https://serivceurl.com/abc");
.to("log:DEBUG?showBody=true&showHeaders=true")
For more options pl refer: https://camel.apache.org/log.html
If you are planning to use CXF to invoke web service, out of the box logging feature can be used as below,
<cxf:bus>
<cxf:features>
<cxf:logging/>
</cxf:features>
</cxf:bus>

If you take a look into the org.apache.camel.component.http.HttpProducer class you'll see that there is some logging implemented.
try {
if (LOG.isDebugEnabled()) {
LOG.debug("Executing http {} method: {}", method.getName(), method.getURI());
}
int responseCode = executeMethod(method);
LOG.debug("Http responseCode: {}", responseCode);
So if you configure your logging framework (like logback) to the correct LoggingLevel you'll see what the HTTP-Component exactly does.
If you want to log it yourself you can try with the log component or the log dsl like mentioned in the other answer.

Related

request parameters got duplicated when forwarded between two tomcats

We have a Controller running on tomcat 8.5.32 which receives a POST request with query params
/{path_param}/issue?title=4&description=5
request body is empty
Then controller redirects this request to Spring Boot microservice with tomcat 9.0.27.
At line
CloseableHttpResponse result = httpClient.execute(request);
request.getURI().getQuery() equals&title=1&description=2
But when it arrives to microservice parameters are duplicated (title=[4,4]&description=[5,5]).
This is the code which redirects request to microservice
private static <T, U> T executePostRequest(String url, U body, HttpServletRequest httpServletRequest, Function<String, T> readValueFunction) {
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
URIBuilder uriBuilder = new URIBuilder(url);
httpServletRequest.getParameterMap().forEach((k, v) -> Arrays.stream(v).forEach(e -> uriBuilder.addParameter(k, e)));
HttpPost request = new HttpPost(uriBuilder.build());
CloseableHttpResponse result = httpClient.execute(request);
String json = EntityUtils.toString(result.getEntity(), "UTF-8");
handleResultStatus(result, json);
return readValueFunction.apply(json);
} catch (IOException | URISyntaxException e) {
...
}
}
I found that there was similar issue with jetty and it was fixed but did not find anything related to tomcat - and how it can be fixed.
I saw also this topic whith suggestion how to handle duplicated parameters in spring boot but i am wondering if anyone else experienced same issue and how did you resolve it if yes.
It's not a bug, it's a feature present in every servlet container.
The Servlet API does not require for the request parameters to have unique names. If you send a POST request for http://example.com/app/issue?title=1&description=2 with a body of:
title=3&description=4
then each parameter will have multiple values: title will have values 1 and 3, while description will have values 2 and 4 in that order:
Data from the query string and the post body are aggregated into the request
parameter set. Query string data is presented before post body data. For example, if
a request is made with a query string of a=hello and a post body of a=goodbye&a=
world, the resulting parameter set would be ordered a=(hello, goodbye, world).
(Servlet specification, section 3.1)
If you want to copy just the first value of the parameters use:
httpServletRequest.getParameterMap()//
.forEach((k, v) -> uriBuilder.addParameter(k, v[0]));

Adding correlation id using Serilog to Seq in .NET Standard Web Api

This is my first question but please advise if I can make improvements to the question.
I have a .NET Standard Web Api that uses Serilog to log requests to a Seq server. I want to add a correlation id to responses to use on the front end to track requests.
Here is the logging configuration Global.asax:
Log.Logger = new LoggerConfiguration()
.Enrich.With<HttpRequestIdEnricher>()
.Enrich.FromLogContext()
.WriteTo.Seq(ConfigurationManager.AppSettings["Seq:Url"])
.CreateLogger();
I have a message handler which adds a correlation id to all requests:
public class AddCorrelationIdToResponseHandler : DelegatingHandler
{
private const string CorrelationIdHeaderName = "X-Correlation-Id";
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var responseMessage = await base.SendAsync(request, cancellationToken);
var correlationId = request.GetCorrelationId().ToString();
responseMessage
.Headers
.Add(CorrelationIdHeaderName, correlationId);
return responseMessage;
}
}
The problem is the enricher (understandably) doesn't add the HttpRequestId to the response headers so I cannot use it in the front end and I cannot get the correlation id here into the Seq logs.
Here's what I have tried adding to the handler:
LogContext.PushProperty(CorrelationIdHeaderName, correlationId)
Log.ForContext(CorrelationIdHeaderName, correlationId)
both to no avail.
I have also wrapped the entire handler in a using statement with the LogContext statement.
Seq entry example here
I think the issue might be that the log context isn't valid here since the actual logging happens automatically in a different place.
Is there a way I can get access to the HttpRequestId to add it to the header or otherwise how can I can the correlation id to display as a property in the Seq logs? As a last resort, can one switch off the automatic Web Api logging and log the requests manually using Log.Information or Log.Error (in which case the log context should work in theory)?
Any assistance will be greatly appreciated.

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--

Calling Micro-service using WebClient Sprint 5 reactor web

I am calling a micro-service in my rest controller. It works fine when ever there is a successful response from the Micro-service but if there is some error response I fails to pass on the error response back to user. Below is the sample code.
#GetMapping("/{id}/users/all")
public Mono<Employee> findAllProfiles(#PathVariable("id") UUID organisationId,
#RequestHeader(name = "Authorization", required = false) String oauthJwt) {
return webClient.get().uri(prepareUrl("{id}/users/all"), organisationId)
.header("Authorization", oauthJwt).accept(MediaType.APPLICATION_JSON)
.exchange().then(response -> response.bodyToMono(Employee.class));
}
Now if there is any JSON response with error code then web client does not pass on the error response to the controller due to which no information is propagated to the api end user.
You should be able to chain methods from the Mono API. Look for "onError" to see a number of options which allow you to define the behavior when there is an error.
For example, if you wanted to return an "empty" Employee, you could do the following:
.exchange()
.then(response -> response.bodyToMono(Employee.class))
.onErrorReturn(new Employee());

Getting exception while Consuming https Webservice in mule

I'm trying to call a https web service using cxf generated client proxies within Mule. Almost 99% of the time, I get
Caused by: org.apache.commons.httpclient.ProtocolException: Unbuffered entity enclosing request can not be repeated.
at org.apache.commons.httpclient.methods.EntityEnclosingMethod.writeRequestBody(EntityEnclosingMethod.java:487)
at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2114)
at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)*
The app has http inbound end point. The Mule Java transformer tries to call a webservice using https using cxf generated client proxies. I'm running into above said exception.
I've provided screenshot the mule flow [http://i.stack.imgur.com/7X9Wg.jpg]. Much appreciated!!
Mule config xml
<cxf:jaxws-service serviceClass="test.service.https.TestService" doc:name="SOAP" configuration-ref="CXF_Configuration" enableMuleSoapHeaders="false"/>
<custom-transformer class="test.service.https.CallLicenseService" doc:name="Calls HTTPS WS using CXF generated client proxies" encoding="UTF-8" mimeType="text/plain"/>
<logger message="Success" level="INFO" doc:name="Logger"/>
<set-payload value="#['HELLO SUCCESS']" doc:name="Set Payload"/> </flow>
Transformer
URL wsdlURL = null;
String serviceUrl = "TARGET_HTTPS_WSDL"; //This would be the target https URL
try {
wsdlURL = new URL(serviceUrl);
} catch (MalformedURLException e) {
Logger.getLogger(getClass()).info("", e);
}
AuditLogServiceService ss = new AuditLogServiceService(wsdlURL);
AuditLoggingService port = ss.getAuditLoggingServicePort();
((BindingProvider) port).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
serviceUrl.substring(0, serviceUrl.length() - 5));
AuditLogServiceRequest request = new AuditLogServiceRequest();
request.setClientId("4");
request.setUserId("101");
request.setEventSubType("1");
request.setEventType("1");
AuditLogMessage msg = new AuditLogMessage();
msg.setMessage("Hello Test");
request.getLogMessages().add(msg);
AuditLogServiceResponse response = port.logEvent(request);
System.out.println(response.getMessage());
return response.getMessage();
First of all if you need to consume a webservice You need to put <cxf:jaxws-client serviceClass instead of cxf:jaxws-client ...next step is you need to use an http outbound endpoint to post to the external webservice ... pls refer the following link :- http://www.mulesoft.org/documentation/display/current/Consuming+Web+Services+with+CXF
One more thing .. you need to use java component instead of <custom-transformer class ..you need to set the payload just before the component ... I mean you need to set the payload before posting it to external webservice

Resources