i have these class
//Class 1, ViewModel
public async System.Threading.Tasks.Task<JObject> ExecuteSystemObject(string parameters)
{
...
dynamic j = await ExternalProject.ExecuteSomething<MyModel>(parameters);
//How i can catch the error from the another class?
...
}
//Class2, Manager
public async Task<Object> ExecuteSomething<T>() where T : IModel, new()
{
...
WebResponse response = await ExternalProject.ExecuteRequestAsync(PostRequest);
...
}
//Class 3, from a binding Project
public static async Task<WebResponse> ExecuteRequestAsync(WebRequest request)
{
try
{
return await request.GetResponseAsync();
}
catch(WebException e)
{
var resp = new StreamReader(e.Response.GetResponseStream()).ReadToEnd();
dynamic obj = JsonConvert.DeserializeObject(resp);
//I have the message error here
var messageFromServer = obj.error.text;
throw e;
}
}
I can get the error only in the last class, if i try to get the WebException in the other, it will return null for me. Then, how can i pass that error to the main class(1ยบ one, ViewModel)?
Always use throw; whenever you want to rethrow an exception for you to be able to retain the stacktrace.
public async System.Threading.Tasks.Task<JObject> ExecuteSystemObject(string parameters)
{
try
{
dynamic j = await ExternalProject.ExecuteSomething<MyModel>(parameters);
//How i can catch the error from the another class?
...
}
catch(Exception e)
{
//WebException will be caught here
}
}
public async Task<Object> ExecuteSomething<T>() where T : IModel, new()
{
try
{
WebResponse response = await ExternalProject.ExecuteRequestAsync(PostRequest);
}
catch(Exception)
{
throw;
}
}
public static async Task<WebResponse> ExecuteRequestAsync(WebRequest request)
{
try
{
//return await request.GetResponseAsync();
throw new WebException("Test error message");
}
catch(WebException e)
{
throw;
}
}
EDIT: Just a rule of thumb, only catch exceptions when you have something to do with it. If you are catching exceptions just to log it. Don't do it.
Related
I have this situation (method in Repository):
public string Get(string name)
{
string response;
try
{
using (var context = new MyDB())
{
var row = context.TblSomething.FirstOrDefault();
response = row.GetType().GetProperty(name).GetValue(row, null).ToString();
}
return response;
}
catch (SqlException e)
{
throw e;
}
catch (Exception e)
{
throw e;
}
}
When there is content other than the Property in the name field, it throws an exception
The method is called in the Controller
public IActionResult Get(string name)
{
string response;
try
{
response = _module.MyRepository().Get(name);
}
catch (ValidationException e)
{
return BadRequest(new { error = new { message = e.Message, value = e.Value } });
}
return Ok(response);
}
How to make it not return a 500 error to the user but should be BadRequest?
The way to make it return 400 instead of 500 is to actually catch the exception. You already have a catch block that returns BadRequest, so the only assumption that can be made is that ValidationException is not what's being thrown. Catch the actual exception being thrown and you're good.
That said, absolute do not catch an exception merely to throw the same exception. All you're doing is slowing down your app. You should also never catch Exception, unless you're simply trying to generally log all exceptions and then rethrow. If you don't have a specific handler for an exception type, then don't catch it. In other words, remove these lines:
catch (SqlException e)
{
throw e;
}
catch (Exception e)
{
throw e;
}
If you're not going to handle any exceptions as your repo code does, then don't use a try block at all.
It's also worth mentioning that you shouldn't rely on exceptions unless you have to. Throwing exceptions is a drain on performance. In a situation like this, you should simply return null, instead of throwing an exception when there's no matching property. Then, you can do a null check to verify instead of a try/catch.
You could create your own Exception Handling Middleware to catch 500 error and return your custom error status code and message.
1.Create the middleware:
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
public ExceptionHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context )
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
HttpStatusCode httpStatusCode = HttpStatusCode.InternalServerError;
string message = "Something is wrong!";
httpStatusCode = HttpStatusCode.BadRequest; // Or whatever status code you want to return
message = exception.Message; // Or whatever message you want to return
string result = JsonConvert.SerializeObject(new
{
error = message,
});
context.Response.StatusCode = (int)httpStatusCode;
context.Response.ContentType = "application/json";
return context.Response.WriteAsync(result);
}
}
2.Add it into the middleware pipeline after app.UseDeveloperExceptionPage();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMiddleware(typeof(ExceptionHandlingMiddleware));
}
I want to catch custom exceptions and return a custom object containing information about the exception. For this I have created a custom CustomInvalidException that extends Extension:
[Serializable]
public class CustomInvalidException<T> : Exception
where T : Entity
{
public string Title { get; set; }
public HttpStatusCode StatusCode { get; set; }
public CustomInvalidException(string message)
: base($"{typeof(T).Name}: {message}")
{
this.Title = "Custom invalid exception";
this.StatusCode = HttpStatusCode.BadRequest;
}
}
At some point I can throw this exception:
if (something) throw new CustomInvalidException<CustomEntity>(message);
And I can catch this exception and handle it in some middleware I use for this:
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
// Here I want to create a response with error information
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)exception.StatusCode;
var result = new {exception.Title, exception.StatusCode, exception.Message}
return context.Response.WriteAsync(JsonConvert.SerializeObject(result));
}
This gives errors since I try to get exception.Title and exception.StatusCode, and these elements do not exist on the Exception class. In debug mode I can see that these elements are present since I thrown the CustomInvalidException.
I want to keep the HandleExceptionAsync method as generic as possible, but I can only get these values by explicitly casting to CustomInvalidException (which is not what I want). How can I achieve the result as generic as possible?
EDIT
HandleExceptionAsync is called in the ErrorHandlingMiddleware class:
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context /* other dependencies */)
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
// Handle exception
}
}
Which in turn is called in the Configure of Startup.cs:
app.UseMiddleware(typeof(ErrorHandlingMiddleware));
I have a windows service that references the following code. My code that uses the below method contains a try..catch block but it doesn't seem to catch RefereshTokenException that is thrown in the below method. Obviously my understanding of async is not correct.
private async void RefreshTokens()
{
try
{
var cognito = new CognitoApi();
var response = cognito.TokenRefresh(_refreshToken);
if (response.HttpStatusCode == HttpStatusCode.OK)
{
_idToken = new AwsToken(response.AuthenticationResult.IdToken);
_accessToken = new AwsToken(response.AuthenticationResult.AccessToken);
}
else
{
await _signIn(_credentials.SiteId, _credentials.LocationId, null);
}
}
catch (NotAuthorizedException)
{
await _signIn(_credentials.SiteId, _credentials.LocationId, null);
}
catch (Exception ex)
{
throw new RefreshTokenException("Failed refreshing tokens.", ex);
}
}
This is the code that calls RefreshTokens
public async void Process(QueueMessage queueMessage, Action<QueueMessage> retryAction)
{
_processingCounter.Increment();
try
{
......
IAwsToken idToken = authenticationService.Tokens.IdToken; //This is the code that calls "RefreshTokens" method
........
}
catch (Exception ex)
{
//Code never reaches here...
_logger.Error("Error in ProcessMessage", ex);
}
_processingCounter.Decrement();
}
This is an async void. One of the main reasons to avoid async void methods is that you cannot handle the exceptions they throw.
Make it an async Task and await it in the caller.
Note that you then have the same issue in that caller, async void Process(...)
Make that an async Task as well and work your way up. async/await should form a chain, from your GUI or Controller down to an async I/O call.
I have written a very simple WebApiClient extending HttpClient. The code is following. The main reason to do that was to throw MyOwnWebApiException when httpResponse.IsSuccessStatusCode is false.
public class WebApiClient : HttpClient
{
public WebApiClient(string apiBaseUrl)
{
this.BaseAddress = new Uri(apiBaseUrl);
this.DefaultRequestHeaders.Accept.Clear();
}
public void AddAcceptHeaders(MediaTypeWithQualityHeaderValue header)
{
this.DefaultRequestHeaders.Accept.Add(header);
}
public async Task<string> DoPost(string endPoint, Object dataToPost)
{
HttpResponseMessage httpResponse = await ((HttpClient)this).PostAsJsonAsync(endPoint, dataToPost);
if (httpResponse.IsSuccessStatusCode)
{
string rawResponse = await httpResponse.Content.ReadAsStringAsync();
return rawResponse;
}
else
{
string rawException = await httpResponse.Content.ReadAsStringAsync();
MyOwnWebApiErrorResponse exception =
JsonConvert.DeserializeObject<MyOwnApiErrorResponse>(rawException, GetJsonSerializerSettings());
throw new MyOwnWebApiException (exception.StatusCode,exception.Message,exception.DeveloperMessage,exception.HelpLink);
}
}
#region "Private Methods"
private static JsonSerializerSettings GetJsonSerializerSettings()
{
// Serializer Settings
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
ObjectCreationHandling = ObjectCreationHandling.Auto
};
return settings;
}
#endregion
Following is the code of the class using WebApiClient.
class TestWebApiClient
{
private WebApiClient _client;
public ComputationProcessesWebApiClient()
{
_client = new WebApiClient("http://test.api/");
_client.AddAcceptHeaders(new MediaTypeWithQualityHeaderValue("application/json"));
}
public void GetData(string dataFor)
{
try
{
DataRequest request = new DataRequest();
request.dataFor = dataFor;
**// THIS LINE IS THROWING AGGREGATEEXCEPTION--- **I WANT MyOwnException ****
string response = _client.DoPost("GetData", request).Result; // Use the End Point here ....
}
catch (MyOwnWebApiException exception)
{
//Handle exception here
}
}
}
Question
In the TestWebApiClient class, i dont want to catch AggregateException, rather i want to keep it more elegent and catch MyOwnWebApiException, but the problem is the line ** _client.DoPost("GetData", request).Result** throws an AggregateException if something goes wrong from the WebApi. How to change the code so that from TestWebApiClient i only have to catch MyOwnException ??
This is as a result of synchronously waiting for your task. If you stay async and await your task instead, you'll find that your actual Exception is the one that is caught.
Compare the following below:
void Main()
{
TryCatch();
TryCatchAsync();
}
void TryCatch()
{
try
{
ThrowAnError().Wait();
}
catch(Exception ex)
{
//AggregateException
Console.WriteLine(ex);
}
}
async Task TryCatchAsync()
{
try
{
await ThrowAnError();
}
catch(Exception ex)
{
//MyException
Console.WriteLine(ex);
}
}
async Task ThrowAnError()
{
await Task.Yield();
throw new MyException();
}
public class MyException:Exception{};
Top hint for async/await? It's async/await all the way down. The moment you .Wait() or .Result on a Task, things start to get messy.
I've created an exception filter for my Web API controller actions, but it doesn't seem to do anything (even though it does get invoked).
Attribute
public class ExceptionHandlerAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
context.Response = new HttpResponseMessage(HttpStatusCode.BadRequest);
context.Response.Content = new StringContent("My content");
context.Response.ReasonPhrase = "My reason";
}
}
I've also tried:
public override void OnException(HttpActionExecutedContext context)
{
throw new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent("The content"),
ReasonPhrase = "The reason"
});
}
Controller
[ExceptionHandler]
public class MyController : ApiController
{
[Route("MyRoute"), HttpGet]
public MyModel Index() {
// code causing exception
}
}
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new ExceptionHandlerAttribute());
}
}
However, when an exception occurs the client receives this:
You need to throw an HttpResponseException with the response from your exception filter:
public override void OnException(HttpActionExecutedContext context)
{
throw new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent("The content"),
ReasonPhrase = "The reason"
});
}
Here's more details on how to handle exceptions in Web API.
your API side codes are ok. but getting the response should be like this (using WebException catching):
string getResponse(WebRequest request, out exceptionOccured)
{
exceptionOccured = false;
try
{
HttpWebResponse resp = (HttpWebResponse)request.GetResponse();
var stream = resp.GetResponseStream();
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
catch (WebException ex)
{
exceptionOccured = true;
using (var stream = ex.Response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
catch (Exception ex)
{
exceptionOccured = true;
// Something more serious happened
// like for example you don't have network access
// we cannot talk about a server exception here as
// the server probably was never reached
return ex.Message;
}
}