What's the Best Way to Handle Exceptions from Net::Http

What’s the best way to handle exceptions from Net::HTTP?

I agree it is an absolute pain to handle all the potential exceptions. Look at this to see an example:

Working with Net::HTTP can be a pain. It's got about 40 different ways
to do any one task, and about 50 exceptions it can throw.

Just for the love of google, here's what I've got for the "right way"
of catching any exception that Net::HTTP can throw at you:

begin
response = Net::HTTP.post_form(...) # or any Net::HTTP call
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
...
end

Why not just rescue Exception => e? That's a bad habit to get into, as
it hides any problems in your actual code (like SyntaxErrors, whiny
nils, etc). Of course, this would all be much easier if the possible
errors had a common ancestor.

The issues I've been seeing in dealing with Net::HTTP have made me
wonder if it wouldn't be worth it to write a new HTTP client library.
One that was easier to mock out in tests, and didn't have all these
ugly little facets.

What I've done, and seen most people do, is move away from Net::HTTP and move to 3rd party HTTP libraries such as:

httparty and faraday

http client exception handling in asp.net core

The easiest way would be to use the proper enum for comparison:

var response = await _httpClient.SendAsync(request);
try
{
response.EnsureSuccessStatusCode();
data = await response.Content.ReadFromJsonAsync<Abc>();
return data;
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound) ----how to check 404 error?
{
throw;
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.ServiceUnavailable)
{
throw;
}

Another option would be casting to (int?), but using the enum should provide better readibility.

ASP.NET Core Web API exception handling

Quick and Easy Exception Handling

Simply add this middleware before ASP.NET routing into your middleware registrations.

app.UseExceptionHandler(c => c.Run(async context =>
{
var exception = context.Features
.Get<IExceptionHandlerPathFeature>()
.Error;
var response = new { error = exception.Message };
await context.Response.WriteAsJsonAsync(response);
}));
app.UseMvc(); // or .UseRouting() or .UseEndpoints()

Done!



Enable Dependency Injection for logging and other purposes

Step 1. In your startup, register your exception handling route:

// It should be one of your very first registrations
app.UseExceptionHandler("/error"); // Add this
app.UseEndpoints(endpoints => endpoints.MapControllers());

Step 2. Create controller that will handle all exceptions and produce error response:

[AllowAnonymous]
[ApiExplorerSettings(IgnoreApi = true)]
public class ErrorsController : ControllerBase
{
[Route("error")]
public MyErrorResponse Error()
{
var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
var exception = context.Error; // Your exception
var code = 500; // Internal Server Error by default

if (exception is MyNotFoundException) code = 404; // Not Found
else if (exception is MyUnauthException) code = 401; // Unauthorized
else if (exception is MyException) code = 400; // Bad Request

Response.StatusCode = code; // You can use HttpStatusCode enum instead

return new MyErrorResponse(exception); // Your error model
}
}

A few important notes and observations:

  • You can inject your dependencies into the Controller's constructor.
  • [ApiExplorerSettings(IgnoreApi = true)] is needed. Otherwise, it may break your Swashbuckle swagger
  • Again, app.UseExceptionHandler("/error"); has to be one of the very top registrations in your Startup Configure(...) method. It's probably safe to place it at the top of the method.
  • The path in app.UseExceptionHandler("/error") and in controller [Route("error")] should be the same, to allow the controller handle exceptions redirected from exception handler middleware.

Here is the link to official Microsoft documentation.



Response model ideas.

Implement your own response model and exceptions.
This example is just a good starting point. Every service would need to handle exceptions in its own way. With the described approach you have full flexibility and control over handling exceptions and returning the right response from your service.

An example of error response model (just to give you some ideas):

public class MyErrorResponse
{
public string Type { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }

public MyErrorResponse(Exception ex)
{
Type = ex.GetType().Name;
Message = ex.Message;
StackTrace = ex.ToString();
}
}

For simpler services, you might want to implement http status code exception that would look like this:

public class HttpStatusException : Exception
{
public HttpStatusCode Status { get; private set; }

public HttpStatusException(HttpStatusCode status, string msg) : base(msg)
{
Status = status;
}
}

This can be thrown from anywhere this way:

throw new HttpStatusCodeException(HttpStatusCode.NotFound, "User not found");

Then your handling code could be simplified to just this:

if (exception is HttpStatusException httpException)
{
code = (int) httpException.Status;
}

HttpContext.Features.Get<IExceptionHandlerFeature>() WAT?

ASP.NET Core developers embraced the concept of middlewares where different aspects of functionality such as Auth, MVC, Swagger etc. are separated and executed sequentially in the request processing pipeline. Each middleware has access to request context and can write into the response if needed. Taking exception handling out of MVC makes sense if it's important to handle errors from non-MVC middlewares the same way as MVC exceptions, which I find is very common in real world apps. So because built-in exception handling middleware is not a part of MVC, MVC itself knows nothing about it and vice versa, exception handling middleware doesn't really know where the exception is coming from, besides of course it knows that it happened somewhere down the pipe of request execution. But both may needed to be "connected" with one another. So when exception is not caught anywhere, exception handling middleware catches it and re-runs the pipeline for a route, registered in it. This is how you can "pass" exception handling back to MVC with consistent content negotiation or some other middleware if you wish. The exception itself is extracted from the common middleware context. Looks funny but gets the job done :).

Robustly call a flaky API: proper error handling with Net::HTTP

Exceptions are meaningful, and Net::HTTP offers specific exceptions for different sorts of cases. So if you want to handle them each in a particular way, you can.

That article says that handling those specific exceptions is better/safer than handling rescue Exception, and that's very true. BUT, rescue Exception is different from rescue by itself, which is the equivalent to rescue StandardError, which is what you should usually do by default if you don't have a reason to do anything else.

Rescuing top-level Exception will rescue anything that could possibly happen in the entire execution stack, including some part of ruby running out of disk or memory or having some obscure system-related IO problem.

So, as far as "what to rescue", you're generally better off if you change your code to rescue. You'll catch everything you want to, and nothing that you don't want to. However, in this particular case, there is one lone exception in that guy's list that is NOT a descendent of StandardError:

def parents(obj)
( (obj.superclass ? parents(obj.superclass) : []) << obj)
end

[Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse,
Net::HTTPHeaderSyntaxError, Net::ProtocolError].inject([]) do |a,c|
parents(c).include?(StandardError) ? a : a << c
end
# Timeout::Error < Interrupt

parents(Timeout::Error)
# [ Object, Exception < Object, SignalException < Exception,
# Interrupt < SignalException, Timeout::Error < Interrupt ]

So you could change your code to rescue StandardError, Timeout::Error => e and you'll cover all the cases mentioned in that article, and more, but not the stuff that you don't want to cover. (the => e is not required, but more on that below).

Now, as far as your actual technique for dealing with the flakey API -- the question is, what's the problem with the API that you are dealing with? Badly formatted responses? No responses? Is the problem at the HTTP level or in the data you are getting back?

Maybe you don't yet know, or you don't yet care, but you know that retrying tends to get the job done. In that case, I would at least recommend logging the exceptions. Hoptoad has a free plan, and has some sort of thing like Hoptoad.notify(e) -- I can't remember if that's the exact invocation. Or you can email it or log it, using e.message and e.stacktrace.

How using try catch for exception handling is best practice

My exception-handling strategy is:

  • To catch all unhandled exceptions by hooking to the Application.ThreadException event, then decide:

    • For a UI application: to pop it to the user with an apology message (WinForms)
    • For a Service or a Console application: log it to a file (service or console)

Then I always enclose every piece of code that is run externally in try/catch :

  • All events fired by the WinForms infrastructure (Load, Click, SelectedChanged...)
  • All events fired by third party components

Then I enclose in 'try/catch'

  • All the operations that I know might not work all the time (IO operations, calculations with a potential zero division...). In such a case, I throw a new ApplicationException("custom message", innerException) to keep track of what really happened

Additionally, I try my best to sort exceptions correctly. There are exceptions which:

  • need to be shown to the user immediately

  • require some extra processing to put things together when they happen to avoid cascading problems (ie: put .EndUpdate in the finally section during a TreeView fill)

  • the user does not care, but it is important to know what happened. So I always log them:

  • In the event log

  • or in a .log file on the disk

It is a good practice to design some static methods to handle exceptions in the application top level error handlers.

I also force myself to try to:

  • Remember ALL exceptions are bubbled up to the top level. It is not necessary to put exception handlers everywhere.
  • Reusable or deep called functions does not need to display or log exceptions : they are either bubbled up automatically or rethrown with some custom messages in my exception handlers.

So finally:

Bad:

// DON'T DO THIS; ITS BAD
try
{
...
}
catch
{
// only air...
}

Useless:

// DON'T DO THIS; IT'S USELESS
try
{
...
}
catch(Exception ex)
{
throw ex;
}

Having a try finally without a catch is perfectly valid:

try
{
listView1.BeginUpdate();

// If an exception occurs in the following code, then the finally will be executed
// and the exception will be thrown
...
}
finally
{
// I WANT THIS CODE TO RUN EVENTUALLY REGARDLESS AN EXCEPTION OCCURRED OR NOT
listView1.EndUpdate();
}

What I do at the top level:

// i.e When the user clicks on a button
try
{
...
}
catch(Exception ex)
{
ex.Log(); // Log exception

-- OR --

ex.Log().Display(); // Log exception, then show it to the user with apologies...
}

What I do in some called functions:

// Calculation module
try
{
...
}
catch(Exception ex)
{
// Add useful information to the exception
throw new ApplicationException("Something wrong happened in the calculation module:", ex);
}

// IO module
try
{
...
}
catch(Exception ex)
{
throw new ApplicationException(string.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex);
}

There is a lot to do with exception handling (Custom Exceptions) but those rules that I try to keep in mind are enough for the simple applications I do.

Here is an example of extensions methods to handle caught exceptions a comfortable way. They are implemented in a way they can be chained together, and it is very easy to add your own caught exception processing.

// Usage:

try
{
// boom
}
catch(Exception ex)
{
// Only log exception
ex.Log();

-- OR --

// Only display exception
ex.Display();

-- OR --

// Log, then display exception
ex.Log().Display();

-- OR --

// Add some user-friendly message to an exception
new ApplicationException("Unable to calculate !", ex).Log().Display();
}

// Extension methods

internal static Exception Log(this Exception ex)
{
File.AppendAllText("CaughtExceptions" + DateTime.Now.ToString("yyyy-MM-dd") + ".log", DateTime.Now.ToString("HH:mm:ss") + ": " + ex.Message + "\n" + ex.ToString() + "\n");
return ex;
}

internal static Exception Display(this Exception ex, string msg = null, MessageBoxImage img = MessageBoxImage.Error)
{
MessageBox.Show(msg ?? ex.Message, "", MessageBoxButton.OK, img);
return ex;
}

Properly handling HttpClient exceptions within async / await

This is an artifact of the debugger. It's determining that an exception is "uncaught" because it's not caught yet. In this case this is expected behavior.

You are handling the exceptions correctly.

.Net Core API how to handle HTTP exceptions

Filters are registered globally in the Startup.cs via the options of AddMVC or AddControllersWithViews options.

Another way to handle exceptions globally is using an exception handling middleware which would catch also unexpected exceptions (which is my preferred way).

Handling exception HTTP request flutter

I used dio package. That's more easier and bug-less than i make it

https://pub.dev/packages/dio

Best way to handle HTTP 429 errors

One thing to consider here is that the api you are consuming might "penalize" you if you start going over the limit. I think it's best to be proactive and throttle your api requests, so you never receive the 429 errors.

We started experiencing the same thing on our app (429 errors) from an api we are consuming. The limit on this particular api was 10 every 60 seconds. We implemented a throttle() function that utilizes a memory cache with the date/time embedded. The api we used also tracked usage on an account basis. You may not need that. But this is a snippet we used to throttle our requests to ensure we were always under the limit:

    private void throttle()
{
var maxPerPeriod = 250;
//If you utilize multiple accounts, you can throttle per account. If not, don't use this:
var keyPrefix = "a_unique_id_for_the_basis_of_throttling";
var intervalPeriod = 300000;//5 minutes
var sleepInterval = 5000;//period to "sleep" before trying again (if the limits have been reached)
var recentTransactions = MemoryCache.Default.Count(x => x.Key.StartsWith(keyPrefix));
while (recentTransactions >= maxPerPeriod)
{
System.Threading.Thread.Sleep(sleepInterval);
recentTransactions = MemoryCache.Default.Count(x => x.Key.StartsWith(keyPrefix));
}
var key = keyPrefix + "_" + DateTime.Now.ToUniversalTime().ToString("yyyyMMddHHmm");
var existing = MemoryCache.Default.Where(x => x.Key.StartsWith(key));
if (existing != null && existing.Any())
{
var counter = 2;
var last = existing.OrderBy(x => x.Key).Last();
var pieces = last.Key.Split('_');
if (pieces.Count() > 2)
{
var lastCount = 0;
if (int.TryParse(pieces[2], out lastCount))
{
counter = lastCount + 1;
}
}
key = key + "_" + counter;
}
var policy = new CacheItemPolicy
{
AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(intervalPeriod)
};
MemoryCache.Default.Set(key, 1, policy);
}

We used this throttle() function in our code like this:

    public override void DoAction()
{
throttle();
var url = ContentUri;
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.Headers.Add("Authorization", "Bearer " + AccessToken);
request.Accept = "application/json";
WebResponse response = request.GetResponse();
var dataStream = new MemoryStream();
using (Stream responseStream = request.GetResponse().GetResponseStream())
{
//DO STUFF WITH THE DOWNLOADED DATA HERE...
}
dataStream.Close();
response.Close();
}

It essentially keeps track of your requests in the cache. If the limit has been reached, it pauses until enough time has passed so that you are still under the limit.

.Net Core Handle exceptions that returned from web api

According to your description, I suggest you could use try catch on the server-side to capture the exception and return as a json response.

In the client side, you could use deserlize the response and create a new view named Error to show the response message.

More details, you could refer to below codes:

Error Class:

public class APIError
{
public string Version { get; set; }
public string StatusCode { get; set; }
public string ErrorMessage { get; set; }
}

API:

[HttpGet]
public IActionResult Get()
{
try
{
throw new Exception("UserNotFound");
}
catch (Exception e)
{

return Ok(new APIError { Version="1.0", ErrorMessage=e.Message, StatusCode="500" });
}


}

Application:

       var request = new HttpRequestMessage(HttpMethod.Get,
"https://localhost:44371/weatherforecast");


var client = _clientFactory.CreateClient();

var response = await client.SendAsync(request);

if (response.IsSuccessStatusCode)
{
var responseStream = await response.Content.ReadAsStringAsync();
APIError re = JsonSerializer.Deserialize<APIError>(responseStream, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
});

if (re.StatusCode == "500")
{

return View("Error", new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier, Version = re.Version, StatusCode = re.StatusCode, ErrorMessage = re.ErrorMessage });

}


}
else
{
// Hanlde if request failed issue
}

Notice: I created a new Error view, you could create it by yourself or modify the default error view.

Error Viewmodel:

public class ErrorViewModel
{
public string RequestId { get; set; }

public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

public string Version { get; set; }
public string StatusCode { get; set; }
public string ErrorMessage { get; set; }
}

Error view:

@model ErrorViewModel
@{
ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}

<h3>@Model.StatusCode</h3>
<p>
@Model.ErrorMessage
</p>

Result:

Sample Image



Related Topics



Leave a reply



Submit