How to Disable zuul filter for specific condition case and not sending to mapped URL - spring-boot

I have zuul filter implementation with route config
related:
path: /api/search/related/**
url: http://abc.xyz.neverhit.this.URl
and run implementation
#Override
public Object run() {
RequestContext context = getCurrentContext();
HttpServletRequest request = context.getRequest();
UriComponents uri = UriComponentsBuilder.fromHttpUrl(recommendationsServiceHostname)
.path("/recommendations/related")
.query(request.getQueryString()).build();
if (shouldRouteToRecommendationsService(request, uri)) {
logger.info("Calling proxy service");
try {
context.setRouteHost(new URL(uri.toString()));
} catch (MalformedURLException ex) {
logger.error("MalformedURLException for URL:" + uri.toString());
}
}
else
{
//Something here or Solution that should handle a request like a filter is not present.
}
return null;
}
Its working fine for if part and sending the request to proxy service. Problem is for else part.
What I am looking for is in else scenario it should behave like filter never existed and it should handle request it was handling early executing API call from local code.
Any hack or proper solution for this one ?

Related

How to return instance of Error using Spring REST call

In my front end application, I have to handle the error response of the HTTP REST call.
Front end:
restservice.check().subscribe(
response => {
if (response != null) {
},
error => {
if (error instanceof Error) {
}}
});
Controller.java
public ResponseEntity updateEstablishment
{
return new ResponseEntity<>(obj, HttpStatus.OK);
}
How can I return an instance of Error here.
REST services should catch and handle internal exception. And should return meaningful error code and message back to client. Please find below link to best way to handle exception in REST
https://www.baeldung.com/rest-api-error-handling-best-practices

spring boot DeferredResult onError how to invoke the callback?

Need to perform some asynchronous processing in a Rest service without holding up the server's Http threads .
I think DeferredResult would be a good option.
However when I am trying to ensure my callback on error gets called - am not able to do so .
Here is a naive attempt on my part:
#GetMapping("/getErrorResults")
public DeferredResult<ResponseEntity<?>> getDeferredResultsError(){
final String METHOD_NAME = "getDeferredResultsError";
logger.info("START : {}",METHOD_NAME);
DeferredResult<ResponseEntity<?>> deferredOutput = new DeferredResult<>();
ForkJoinPool.commonPool().submit(() -> {
logger.info("processing in separate thread");
int age = 0;
try {
age = age / 0;
}catch(Exception e) {
logger.error("we got some error");
logger.error(e);
throw e;
}
logger.info("after try catch block");
});
deferredOutput.onError((Throwable t) -> {
logger.error("<<< HERE !!! >>>");
deferredOutput.setErrorResult(
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(t.getMessage()));
});
logger.info("done");
return deferredOutput;
}
When I call this Rest endpoint from Postman - I can see in server logs the arithmetic exception by zero but dont see the 'onError' getting invoked.
After some time get a response in Postman as follows:
{
"timestamp": "2019-07-30T09:57:16.854+0000",
"status": 503,
"error": "Service Unavailable",
"message": "No message available",
"path": "/dfr/getErrorResults"
}
So my question is how does the 'onError' get invoked ?
You need to pass the DeferredResult object to the asynchronous operation so you could update it in case of success or failure:
#GetMapping(value = "/getErrorResults")
public DeferredResult<ResponseEntity<String>> getDeferredResultsError() {
DeferredResult<ResponseEntity<String>> deferredResult = new DeferredResult<>();
ForkJoinPool.commonPool().submit(() -> {
System.out.println("Processing...");
int age = 0;
try {
age = age / 0;
deferredResult.setResult(ResponseEntity.ok("completed"));
}catch(Exception e) {
System.out.println("Failed to process: " + e.getMessage());
deferredResult.setErrorResult(
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(e.getMessage()));
}
});
return deferredResult;
}
In the code you posted, you returned the DeferredResult object without passing it to the asynchronous operation. So after your return it, SpringMVC holds the client connection and wait until the DeferredResult object will be assigned with some kind of result. But in your case, the DeferredResult is not held by the asynchronous operation and will never updated so you get "Service Unavailable".
Here you can find working (light) project example.

Customizing the criteria for triggering Fallback in Hystrix Circuit Breaker

I would like to trigger a fallback from a #HystrixCommand Method based on my own criteria (checking for a specific response status).
My method basically acts as a client which calls a service in another URL (marked here as URL).
Here is my code:
#HystrixCommand(fallbackMethod="fallbackPerformOperation")
public Future<Object> performOperation(String requestString) throws InterruptedException {
return new AsyncResult<Object>() {
#Override
public Object invoke() {
Client client = null;
WebResource webResource = null;
ClientResponse response =null;
String results = null;
try{
client = Client.create();
webResource = client.resource(URL);
client.setConnectTimeout(10000);
client.setReadTimeout(10000);
response = webResource.type("application/xml")
.post(ClientResponse.class, requestString);
logger.info("RESPONSE STATUS: " + response.getStatus());
if (response.getStatus() != 200) {
webResource = null;
logger.error(" request failed with the HTTP Status: " + response.getStatus());
throw new RuntimeException(" request failed with the HTTP Status: "
+ response.getStatus());
}
results = response.getEntity(String.class);
} finally {
client.destroy();
webResource = null;
}
return results;
}
};
}
This triggers the fallback Method fallbackPerformOperation() when the response status code is not 200 i.e. response.getStatus()!=200.
The fallback method returns a string which tells the user that the Request did not return a status of 200 and so it is falling back.
I want to know if I can trigger the fallback without having to explicitly throw an exception inside my performOperation() Method.
Could I use #HystrixProperty? I know people mostly use it for timeouts and volume thresholds but could I write a custom #HystrixProperty that checks if the response status is 200 or not within my Method?

MVC 6 WebAPI returning html error page instead of json version of exception object

I am calling an api endpoint in an MVC 6 WebAPI:
POST http://localhost:57287/mytestapi/testentity/ HTTP/1.1
Accept: application/json
X-APIKey: 00000000-0000-0000-0000-000000000000
Content-Type: application/json; charset=utf-8
Host: localhost:57287
Content-Length: 1837
Expect: 100-continue
Connection: Keep-Alive
In the body I have json serialized test entity.
I have a bug in my entity controller code and the api is returning a 500 response 'Server Error' I know what the bug is an will fix it, however the issue I need some help with is that the API is returning HTML instead of the json serialized exception object - Json is what I expect: it's what the old webapi would return. I have ported the coded from an old test project that I know works.
So why is MVC 6 WebAPI returning html rather than json? Is there some configuration I need to do?
EDIT:
I added Accept: application/json to headers as suggested by #danludwig, however this did not resolve the issue, I still got an html error page back.
I looked at my StartUp.cs and found:
if (env.IsDevelopment())
{
//app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
in the ConfigureApp method. I tested with app.UseDeveloperExceptionPage(); commented out. This prevented the return of the html error page in the api response body, however I am still not getting the json serialised exception object.
The ExceptionHandlerMiddleware configured when using UseExceptionHandler("Home/Error") does not include any support for JSON. It will just return the error html page. The same can be said when using UseDeveloperExceptionPage.
As far as I know you will need to add yourself some piece of code that will handle errors and return a json.
One option is to use an exception filter and add it either globally or on selected controllers, although this approach would only cover exceptions coming from the controller action methods. For example the following filter will return a json object only when the request accept was application/json (Otherwise it would let the exception pass through which for example could be handled by the global error page):
public class CustomJSONExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
if (context.HttpContext.Request.GetTypedHeaders().Accept.Any(header => header.MediaType == "application/json"))
{
var jsonResult = new JsonResult(new { error = context.Exception.Message });
jsonResult.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
context.Result = jsonResult;
}
}
}
services.AddMvc(opts =>
{
//Here it is being added globally.
//Could be used as attribute on selected controllers instead
opts.Filters.Add(new CustomJSONExceptionFilter());
});
Another option is to add your own exception handler middleware using the app.UseExceptionHandler overload that lets you specify the behavior of the alternative pipeline that will process the exception. I have quickly wrote a similar example using an inline middleware, which will return a json object only when the request accept was application/json:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseExceptionHandler(appBuilder =>
{
appBuilder.Use(async (context, next) =>
{
var excHandler = context.Features.Get<IExceptionHandlerFeature>();
if (context.Request.GetTypedHeaders().Accept.Any(header => header.MediaType == "application/json"))
{
var jsonString = string.Format("{{\"error\":\"{0}\"}}", excHandler.Error.Message);
context.Response.ContentType = new MediaTypeHeaderValue("application/json").ToString();
await context.Response.WriteAsync(jsonString, Encoding.UTF8);
}
else
{
//I haven't figured out a better way of signally ExceptionHandlerMiddleware that we can't handle the exception
//But this will do the trick of letting the other error handlers to intervene
//as the ExceptionHandlerMiddleware class will swallow this exception and rethrow the original one
throw excHandler.Error;
}
});
});
Both approaches will let you have other error handlers that maybe provide html pages for non json requests (Another idea would be to either return a json or an html page from your custom error handler).
PS. If using the second approach, you most likely want to put that logic into its own middleware class and use a different approach to generate the json response. In that case take a look at what JsonResultExecutor does
I found a cheap hack to get what I want by adding this to the Startup Configure method:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Simple error page to avoid a repo dependency.
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
if (context.Response.HasStarted)
{
throw;
}
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
var json = JToken.FromObject(ex);
await context.Response.WriteAsync(json.ToString());
}
});
//Rest of configure method omitted for brevity.
}

Volley retry request

I am currently testing out the volley library. But when request fails (404) it doesn't get executed again or at least there are no errors.However there is data missing. Is this the right way to retry a request if it has been failed ?
Thanks in advance
req.setRetryPolicy(new DefaultRetryPolicy(5000,1,1.0f));
queue.add(req);
Usage :
JsonObjectRequest req = null;
for(int i=0;i<profielen.size();i++){
final int pos = i;
req = new JsonObjectRequest(Request.Method.GET, imageLocUrl, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
setImageOnProfile(pos,response.get("thumbnail").toString());
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
req.setRetryPolicy(new DefaultRetryPolicy(5000,1,1.0f));
queue.add(req);
}
No, that is not the right way.
Asides:
HTTP 404 is not a status code I would expect a normally-behaved HTTP
client under normal condition to retry.
You most like are receiving an error via the error listener you
supply to the request, but your error listener is a NOOP so maybe
you're not noticing?
(http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html has OK
descriptions of the status code meanings.)
The request's retry policy only applies to failures due to: open socket timeouts, socket opening timeouts, HTTP 401s and HTTP 403s. All other failures are not automatically retried, AFAIK.
I think that to retry a 404 with Volley you need to retry it by hand in onErrorResponse.
(Ficus: it would be nice if the RetryPolicy was consulted for error status codes. I would like to be able to set a policy that retries on 503s.)

Resources