Spring webflux , POST request data is getting truncated intermittently - spring-boot

Request Body sent via Post Method is getting truncated (sometimes) and hence not processed as required.
There is no memory exception thrown or seen in the application logs.
The same request is processed successfully after retrying
Spring boot Code Samples
Controller
#RequestMapping(value = "/updateRequest", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
public Mono<BookRecord> update(#RequestBody Payload e) {
return bookService.sendRequest(e);
}
public class RequestProcessor extends ServerHttpRequestDecorator {
private final ServerHttpRequest requestDecorator;
public RequestProcessor(ServerHttpRequest requestDecorator) {
super(requestDecorator);
this.requestDecorator = requestDecorator;
}
#Override
public Flux<DataBuffer> getBody() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
return super.getBody().doOnNext((dataBuffer) -> {
try {
Channels.newChannel(baos).write(dataBuffer.asByteBuffer());
System.out.println(" Received payload " + IOUtils.toString(baos.toByteArray(), "UTF-8"));
} catch (IOException var11) {
var11.printStackTrace();
} finally {
try {
baos.flush();
baos.close();
} catch (IOException var10) {
var10.printStackTrace();
}
}
});
}
Sample logs...
The method iterates when the payload is truncated. See below
2023-01-20 18:33:53.059 | Severity=DEBUG | [16b38379-1, L:/0:0:0:0:0:0:0:1:8084 - R:/0:0:0:0:0:0:0:1:62902] FluxReceive{pending=0, cancelled=false, inboundDone=false, inboundError=null}: subscribing inbound receiver
Received payload {
"data": "EP2+t7DXkiKcdUeScGwck6kgWC2/Fzf5Q8oU2Jp2mQzAZm6SpB/Bjp0iDyB6HOuAafFi9X1vBkMqry+4TXEfTpjncHEbuGwiTD5IkujMA6/G4aVtbZ915eHMTcCdaCfqlg2v8X4GKZbKrnV7fmMLxEUruFItgfVZ+k6/U6MkqdluK6bXkXKaeopMTHQ2u8SgsDuYnqcx7Kh1313gOeHC/QuYDm4tdkXRnccs3CAcicQRMJJHQ3c2eR7Cbx1sY5KsgLazNz795iXjxStoJKoaY8AzwKwf6EpG2D9ndnCwTEKU18s73QJbgAXlB/XBVvSKJ7VYPx1z2sBPjCIbNByfOW6FoKnMdj4ux/dh/XrfQCuGsBg9yVTyGt2i4x5A5IqzzuD9qON0+5v8FXK78CiEFcIr0zlJqH3vdbRQKWwNuZdTB//9Ya6gZxxpsQ7aq9blx+0sgSPzK3p9tD+HvQ+vSr0v/N+nCXO/wVuaLBXpRkt2dzSrb3i1exRHOEFGL6Oefjl4vGdk0WUaNc6MSqxl1P+q7VBsJRQy7tsHj+SLucrUwmN+IykJlNkbXNn8jPxW7bLV65YFBXsQ5Ye5moYfs+SQqGDUrlofQ2R8yoAFmAZdOzrzjcdh8kdLjXCFtxrQRgqzo42HEm/wXCBIst/C1ThFTZkTaw5H4mqfVVqUQBl6UxqLvEH5YJYVR8mVJQ6QWNHoh2iKzweuV6JbHA4ususgMD03bDP/Qchd/0mz/CgrL1tuvpgX1UjWpwULBWzVFLt+bMWk314RSj9puCAAnTP2mEZdJ2a7fxz28y3Qmus/hPZprcABbJHAyCDQBsqmEfhw0fDdDdO9v0CmXXg2B93EILqIaks+x3HSqzUI1yvBo2RmCvHfX3iMe7wrbdWCXjUn1CRVKJsUjThXotfzqgYYdNQYo2CRApbHu3+VdaUc8KeOhVk86i62gQJXpHEgwSQgDmVE2CwpxGaT8tnbo2jIHxQ9W1iwCwwR8wuyyUb1dhZUYPC2/BzdloJaiYlkcMPktGO04j9VcfUIDXg90DKJ3wJpOY7heHzGM9R8Or7VMg9q3in1CKVlXplPAblpEHsMbjjh8CYx98fPuStsvFU0KBmVetOFcw45a9zEugEssi+8xuQKFIuzaJ1nQsAcScbNwSJ7GQM/1LgBLoX6gfMrux7cQg8nmc/0
Received payload {
"data": "EP2+t7DXkiKcdUeScGwck6kgWC2/Fzf5Q8oU2Jp2mQzAZm6SpB/Bjp0iDyB6HOuAafFi9X1vBkMqry+4TXEfTpjncHEbuGwiTD5IkujMA6/G4aVtbZ915eHMTcCdaCfqlg2v8X4GKZbKrnV7fmMLxEUruFItgfVZ+k6/U6MkqdluK6bXkXKaeopMTHQ2u8SgsDuYnqcx7Kh1313gOeHC/QuYDm4tdkXRnccs3CAcicQRMJJHQ3c2eR7Cbx1sY5KsgLazNz795iXjxStoJKoaY8AzwKwf6EpG2D9ndnCwTEKU18s73QJbgAXlB/XBVvSKJ7VYPx1z2sBPjCIbNByfOW6FoKnMdj4ux/dh/XrfQCuGsBg9yVTyGt2i4x5A5IqzzuD9qON0+5v8FXK78CiEFcIr0zlJqH3vdbRQKWwNuZdTB//9Ya6gZxxpsQ7aq9blx+0sgSPzK3p9tD+HvQ+vSr0v/N+nCXO/wVuaLBXpRkt2dzSrb3i1exRHOEFGL6Oefjl4vGdk0WUaNc6MSqxl1P+q7VBsJRQy7tsHj+SLucrUwmN+IykJlNkbXNn8jPxW7bLV65YFBXsQ5Ye5moYfs+SQqGDUrlofQ2R8yoAFmAZdOzrzjcdh8kdLjXCFtxrQRgqzo42HEm/wXCBIst/C1ThFTZkTaw5H4mqfVVqUQBl6UxqLvEH5YJYVR8mVJQ6QWNHoh2iKzweuV6JbHA4ususgMD03bDP/Qchd/0mz/CgrL1tuvpgX1UjWpwULBWzVFLt+bMWk314RSj9puCAAnTP2mEZdJ2a7fxz28y3Qmus/hPZprcABbJHAyCDQBsqmEfhw0fDdDdO9v0CmXXg2B93EILqIaks+x3HSqzUI1yvBo2RmCvHfX3iMe7wrbdWCXjUn1CRVKJsUjThXotfzqgYYdNQYo2CRApbHu3+VdaUc8KeOhVk86i62gQJXpHEgwSQgDmVE2CwpxGaT8tnbo2jIHxQ9W1iwCwwR8wuyyUb1dhZUYPC2/BzdloJaiYlkcMPktGO04j9VcfUIDXg90DKJ3wJpOY7heHzGM9R8Or7VMg9q3in1CKVlXplPAblpEHsMbjjh8CYx98fPuStsvFU0KBmVetOFcw45a9zEugEssi+8xuQKFIuzaJ1nQsAcScbNwSJ7GQM/1LgBLoX6gfMrux7cQg8nmc/0rLgZ4sKWT2d8LU9dHYp0+a2cEPaPsd0bv6swxBnh2mdoj89ZpAfE/SoCCV3OUQsqjqMWXTsCbefUiAPi88R5J9T4/FzKvLXz5VyKhA93hx5H+H6m4tGqihUCzl0VDsyqdxJZsrgnysUd0bFphnD5VUIwSwBNNSk5km0oU/1k+YKNcRTO/oZbcgDCqOohvp/4D6ZI9LCluVcKLUVxs1nZcr8m9ztDo4whdADP6uztZO13i4rfq3+N"
}
Successful Request
2023-01-20 18:43:08.415 | Severity=DEBUG | [16b38379-3, L:/0:0:0:0:0:0:0:1:8084 - R:/0:0:0:0:0:0:0:1:62902] FluxReceive{pending=0, cancelled=false, inboundDone=false, inboundError=null}: subscribing inbound receiver
Received payload {
"data": "EP2+t7DXkiKcdUeScGwck6kgWC2/Fzf5Q8oU2Jp2mQzAZm6SpB/Bjp0iDyB6HOuAafFi9X1vBkMqry+4TXEfTpjncHEbuGwiTD5IkujMA6/G4aVtbZ915eHMTcCdaCfqlg2v8X4GKZbKrnV7fmMLxEUruFItgfVZ+k6/U6MkqdluK6bXkXKaeopMTHQ2u8SgsDuYnqcx7Kh1313gOeHC/QuYDm4tdkXRnccs3CAcicQRMJJHQ3c2eR7Cbx1sY5KsgLazNz795iXjxStoJKoaY8AzwKwf6EpG2D9ndnCwTEKU18s73QJbgAXlB/XBVvSKJ7VYPx1z2sBPjCIbNByfOW6FoKnMdj4ux/dh/XrfQCuGsBg9yVTyGt2i4x5A5IqzzuD9qON0+5v8FXK78CiEFcIr0zlJqH3vdbRQKWwNuZdTB//9Ya6gZxxpsQ7aq9blx+0sgSPzK3p9tD+HvQ+vSr0v/N+nCXO/wVuaLBXpRkt2dzSrb3i1exRHOEFGL6Oefjl4vGdk0WUaNc6MSqxl1P+q7VBsJRQy7tsHj+SLucrUwmN+IykJlNkbXNn8jPxW7bLV65YFBXsQ5Ye5moYfs+SQqGDUrlofQ2R8yoAFmAZdOzrzjcdh8kdLjXCFtxrQRgqzo42HEm/wXCBIst/C1ThFTZkTaw5H4mqfVVqUQBl6UxqLvEH5YJYVR8mVJQ6QWNHoh2iKzweuV6JbHA4ususgMD03bDP/Qchd/0mz/CgrL1tuvpgX1UjWpwULBWzVFLt+bMWk314RSj9puCAAnTP2mEZdJ2a7fxz28y3Qmus/hPZprcABbJHAyCDQBsqmEfhw0fDdDdO9v0CmXXg2B93EILqIaks+x3HSqzUI1yvBo2RmCvHfX3iMe7wrbdWCXjUn1CRVKJsUjThXotfzqgYYdNQYo2CRApbHu3+VdaUc8KeOhVk86i62gQJXpHEgwSQgDmVE2CwpxGaT8tnbo2jIHxQ9W1iwCwwR8wuyyUb1dhZUYPC2/BzdloJaiYlkcMPktGO04j9VcfUIDXg90DKJ3wJpOY7heHzGM9R8Or7VMg9q3in1CKVlXplPAblpEHsMbjjh8CYx98fPuStsvFU0KBmVetOFcw45a9zEugEssi+8xuQKFIuzaJ1nQsAcScbNwSJ7GQM/1LgBLoX6gfMrux7cQg8nmc/0rLgZ4sKWT2d8LU9dHYp0+a2cEPaPsd0bv6swxBnh2mdoj89ZpAfE/SoCCV3OUQsqjqMWXTsCbefUiAPi88R5J9T4/FzKvLXz5VyKhA93hx5H+H6m4tGqihUCzl0VDsyqdxJZsrgnysUd0bFphnD5VUIwSwBNNSk5km0oU/1k+YKNcRTO/oZbcgDCqOohvp/4D6ZI9LCluVcKLUVxs1nZcr8m9ztDo4whdADP6uztZO13i4rfq3+N"
}

Related

Okhttp3.14 Stream closed

I have some usage issue about okhttp in 3.14.9 release
if i want add LoggingInterceptor for each request, how can i get response body, which can only consume once?
And follow is my attemp
public class LoggingRequestInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
log.debug(
"{}, {}, {}, {}, {}, {}, {}",
request.url(),
request.method(),
JSONUtil.toJsonStr(request.body()),
request.headers(),
dup.body() == null ? null : dup.body().string());
return response;
}
}
It will throw exception of stream closed, how to fix it?
Use peekBody for this
val client = OkHttpClient.Builder()
.addInterceptor {
val response = it.proceed(it.request())
println(response.peekBody(1000000).string())
response
}
.build()
I do research for this issue. We can use buffer, which is in RequestBody-source-getBuffer.
Working code is below:
public String getResponseBody(Response response) {
try {
ResponseBody responseBody = response.body();
if (!ObjectUtil.isNull(responseBody)) {
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE);
Buffer buffer = source.getBuffer();
return buffer.clone().readString(UTF8);
}
} catch (IOException e) {
log.error("get response body failed: ", e);
}
return null;
}
From ernest-kiwele:
Using a try with a resources block with the response causes this "closed" problem when the response body is read outside of the try block:
try (Response response = client.newCall(request.build()).execute()) {
return response;
} //response.close() called implicitly by the JVM
The fix is to restructure the code to only use the response within the try block.

Extracting response body in aspect for Spring boot application

I would like to log response body with aspect in my Spring Boot application. As of now, I am doing this as follows:
#Pointcut("within(com.web.rest.*)")
public void applicationResourcePointcut() {
}
#AfterReturning(value = ("applicationResourcePointcut()"),
returning = "returnValue")
public void endpointAfterReturning(JoinPoint joinPoint, Object returnValue) throws Throwable {
try {
System.out.println("RESPONSE OBJECT = " + mapper.writeValueAsString(returnValue));
} catch (JsonProcessingException e) {
System.out.println(e.getMessage());
}
}
But here, I am getting full response with Http Status code and other meta data as follows:
RESPONSE OBJECT = {"headers":{"Location":["/api/students/de7cc0b7-dcdf-4f2e-bc26-41525064dd55"],"X-ThreatModelSvc-Alert":["Entit ystudent is created with id:de7cc0b7-dcdf-4f2e-bc26-41525064dd55"]},"body":{"id":"de7cc0b7-dcdf-4f2e-bc26-41525064dd55","name":"Test Name","description":"Test Description","lastModifiedBy":"amallik"},"statusCodeValue":201,"statusCode":"CREATED"}
Here, I just would like to capture the response body. I cannot understand how to extract just the body from the response.

How to send URL encoded data in spring webflux

I am writing a spring 5 web app and my requirement is to get a urlencoded form and in response send url encoded response back
This is Router Function code
#Configuration
public class AppRoute {
#Bean
public RouterFunction<ServerResponse> route(FormHandler formHandler) {
return RouterFunctions.route()
// .GET("/form", formHandler::sampleForm)
// .POST("/form", accept(MediaType.APPLICATION_FORM_URLENCODED), formHandler::displayFormData)
.POST("/formnew", accept(MediaType.APPLICATION_FORM_URLENCODED).and(contentType(MediaType.APPLICATION_FORM_URLENCODED)), formHandler::newForm)
.build();
}
}
and here's my Handler code
public Mono<ServerResponse> newForm(ServerRequest request) {
Mono<MultiValueMap<String, String>> formData = request.formData();
MultiValueMap<String, String> newFormData = new LinkedMultiValueMap<String, String>();
formData.subscribe(p -> newFormData.putAll(p));
newFormData.add("status", "success");
return ServerResponse.ok().contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(fromObject(newFormData));
}
Here's the error I get
2020-04-07 02:37:33.329 DEBUG 38688 --- [ctor-http-nio-3] org.springframework.web.HttpLogging : [07467aa5] Resolved [UnsupportedMediaTypeException: Content type 'application/x-www-form-urlencoded' not supported for bodyType=org.springframework.util.LinkedMultiValueMap] for HTTP POST /formnew
Whats the issue here. I couldn't find any way to write the url encoded response back.
Could anyone point what's the issue.
Try to refactor your code to functional style:
public Mono<ServerResponse> newForm(ServerRequest request) {
Mono<DataBuffer> resultMono = request.formData()
.map(formData -> new LinkedMultiValueMap(formData))
.doOnNext(newFormData -> newFormData.add("status", "success"))
.map(linkedMultiValueMap -> createBody(linkedMultiValueMap));
return ServerResponse.ok().contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromDataBuffers(resultMono));
}
private DataBuffer createBody(MultiValueMap multiValueMap) {
try {
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
return factory.wrap(ByteBuffer.wrap(objectMapper.writeValueAsString(multiValueMap).getBytes(StandardCharsets.UTF_8)));
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("incorrect body");
}
}

Spring Boot Camel Route - get data from rest endpoint

I want to create camel route in Spring Boot (2.1.1) project to get the data from some (rest) endpoint (http://localhost:8080/getAllUsers) and to send that data to activeMq.
I have tried with timer data to send it on activeMq and to consume it and it is working. But I have problem with collecting data from endpoint.
I have tried several things but no success. This is what I have tried.
In this example I am not sending the data to ActiveMq, I just want to see the response...
public void createNewRoute() {
CamelContext context = new DefaultCamelContext();
try {
ProducerTemplate template = context.createProducerTemplate();
context.start();
Exchange exchange = template.request("http://localhost:8080/getAllUsers",
new Processor() {
public void process(Exchange exchange) throws Exception {
}
});
if (null != exchange) {
Message out = exchange.getOut();
int responseCode = out.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
System.out.println("Response: " + String.valueOf(responseCode));
}
Thread.sleep(1000 * 3);
context.stop();
} catch (Exception ex) {
System.out.println("Exception: " + ex);
}
System.out.println("DONE!!");
}
Another route:
from("servlet://localhost:8080/getAllUsers").to("activemq://all-users");
And another:
rest("//localhost:8080/getAllUsers")
.get().consumes("application/json")
.to("activemq://all-users");
I will go with your second example:
from("timer://test?repeatCount=1").routeId("newRoute")
.streamCaching()
.process(exchange -> exchange.getIn()
.setBody(exchange.getIn()
.getBody()))
.marshal()
.json(JsonLibrary.Jackson)
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.to("http://localhost:8080/getAllUsers")
.log(LoggingLevel.INFO, "This is my body: ${body}")
.to("activemq:queue://new-queue");
This will trigger it once.
Try this without context.start() ....
CamelContext camelContext = new DefaultCamelContext();
ProducerTemplate template = camelContext.createProducerTemplate();
Exchange exchange = template.send("http://localhost:8080/getAllUsers", new Processor() {
public void process(Exchange exchange) throws Exception {}
});
Message out = exchange.getOut();
The http components are streaming based, so you can ask Camel to give you the response as string instead.
String s = exchange.getMessage().getBody(String.class);
See more in these links
http://camel.apache.org/stream-caching
http://camel.apache.org/why-is-my-message-body-empty.html

Global Exception handling in Spring reactor and dependent responses

I am trying to create a web application using spring 5 . It's a micro-service which hit few other micro-services. Response from one service is dependent the other.I am using global exception handing in my application.
Here is my code:
#Override
public Mono<Response> checkAvailablity(Request request) {
Mono<Response> authResponse = userService.authenticateToken(request);
return authResponse.doOnSuccess(t -> {
// if success is returned.
// Want to return this innerResponse
Mono<Response> innerResponse =
httpService.sendRequest(Constant.SER_BOOKING_SERVICE_CHECK_AVAILABILTY,
request.toString(), Response.class);
}).doOnError(t -> {
logger.info("Subscribing mono in Booking service - On Error");
Mono.error(new CustomException(Constant.EX_MODULE_CONNECTION_TIMED_OUT));
});
In case of error I want to throw CustomException and catch it in global exception handler:
#ControllerAdvice
public class ExceptionInterceptor {
public static Logger logger = Logger.getLogger(ExceptionInterceptor.class);
#ExceptionHandler(value = CustomException.class)
#ResponseBody
public Response authenticationFailure(ServerHttpRequest httpRequest, ServerHttpResponse response,
CustomException ex) {
logger.info("CustomException Occured with code => " + ex.getMessage());
return buildErrorResponse(ex.getMessage());
}
Based on the above code I have two problems:
The exception which is thrown in Mono.error() is not captured in global exception handler.
In case of success, response from the inner service should be returned.
Used two methods in mono: flatmap() and onErrorMap()
and updated my checkAvailablity() code:
public Mono<Response> checkAvailablity(Request request) {
Mono<Response> authResponse = userService.authenticateToken(request);
return authResponse.flatmap(t -> {
// Added transform() for success case
Mono<Response> response = httpService.sendRequest(Constant.SER_BOOKING_SERVICE_CHECK_AVAILABILTY,
request.toString(), Response.class);
logger.info("Response from SER_BOOKING_SERVICE_CHECK_AVAILABILTY");
return response;
}).onErrorMap(t -> {
// Added onErrorMap() for failure case & now exception is caught in global exception handler.
throw new CustomException(Constant.EX_MODULE_CONNECTION_TIMED_OUT);
});
}

Resources