I am trying to learn how to handle exception in APIs servers so I followed this where he has built API for birds, he finally reached to APIs like this:
#GetMapping(value = "/params")
public Bird getBirdRequestParam(#RequestParam("birdId") Long birdId) throws EntityNotFoundException {
Bird bird = birdRepository.findOne(birdId);
if(bird == null){
throw new EntityNotFoundException(Bird.class, "id", birdId.toString());
}
return bird;
}
and the ControllerAdvice has a method:
#ExceptionHandler(EntityNotFoundException.class)
protected ResponseEntity<Object> handleEntityNotFound(
EntityNotFoundException ex) {
ApiError apiError = new ApiError(NOT_FOUND);
apiError.setMessage(ex.getMessage());
return buildResponseEntity(apiError);
}
the response will be a bird like this
{
"id": 1,
"scientificName": "Atlantic canary",
"specie": "serinus canaria",
"mass": 10,
"length": 11
}
or an exception details like this:
{
"apierror": {
"status": "NOT_FOUND",
"timestamp": "09-04-2018 10:11:44",
"message": "Bird was not found for parameters {id=2}"
}
but the problem is with my server that contacts with API
I am using :
public void gett(#RequestParam Long id) {
ResponseEntity<Bird> responseEntity = restTemplate.getForEntity("http://localhost:8181/params" + id, Bird.class);
bird = responseEntity.getBody();
model.addAttribute("birdform", bird);
return "bird";
}
the getForEntity is waiting for a response with bird body but an exception may be thrown in server and the response may be json of the error.
how to handle this problem in my client server?
in other words :
how to know in my client server that the api server has thrown an exception in json form.???
I have tried to get the response in "Object" variable and then try to know if it was excption or bird with "instance of" expression like this code
#GetMapping("/getbird")
public String getAll(#RequestParam Long id, Model model) {
ResponseEntity<Object> responseEntity = restTemplate.getForEntity("http://localhost:8181/api/bird/getone?id=" + id, Object.class);
if (responseEntity.getBody() instanceof Bird.class) {
Bird bird= (Bird) responseEntity.getBody();
model.addAttribute("Bird", bird);
return "bird-form";
}
else{
// something else
return "someview";
}
}
but first thing it didnot work (the instance of always return false)
the second thing is that this is a hard work to do with all my controllers' actions.
I hope that i could explain my problem clearly .
thanks....
You don't need to check the body of the response to see if you got an error. restTemplate.getForEntity() (and other methods) would throw an HttpClientErrorException if you get a 4XX response, or HttpServerErrorException if you get a 5XX response from the call.
To catch these exceptions, you can define a #ControllerAdvice with proper methods for exception handling:
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 500
#ExceptionHandler(HttpClientErrorException.class)
public ApiError handle4xxFromClient(HttpClientErrorException ex) {
// construct and return a custom ApiError object
}
}
See https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc for details.
Two solutions :
Use a custom implementation error handler in your reste template.
restTemplate.setErrorHandler(customerErrorHandler)
Use the http status, rest template will throw an exception if it gets a bad http status from the server it is querying. Catch the exception and get the ApiError Object from the exception, and then throw and exception of your own that will be handled by your exception handler in your client server.
For these solutions to work, the server your are querying needs to send the right http status code when something wrong happens.
Related
I am using WebClient from Spring WebFlux to communicate with a REST API backend from a Spring client.
When this REST API backend throws an exception, it answers with a specific format (ErrorDTO) that I would like to collect from my client.
What I have tried to do is to make my client throw a GestionUtilisateurErrorException(ErreurDTO) containing this body once the server answers with a 5xx HTTP status code.
I have tried several options :
I/ onStatus
#Autowired
WebClient gestionUtilisateursRestClient;
gestionUtilisateursRestClient
.post()
.uri(profilUri)
.body(Mono.just(utilisateur), UtilisateurDTO.class)
.retrieve()
.onStatus(HttpStatus::is5xxServerError,
response -> {
ErreurDTO erreur = response.bodyToMono(ErreurDTO.class).block();
return Mono.error(new GestionUtilisateursErrorException(erreur));
}
)
.bodyToMono(Void.class)
.timeout(Duration.ofMillis(5000))
.block();
This method doesn't work because webclient doesn't allow me to call the block method in the onStatus. I am only able to get a Mono object and I can't go further from here.
It seems like "onStatus" method can't be used in a WebClient blocking method, which means I can throw a custom Exception, but I can't populate it with the data from the response body.
II/ ExchangeFilterFunction
#Bean
WebClient gestionUtilisateursRestClient() {
return WebClient.builder()
.baseUrl(gestionUtilisateursApiUrl)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.filter(ExchangeFilterFunction.ofResponseProcessor(this::gestionUtilisateursExceptionFilter))
.build();
}
private Mono<ClientResponse> gestionUtilisateursExceptionFilter(ClientResponse clientResponse) {
if(clientResponse.statusCode().isError()){
return clientResponse.bodyToMono(ErreurDTO.class)
.flatMap(erreurDto -> Mono.error(new GestionUtilisateursErrorException(
erreurDto
)));
}
return Mono.just(clientResponse);
}
This method works but throw a reactor.core.Exceptions$ReactiveException that I am struggling to catch properly (reactor.core.Exceptions is not catchable, and ReactiveException is private).
This Exception contains in its Cause the exception I need to catch (GestionUtilisateurErrorException) but I need a way to catch it properly.
I also tried to use "onErrorMap" and "onErrorResume" methods but none of them worked the way I needed.
Edit 1 :
I am now using the following workaround even if I feel it's a dirty way to do what I need :
gestionUtilisateursRestClient
.post()
.uri(profilUri)
.body(Mono.just(utilisateur), UtilisateurDTO.class)
.retrieve()
.onStatus(h -> h.is5xxServerError(),
response -> {
return response.bodyToMono(ErreurDTO.class).handle((erreur, handler) -> {
LOGGER.error(erreur.getMessage());
handler.error(new GestionUtilisateursErrorException(erreur));
});
}
)
.bodyToMono(String.class)
.timeout(Duration.ofMillis(5000))
.block();
}
catch(Exception e) {
LOGGER.debug("Erreur lors de l'appel vers l'API GestionUtilisateur (...)");
if(ExceptionUtils.getRootCause(e) instanceof GestionUtilisateursErrorException) {
throw((GestionUtilisateursErrorException) e.getCause());
}
else {
throw e;
}
}
Here, it throws the expected GestionUtilisateursErrorException that I can handle synchronously.
I might implement this in a global handler to avoid writing this code around each call to my API.
Thank you.
Kevin
I've encountered a similar case for accessing the response body that might be of use to you using the Mono.handle() method (see https://projectreactor.io/docs/core/release/api/index.html?reactor/core/publisher/Mono.html).
Here handler is a SynchronousSink (see https://projectreactor.io/docs/core/release/api/reactor/core/publisher/SynchronousSink.html) and can call at most next(T) one time, and either complete() or error().
In this case, I call 'handler.error()' with a new GestionUtilisateursErrorException constructed with the 'erreur'.
.onStatus(h -> h.is5xxServerError(),
response -> {
return response.bodyToMono(ErreurDTO.class).handle((erreur, handler) -> {
// Do something with erreur e.g.
log.error(erreur.getErrorMessage());
// Call handler.next() and either handler.error() or handler.complete()
handler.error(new GestionUtilisateursErrorException(erreur));
});
}
)
I'm new to MSF4J and I need to write a REST API that accepts a large XML data through POST. I am using
request.getMessegeBody()
method to get the data. I discovered that it's now deprecated but I couldn't find the newer version of it so I decided to use it anyway.
The problem is, when I send data to the microservice for the first time, it doesn't get the whole data. All the subsequent requests will get the full message body except the first.
When I try passing the request through ESB, ESB receives the whole body but when it reaches the endpoint it will be truncated.
I have also tried sending requests from different rest clients but for the first time it always gets the incomplete message body
#POST
#Consumes({ "application/xml", "application/json", "text/xml" })
#Path("test/")
public Response getReqNotification(#Context Request request) throws Exception {
Response.ResponseBuilder respBuilder =
Response.status(Response.Status.OK).entity(request);
ByteBuf b = request.getMessageBody();
byte[] bb = new byte[b.readableBytes()];
b.duplicate().readBytes(bb);
System.out.println(new String(bb));
return respBuilder.build();
}
I expect it to print the full message(which is about 2000 bytes long) every time when I send a request, but I'm only getting around 800 bytes when I first run the microservice.
I hope ill get assistance here. I have tried elsewhere but wso2 doesn't have much documentation (⌣_⌣”)
I still don't really understand what I was doing wrong but with the help of this link I have managed to come up with the following code and it works fine.
The major cha is that I now use request.getMessageContentStream() instead of the depricated request.getMessageBody()
#Consumes({ "application/xml", "application/json", "text/xml" })
#Path("test/")
public Response getReqNotification(#Context Request request) throws Exception {
Response.ResponseBuilder respBuilder =
Response.status(Response.Status.OK).entity(request);
String data = "";
BufferedInputStream bis = new BufferedInputStream(request.getMessageContentStream());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
int d;
while ((d = bis.read()) != -1) {
bos.write(d);
}
data = bos.toString();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
bos.close();
} catch (IOException e) {
}
}
System.out.println(data);
//////do stuff
return respBuilder.build();
}
I am exposing a rest service using "CamelHttpTransportServlet" that receive orders and place in jms queue. The code works fine on happy path and returns 200 response.
I have written Processor to validate the input JSON, and set http_response_code based on the input.
The issue is - for invalid requests though failure response code - 400 is set, the flow continues to the next route and pushes the data to the queue instead of sending the 400 response back to the calling app.
rest("/ordermanagement")
.post("/order").to("direct:checkInput");
from("direct:checkInput")
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
String requestBody = exchange.getIn().getBody(String.class);
if(requestBody == "" || requestBody== null) {
exchange.getIn().setBody("{ "error": Bad Request}");
exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/json");
exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
}
}
})
.to("direct:sendToQ");
from("direct:sendToQ")
.to("jms:queue:orderReceiver")
.log("Sent to JMS");
Can someone advise what is missing here and provide a sample if possible?
Trying to implement onException approach:
rest("/ordermanagement")
.post("/order").to("direct:checkInput");
onException(CustomException.class).handled(true)
.setHeader(Exchange.HTTP_RESPONSE_CODE, code)
.setBody(jsonObject);
from("direct:checkInput")
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
String requestBody = exchange.getIn().getBody(String.class);
if(requestBody == "" || requestBody== null) {
throw CustomException(code, jsonObject)
}
}
})
.to("direct:sendToQ");
from("direct:sendToQ")
.to("jms:queue:orderReceiver")
.log("Sent to JMS");
However I could not figure out how to pass the parameters - code,jsonObject from processor to onException block.
Any help on this? Is this feasible?
I'd use something along the lines of the code example below:
onException(CustomException.class)
.handled(true)
.bean(PrepareErrorResponse.class)
.log("Error response processed");
rest("/ordermanagement")
.post("/order")
.to("direct:checkInput");
from("direct:checkInput")
.process((Exchange exchange) -> {
String requestBody = exchange.getIn().getBody(String.class);
if(requestBody == "" || requestBody== null) {
throw new CustomException(code, jsonObject);
}
})
.to("direct:sendToQ");
from("direct:sendToQ")
.to("jms:queue:orderReceiver")
.log("Sent to JMS");
Camel will store any exception caught in the exchange's property and should be therefore obtainable via the Exchange.EXCEPTION_CAUGHT property key. The sample below illustrates how such a custom error message bean can look like:
public class PrepareErrorResponse {
#Handler
public void prepareErrorResponse(Exchange exchange) {
Throwable cause = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Throwable.class);
if (cause instanceof CustomException) {
CustomException validationEx = (CustomException) cause;
// ...
}
Message msg = exchange.getOut();
msg.setHeader(Exchange.CONTENT_TYPE, MediaType.APPLICATION_JSON);
msg.setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
JsonObject errorMessage = new JsonObject();
errorMessage.put("error", "Bad Request");
errorMessage.put("reason", cause.getMessage());
msg.setBody(errorMessage.toString());
// we need to do the fault=false below in order to prevent a
// HTTP 500 error code from being returned
msg.setFault(false);
}
}
Camel provides a couple of ways actually to deal with exceptions. The presented way here is just one example. The proposed code however allows to use custom redelivery strategies for different caught exceptions as well as additional stuff. If the error could get resolved within the exception handler, the route is proceeded at the point the exception occurred (i.e. temporary network issue with a redelivery strategy applied). If the error could not get fixed within the handler, the exchange will be stopped. Usually one would then send the currently processed message to a DLQ and log something about the error.
Note that this example will assume that CustomException is an unchecked exception as the processor is replaced with a simpler lambda. If you can't or don't want to use such an exception (or lambda expressions) replace the lambda-processor with new Processor() { #Override public void process(Exchange exchange) throws Exception { ... } } construct.
Here is one way to do it. You can use choice
rest("/ordermanagement")
.post("/order").to("direct:checkInput");
from("direct:checkInput")
.process(exchange -> {
String requestBody = exchange.getIn().getBody(String.class);
if(requestBody == null || requestBody.equals("")) {
exchange.getIn().setBody("{ "error": Bad Request}");
exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/json");
exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
}
})
.choice()
.when(exchange -> {
Object header = exchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE);
return header != null && header.equals(400);
})
.stop()
.otherwise()
.to("direct:sendToQ")
.endChoice();
from("direct:sendToQ")
.to("jms:queue:orderReceiver")
.log("Sent to JMS");
Setting ROUTE_STOP property to true in the processor should prevent further flow and return your response:
...
exchange.getIn().setBody("{ "error": Bad Request}");
exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/json");
exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE);
...
I am new to Jmeter and am looking for a way to send emails out for every failed assertion.
Test Structure:
Thread Group:
Transaction Controller:
Http Request:
Http Request:
Http Request:
Thread Group:
Transaction Controller:
Http Request:
Http Request:
Http Request:
Each Http request contains a response assertion. I would like to capture and send one email that contains all of the failed assertions within a transaction controller. Is there a way to do this? I tried using adding a SMTP Sampler within a thread group with a child Bean PreProcessor that contains this code:
import org.apache.jmeter.assertions.AssertionResult;
try {
AssertionResult[] results = prev.getAssertionResults();
StringBuilder body = new StringBuilder();
for (AssertionResult result : results) {
body.append(result.getName());
body.append(result.getFailureMessage());
body.append(System.getProperty("line.separator"));
}
vars.put("body", body.toString());
}
catch (Throwable ex) {
log.error("Error in Beanshell", ex);
throw ex;
}
When i do this it will only send an email of the last failed assertion instead of all failed assertions.
Add a global JSR223 Listener (it is much better to use Groovy rather than Beanshell, see Groovy is the New Black article for details)
Put the following code into "Script" area (it is basically your code but amended to read previous samplers results from the ${body} variable and append new results to them
import org.apache.jmeter.assertions.AssertionResult;
try {
AssertionResult[] results = prev.getAssertionResults();
StringBuilder body = new StringBuilder();
String previousBody = vars.get("body");
if (previousBody != null) {
body.append(previousBody);
}
for (AssertionResult result : results) {
body.append(result.getName());
body.append(result.getFailureMessage());
body.append(System.getProperty("line.separator"));
}
vars.put("body", body.toString());
} catch (Throwable ex) {
log.error("Error in Groovy", ex);
throw ex;
}
We have a test that prints out request body information to the console on failure. This is added to each sampler individually and works quite nicely. You could update this code send a failure message instead of printing to the console.
//Loop through assertions and check for failed
for (int i = 0; i < results.length; i++){
if (results[i].isFailure() || results[i].isError()){
failed_assertion = true;
System.out.println("\n********** Request Body ***************\n"+ctx.getCurrentSampler().getUrl().getPath()+ctx.getCurrentSampler().getQueryString()+'\n'+ctx.getCurrentSampler().getArguments().getArgument(0).getValue()+"\n");
System.out.println("************ ERROR DETECTED ***********\n"+prev.getResponseDataAsString()+"\n****************************************\n");
break;
}
}
If that doesn't work you could construct an IF block on each sampler. Similar to what they're doing here.
Hi I'm using the restlet studio to generate a client and server from your sample pet store API . Here are my steps:
Generate Java Server (JAX-RS)
Edit pom.xml to make a war file
mvn package
Deploy to jetty server as webapp
Verify it works by going to hitting the URL with a browser:
http://54.149.215.125:8080/v2/pet/findByTags
Response:
{"code":4,"type":"ok","message":"magic!"}
At this point I think it works, until I generate the client in Java
I change the endpoint from the webnik one to my webserver
Make a simple main method
public static void main(String[] args) {
try {
FindPetByTagsClientResource a = new FindPetByTagsClientResource();
Pet represent = a.represent();
} catch (Exception ex) {
Logger.getLogger(APIPetStore.class.getName()).log(Level.SEVERE, null, ex);
}
}
When I run it I get this:
run:
Starting the internal HTTP client
null
Unprocessable Entity (422) - The server understands the content type of the request entity and the syntax of the request entity is correct but was unable to process the contained instructions
at org.restlet.resource.Resource.toObject(Resource.java:893)
at org.restlet.engine.resource.ClientInvocationHandler.invoke(ClientInvocationHandler.java:326)
at com.sun.proxy.$Proxy5.represent(Unknown Source)
at net.apispark.webapi.client.FindPetByTagsClientResource.represent(FindPetByTagsClientResource.java:22)
at apipetstore.APIPetStore.main(APIPetStore.java:28)
Caused by: java.io.IOException: Unable to create the Object representation
at org.restlet.engine.converter.DefaultConverter.toObject(DefaultConverter.java:282)
at org.restlet.service.ConverterService.toObject(ConverterService.java:229)
at org.restlet.resource.Resource.toObject(Resource.java:889)
... 4 more
Caused by: java.lang.IllegalArgumentException: The serialized representation must have this media type: application/x-java-serialized-object or this one: application/x-java-serialized-object+xml
at org.restlet.representation.ObjectRepresentation.(ObjectRepresentation.java:221)
at org.restlet.representation.ObjectRepresentation.(ObjectRepresentation.java:123)
at org.restlet.representation.ObjectRepresentation.(ObjectRepresentation.java:104)
at org.restlet.engine.converter.DefaultConverter.toObject(DefaultConverter.java:279)
... 6 more
BUILD SUCCESSFUL (total time: 0 seconds)
Change the main method to this and it works:
public static void main(String[] args) {
try {
FindPetByTagsClientResource a = new FindPetByTagsClientResource();
a.getClientResource().get().write(System.out);
} catch (Exception ex) {
Logger.getLogger(APIPetStore.class.getName()).log(Level.SEVERE, null, ex);
}
}
Output:
Starting the internal HTTP client
{"code":4,"type":"ok","message":"magic!"}
Any ideas on how I can fix this?
In fact, the JAXRS server skeleton is really a server skeleton ;-) This means that it doesn't actually send back the right content according to the client. If you look at the server code, you always see this:
public Response findPetsByTags(#ApiParam(value = "Tags to filter by") #QueryParam("tags") List<String> tags)
throws NotFoundException {
// do some magic!
return Response.ok().entity(new ApiResponseMessage(ApiResponseMessage.OK, "magic!")).build();
}
It doesn't correspond to a list of pet objects...
On the client side, you got the error since you try to use annotated interfaces. They automatically try to use the internal converter of Restlet. It fails since it expects an object of type Pet and you received something with this structure: {"code":4,"type":"ok","message":"magic!"}.
In conclusion, you need to do some work to adapt the server skeleton to return the correct objects. Here is an hardcoded solution to make work your client SDK:
#GET
#Path("/findByTags")
#ApiOperation(value = "Finds Pets by tags", notes = "Finds Pets by tags", response = Pet.class, responseContainer = "List")
#ApiResponses(value = {
#ApiResponse(code = 400, message = "") })
public Response findPetsByTags(#ApiParam(value = "Tags to filter by") #QueryParam("tags") List<String> tags)
throws NotFoundException {
// do some magic!
Pet pet = new Pet();
pet.setId(10);
pet.setName("My pet");
pet.setStatus("status");
List<Tag> actualTags = new ArrayList<Tag>();
Tag tag1 = new Tag();
tag1.setId(1);
tag1.setName("tag1");
actualTags.add(tag1);
Tag tag2 = new Tag();
tag2.setId(2);
tag2.setName("tag2");
actualTags.add(tag2);
pet.setTags(actualTags);
return Response.ok().entity(pet).build();
}
I'll have a look if we can improve this for the server side. In fact, the Restlet Studio internally uses the swagger2 codegen tool chain to generate this server skeleton.
Hope it helps,
Thierry