How to check Webclient reposebody? - webclient

i developed external API by WebClient but i don't know how to check the response body..
public class Call {
public Mono<Object> get() {
Mono<Object> http = webClient.get()
.uri(EXTERNAL_URL)
.retrieve()
.bodyToMono(Object.class);
return http;
}
}
and test code
public class Test {
#Test
void test() {
Call call = new Call();
Mono<Object> mono = call.get();
mono.doOnSuccess(
r -> log.info(">>> r = {}", r) //
).subscribe() }
}
log content
>>> r = MonoMap
it just print "MonoMap".. how can i check response body??

Change your code as follows, it will deserialize the response to a string and return
public Mono<String> get() {
Mono<Object> http = webClient.get()
.uri(EXTERNAL_URL)
.retrieve()
.bodyToMono(String.class);
return String;
}

Related

Can't handle bad request using doOnError WebFlux

I wanna send some DTO object to server. Server have "Valid" annotation, and when server getting not valid DTO, he should send validation errors and something like "HttpStatus.BAD_REQUEST", but when I'm trying to send HttpStatus.BAD_REQUEST doOnError just ignore it.
POST-request from client
BookDTO bookDTO = BookDTO
.builder()
.author(authorTf.getText())
.title(titleTf.getText())
.publishDate(LocalDate.parse(publishDateDp.getValue().toString()))
.owner(userAuthRepository.getUser().getLogin())
.fileData(file.readAllBytes())
.build();
webClient.post()
.uri(bookAdd)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(bookDTO)
.retrieve()
.bodyToMono(Void.class)
.doOnError(exception -> log.error("Error on server - [{}]", exception.getMessage()))
.onErrorResume(WebClientResponseException.class, throwable -> {
if (throwable.getStatusCode() == HttpStatus.BAD_REQUEST) {
log.error("BAD_REQUEST!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); --My log doesn't contain this error, but server still has errors from bindingResult
return Mono.empty();
}
return Mono.error(throwable);
})
.block();
Server-part
#PostMapping(value = "/add", consumes = {MediaType.APPLICATION_JSON_VALUE})
public HttpStatus savingBook(#RequestBody #Valid BookDTO bookDTO, BindingResult bindingResult) {
List<FieldError> errors = bindingResult.getFieldErrors();
if (bindingResult.hasErrors()) {
for (FieldError error : errors ) {
log.info("Client post uncorrected data [{}]", error.getDefaultMessage());
}
return HttpStatus.BAD_REQUEST;
}else{libraryService.addingBookToDB(bookDTO);}
return null;
}
doOnError is a so-called side effect operation that could be used for instrumentation before onError signal is propagated downstream. (e.g. to log error).
To handle errors you could use onErrorResume. The example, the following code handles the WebClientResponseException and returns Mono.empty instead.
...
.retrieve()
.doOnError(ex -> log.error("Error on server: {}", ex.getMessage()))
.onErrorResume(WebClientResponseException.class, ex -> {
if (ex.getStatusCode() == HttpStatus.BAD_REQUEST) {
return Mono.empty();
}
return Mono.error(ex);
})
...
As an alternative as #Toerktumlare mentioned in his comment, in case you want to handle http status, you could use onStatus method of the WebClient
...
.retrieve()
.onStatus(HttpStatus.BAD_REQUEST::equals, res -> Mono.empty())
...
Update
While working with block it's important to understand how reactive signals will be transformed.
onNext(T) -> T in case of Mono and List<T> for Flux
onError -> exception
onComplete -> null, in case flow completes without onNext
Here is a full example using WireMock for tests
class WebClientErrorHandlingTest {
private WireMockServer wireMockServer;
#BeforeEach
void init() {
wireMockServer = new WireMockServer(wireMockConfig().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
}
#Test
void test() {
stubFor(post("/test")
.willReturn(aResponse()
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.withStatus(400)
)
);
WebClient webClient = WebClient.create("http://localhost:" + wireMockServer.port());
Mono<Void> request = webClient.post()
.uri("/test")
.retrieve()
.bodyToMono(Void.class)
.doOnError(e -> log.error("Error on server - [{}]", e.getMessage()))
.onErrorResume(WebClientResponseException.class, e -> {
if (e.getStatusCode() == HttpStatus.BAD_REQUEST) {
log.info("Ignoring error: {}", e.getMessage());
return Mono.empty();
}
return Mono.error(e);
});
Void response = request.block();
assertNull(response);
}
}
The response is null because we had just complete signal Mono.empty() that was transformed to null by applying block

How to send response before actions in quarkus/Vert.X

Following similar question in spring. I want to be able to get in the router the response. and do a response flush, so I could continue work on the server without extending the RTT
meaning, do something like the answer in spring:
public void doSomething(#RequestBody List<Message> messages, HttpServletResponse response) {
int code = (messages!=null && !messages.isEmpty()) ? HttpServletResponse.SC_OK
: HttpServletResponse.SC_NOT_FOUND;
if (code != HttpServletResponse.SC_OK) {
response.sendError(code, res);
return;
}
java.io.PrintWriter wr = response.getWriter();
response.setStatus(code);
wr.print(res);
wr.flush();
wr.close();
// Now it it time to do the long processing
...
}
This is my quarkus code today:
#Path("/events")
class EventsRouter {
val logger: Logger = Logger.getLogger(EventsRouter::class.java)
#POST
#Consumes(MediaType.APPLICATION_JSON)
fun handleEvent(
#HeaderParam("User-Agent") userAgent: String?,
eventPayload: EventPayload,
): Response {
val time = LocalDateTime.now()
...
return Response.ok().build()
}
}
You can use Vert.x executeBlocking to run a blocking code asynchronously.
Here's an example (not tested, so please regard this as Pseudo):
#Path("/events")
class EventsRouter {
val logger: Logger = Logger.getLogger(EventsRouter::class.java);
private final Vertx vertx;
#Inject
public EventsRouter(Vertx vertx) {
this.vertx = vertx;
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
fun handleEvent(
#HeaderParam("User-Agent") userAgent: String?,
eventPayload: EventPayload,
): Response {
val time = LocalDateTime.now()
vertx.executeBlocking(promise -> {
//Do something...
promise.complete();
}, res -> {
System.out.println("Something was done");
});
return Response.ok().build();
}
}

Error handling Web Api .net core and Repository Pattern

I have question about web api and Repository may be its a duplicate question.
but i tried to search on it and i did not get any satisfactory answer.
In my Repository i am getting data with the help of httpclient.
My question is that i can get an error inside my response or i can get required json data which i can map to my product class.I am returning IEnumerable.
1) If i get an error how can i bubble it up to controller and display an error to user.
2) Return the MessageResponse instead of IEnumerable and handle it inside the controller.
What is the best way.
enter code here
public interface IProduct{
Task<IEnumerable<Product>> All();
}
public class Product:IProduct
{
public async Task<IEnumerable<Product>> All(){
var ResponseMessage=//some response.
}
}
You could customize a ApiException which is used to get the error message of the response, and call the UseExceptionHandler in your startup.cs ,refer to the following :
ProductRep
public class ProductRep : IProduct
{
private readonly HttpClient _client;
public ProductRep(HttpClient client)
{
_client = client;
}
public async Task<IEnumerable<Product>> All()
{
List<Product> productlist = new List<Product>();
var response = await _client.GetAsync("https://localhost:44357/api/values/GetProducts");
string apiResponse = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode == false)
{
JObject message = JObject.Parse(apiResponse);
var value = message.GetValue("error").ToString();
throw new ApiException(value);
}
productlist = JsonConvert.DeserializeObject<List<Product>>(apiResponse);
return productlist;
}
public class ApiException : Exception
{
public ApiException(string message): base(message)
{ }
}
}
Startup.cs
app.UseExceptionHandler(a => a.Run(async context =>
{
var feature = context.Features.Get<IExceptionHandlerPathFeature>();
var exception = feature.Error;
var result = JsonConvert.SerializeObject(new { error = exception.Message });
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(result);
}));

Apache HttpClient - REST API: Issue in converting response to customized object which is put as SerializableEntity

I am using Apache HttpClient to put/get customized object using REST APIs. Below is the sample code. My putObject() method works fine and I could serialize Person object and put properly. However, while getting the object, I got below error:
Exception in thread "main" java.lang.ClassCastException: [B cannot be cast to Person at MyTest.demoGetRESTAPI(MyTest.java:88) at MyTest.main(MyTest.java:21)
Seems the code to build Person object out of response entity is not correct
HttpEntity httpEntity = response.getEntity();
byte[] resultByteArray = EntityUtils.toByteArray(httpEntity);
Person person = (Person)SerializationUtils.deserialize(resultByteArray);
Am I doing somthing wrong while getting byte[] array and converting to Person object. Please help me out to solve this issue.
Complete Example Program:
import java.io.Serializable;
import org.apache.commons.lang.SerializationUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.SerializableEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
public class MyTest {
public static void main(String[] args) throws Exception {
putObject();
getObject();
}
public static void putObject() throws Exception
{
HttpClient httpClient = new DefaultHttpClient();
Person person = new Person();
person.setName("Narendra");
person.setId("1");
try
{
//Define a postRequest request
HttpPut putRequest = new HttpPut("http://localhost:9084/ehcache-server/rest/screeningInstance/2221");
//Set the API media type in http content-type header
putRequest.addHeader("content-type", "application/x-java-serialized-object");
//Set the request put body
SerializableEntity personSEntity = new SerializableEntity(SerializationUtils.serialize(person));
putRequest.setEntity(personSEntity);
//Send the request; It will immediately return the response in HttpResponse object if any
HttpResponse response = httpClient.execute(putRequest);
//verify the valid error code first
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 201)
{
throw new RuntimeException("Failed with HTTP error code : " + statusCode);
}
}
finally
{
//Important: Close the connect
httpClient.getConnectionManager().shutdown();
}
}
public static void getObject() throws Exception
{
DefaultHttpClient httpClient = new DefaultHttpClient();
try
{
//Define a HttpGet request; You can choose between HttpPost, HttpDelete or HttpPut also.
//Choice depends on type of method you will be invoking.
HttpGet getRequest = new HttpGet("http://localhost:9084/ehcache-server/rest/screeningInstance/2221");
//Set the API media type in http accept header
getRequest.addHeader("accept", "application/x-java-serialized-object");
//Send the request; It will immediately return the response in HttpResponse object
HttpResponse response = httpClient.execute(getRequest);
//verify the valid error code first
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200)
{
throw new RuntimeException("Failed with HTTP error code : " + statusCode);
}
//Now pull back the response object
HttpEntity httpEntity = response.getEntity();
byte[] resultByteArray = EntityUtils.toByteArray(httpEntity);
Person person = (Person)SerializationUtils.deserialize(resultByteArray);
}
finally
{
//Important: Close the connect
httpClient.getConnectionManager().shutdown();
}
}
}
class Person implements Serializable{
String name;
String id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Override
public String toString() {
return "Person [name=" + name + ", id=" + id + "]";
}
}
I got the solution. It was mistake in my code:
While putting object, I have written below code. That was doing two time serialization. First from Person object to byte[] and second from byte[] to byte[].
SerializableEntity personSEntity = new SerializableEntity(SerializationUtils.serialize(person));
putRequest.setEntity(personSEntity);
This is the right approach:
SerializableEntity personSEntity = new SerializableEntity(person);
putRequest.setEntity(personSEntity);
After getting binary from REST, code should be like below to get Object:
HttpEntity httpEntity = response.getEntity();
InputStream inputStream = null;
try {
inputStream = httpEntity.getContent();
Person p = (Person) SerializationUtils.deserialize(inputStream);
System.out.println("Person:" + p.getName());
}
finally {
inputStream.close();
}
This worked like CHARM !!

Vert.x - RxJava - zip observables

I am trying to zip to observables using Vert.x and RxJava. I don't know if I am misunderstanding something or this is just some kind of bug. Here is the code.
public class BusVerticle extends Verticle {
public void start() {
final RxVertx rxVertx = new RxVertx(vertx);
Observable<RxMessage<JsonObject>> bus = rxVertx.eventBus().registerHandler("busName");
Observable<RxHttpClientResponse> httpResponse = bus.mapMany(new Func1<RxMessage<JsonObject>, Observable<RxHttpClientResponse>>() {
public Observable<RxHttpClientResponse> call(RxMessage<JsonObject> rxMessage) {
RxHttpClient rxHttpClient = rxVertx.createHttpClient();
rxHttpClient.coreHttpClient().setHost("localhost").setPort(80);
return rxHttpClient.getNow("/uri");
}
});
Observable<RxMessage<JsonObject>> zipObservable = Observable.zip(bus, httpResponse, new Func2<RxMessage<JsonObject>, RxHttpClientResponse, RxMessage<JsonObject>>() {
public RxMessage<JsonObject> call(RxMessage<JsonObject> rxMessage, RxHttpClientResponse rxHttpClientResponse) {
return rxMessage;
}
});
zipObservable.subscribe(new Action1<RxMessage<JsonObject>>() {
public void call(RxMessage<JsonObject> rxMessage) {
rxMessage.reply();
}
});
}
}
I want to make an HTTP request using information from the received message and then zip both observables, the event bus and the HTTP response, in order to reply to the message with information from the HTTP response.
I am not getting any response for the message where I am sending it.
Thanks in advance!
I have solved it with a workaround. Some kind of mixed solution.
public class BusVerticle extends Verticle {
public void start() {
final RxVertx rxVertx = new RxVertx(vertx);
vertx.eventBus().registerHandler("busName", new Handler<Message<JsonObject>>() {
public void handle(final Message<JsonObject> message) {
RxHttpClient rxHttpClient = rxVertx.createHttpClient();
rxHttpClient.coreHttpClient().setHost("localhost").setPort(80);
Observable<RxHttpClientResponse> httpRequest = rxHttpClient.getNow("/uri");
httpRequest.subscribe(new Action1<RxHttpClientResponse>() {
public void call(RxHttpClientResponse response) {
container.logger().error(response.statusCode());
message.reply(new JsonObject().putString("status", "ok"));
}
});
}
});
}
}