C# cannot catch Exception from within anonymous Action - c#

I have an application in which I execute requests on a seperate thread while a waiting form is displayed. This is done using a Request class which executes an Action object.
Now of course i have some error-handling logic, which seems to catch errors twice, and i cannot figure why.
This is the code inside the Request class:
public virtual Tuple<string, string> ExecuteInternal()
{
ReturnValue<string> rv = new ReturnValue<string>();
try
{
Executor.Invoke(rv);
Response = rv.Value;
return new Tuple<string, string>("Success", Response);
}
catch (Exception ex)
{
//-----------this gets triggered and correctly returns the "Failure" Tuple
return new Tuple<string, string>("Failure", ex.ToString());
}
}
This is the code responsible for executing the requests one after another:
new Thread(() =>
{
foreach (Request request in Requests)
{
string response = "";
try
{
Tuple<string, string> result = request.ExecuteInternal();
string status = result.Item1;
response = result.Item2;
UpdateStatus(request, status);
}
catch (Exception ex)
{
//-------------------somehow this part gets also triggered
request.Response = ex.ToString() + "\n\n\n" + response;
UpdateStatus(request, "Failure2");
}
}
}).Start();
Now if i have an Exception inside the "Executor" which is an Action, eg.
Executor = () => { throw new Exception(); };
, the error handler inside the Request class returns a "Failure" tuple, so the exception handler works. BUT the exception handler inside the Thread also catches the same Exception somehow, and i cannot figure out why.

I fixed it, the "UpdateStatus" method was causing some issues.
It is supposed to call another Action inside the Request responsible for error handling in a user-friendly way, but due to a typo that variable was not getting initialized. It works properly now :)
I debugged everything line by line and traced it down that way. The exception was rethrown, since the error handler was not initialized as said above.
(This is a quite big project i 'inherited' and am still getting used to...)

Related

How to handle exceptions from API calls in Unity and pass them down to the UI?

I'm trying to figure out what to do with some errors in Unity when I call an API and how to propogate it up to the user interface - where and how to handle things. I've built aspnet APIs but there I'd normally use some error handling middleware to keep my controllers clean.
Let say we have some code like this (I'm using controller / repository language cos that's what I know).
A UI button fires an event like OnLoginButtonPressed.
An AuthController class reacts to the event by calling it's login method and then doing some logic when the response comes through, as follows:
public async void Login(LoginModel input)
{
var result = await AuthRepo.instance.Login(input);
app.token = result;
EventService.OnSuccessfulLogin();
}
The Auth.Repo calls the API and tries to return a Token class (just a wrapper around a JWT string)
public async Task<Token> Login(LoginModel input)
{
string json = JsonConvert.SerializeObject(input);
var request = UnityWebRequest.Post(app.baseURL + "authentication/login", json);
request.SetRequestHeader("Content-Type", "application/json");
request.SendWebRequest();
while (!request.isDone)
{
await Task.Yield();
}
if (request.result == UnityWebRequest.Result.Success)
{
Token token = JsonConvert.DeserializeObject<Token>(request.downloadHandler.text);
return token;
}
else
{
throw
}
}
So that's without exception handling. So I want to try to let the user know if there is a connection error, or they have put invalid details etc... I'm guessing I'm supposed add some logic into the AuthRepo such as:
if (request.result == UnityWebRequest.Result.Success)
{
Token token = JsonConvert.DeserializeObject<Token>(request.downloadHandler.text);
return token;
}
else if (request.result== UnityWebRequest.Result.ConnectionError)
{
throw new ConnectionException(request.error);
}
else if (request.result == UnityWebRequest.Result.DataProcessingError)
{
throw new BadRequestException(request.error);
}
else
{
throw new System.Exception(request.error);
}
This seems like a lot of code, and would end up in every method in every repo (unless I pull it out into some helper method?).... anyway, and then in the controller I would do something like:
try {
var result = await AuthRepo.instance.Login(input);
app.token = result;
EventService.OnSuccessfulLogin();
}
catch (ConnectionException ex)
{
EventService.OnConnectionError(ex.Message);
//some UI object would listen for this event and show the connection error message.
}
catch (BadRequestException ex)
{
EventService.LoginFailedError(ex.Message);
}
finally
{
EventService.UnknownError(ex.Message);
}
Is this completely down the wrong path? Seems like the code is just gonna get swamped with exception handling, or is this the correct way?
I've worked through a few YouTube videos that seem to suggest this is right, but they don't really show my use case (talking to APIs) so I'm just trying to be sure.
because UnityWebRequest.Result is an enum, you can start by using a switch statement here. Not only is this cleaner, it performs better too.
Another thing you can do is create an abstract class (e.g. APIException) and make that responsible for creating the correct exception instances, by giving it some static method like APIException FromUWRResult(UnityWebRequest.Result result).
Handling the exceptions can be done in APIException too. Give it an abstract method Handle() and implement accordingly in each of the deriving classes.
Now your code would look like this:
var ex = APIException.FromUWRResult(request.result);
if(ex != null) {
throw ex;
}
...
catch(APIException ex) {
ex.Handle();
}

How to Verify if a Method Is Invoked for a Class That Throws an Exception

I have the following test method. What I want to achieve in this test is to verify that the Error method was invoked by method SendSMSAsync() when it received an exception.
[TestMethod]
public async Task SendSMSAsync_PostAsyncRequestResultedInAnException_LogExceptionAsAnError
{
_client.Setup(clnt => clnt.PostAsync("uri", It.IsAny<HttpContent>()))
.ThrowsAsync(new Exception("Exception!"));
var service = new SMSService();
_ = service.SendSMSAsync("mobile_number");
_logger.Verify(lgr => lgr.Error(exception => exception.Message.Contains("Exception!")))
}
This is the implementation of the service.
public async Task<bool> SendSMSAsync(string mobileNumber)
{
try
{
...
// Set up PostAsync to throw an exception.
using (var response = _client.PostAsync("uri", content))
{
...
}
}
catch(Exception exception)
{
_logger.Error(exception);
throw new Exception("An error has occurred while sending an SMS.");
}
}
If I run this, my test fails saying that the Test method threw an exception.. Is there a way for me to verify if a method has been invoked inside the catch statement?
You could catch the exception as part of the test:
try
{
_ = service.SendSMSAsync("mobile_number");
}
catch
{
_logger.Verify(lgr => lgr.Error(exception => exception.Message.Contains("Exception!")));
}
To protect against false positives you might also return from the catch and intentionally fail the test after the catch. Something like:
try
{
_ = service.SendSMSAsync("mobile_number");
}
catch
{
_logger.Verify(lgr => lgr.Error(exception => exception.Message.Contains("Exception!")));
return;
}
throw new Exception("Test failed!");
The idea here is so the test doesn't "pass" if the method doesn't throw an exception in the first place.
Note that there may be tooling within the test framework to more gracefully fail a test, I don't recall off-hand. But the idea is generally the same.
Side note: Shouldn't there be an await on that method call?

Exception not caught when thrown by RESTSharp

I'm using RestSharp to communicate with a REST-Server and wrote a small wrapper function for the call
private T Get<T>(string restAdress) where T : new()
{
try
{
// throw new Exception(); // This one is caught
IRestClient restClient = new RestClient(settings.Value.HostToConnect).UseSerializer(new JsonNetSerializer()); // (1)
RestRequest restRequest = new RestRequest(restAdress);
IRestResponse<T> result = restClient.Get<T>(restRequest); // (2)
return result.Data;
}
catch (Exception e) // debugger wont stop here
{
// debugger wont stop here too
// code within this block is not executed
}
return null; // (3)
}
Since I want to use the Newtonsoft-Attributes I give in a custom (de)serializer (1).
public class JsonNetSerializer : IRestSerializer
{
public string Serialize(object obj) =>
JsonConvert.SerializeObject(obj);
public string Serialize(RestSharp.Parameter bodyParameter) =>
JsonConvert.SerializeObject(bodyParameter.Value);
public T Deserialize<T>(IRestResponse response) =>
JsonConvert.DeserializeObject<T>(response.Content); // (4)
public string[] SupportedContentTypes { get; } =
{
"application/json", "text/json", "text/x-json", "text/javascript", "*+json"
};
public string ContentType { get; set; } = "application/json";
public DataFormat DataFormat { get; } = DataFormat.Json;
}
When calling the REST service and trying to get the result (2) an Exception is thrown if the deserialization fails (4). But the Exception is not caught by the try-catch block. I tried to debug but after throwing the debugger goes on in line (3), the catch and the logging withing the catch is never executed. The debugger won't even stop at the catch (Exception e) it goes straight from (4) to (3).
(Sorry for not english, the title of the window says "Exception User-Unhandled")
Can anyone explain this behaviour to me?
Whats happening here is an interesting setting of the debugger, another example would be this closed bug report. When the debugger reaches the point the exception is thrown it will break, causing the behaviour you experienced.
If you uncheck the exception setting "Break when this exception type is user-unhandled" in the dialog, you should be able to reach your catch block, as the debugger no longer breaks the execution as soon as the specified exception is thrown.
In your case you find that option under "Ausnahmeeinstellungen".
I am maintaining RestSharp, so I hope I can answer this.
RestSharp doesn't throw on deserialisation by default. If you look at the code, deserialisation happens in the private IRestResponse<T> Deserialize<T>(IRestRequest request, IRestResponse raw) method of RestClient.
When RestSharp can't deserialise the response, it creates an error response. You get the response status set to Error and the exception is put into the ErrorException property of the response object, along with the exception message that gets to the ErrorMessage of the response.
It is still possible to tell RestSharp to throw on deserialisation if you assign the FailOnDeserialization property of the RestClient instance to true.

System.Threading.ThreadAbortException on generic redirection

I am working on an ASP.Net project where we have an centralized redirection method. But some times it throws an exception:
System.Threading.ThreadAbortException
The main problem is that often the code execution is not stopping after calling SBA.Redirect("AnotherPage.aspx") and the following code is still executing.
My generic function:
public static class SBA
{
public static void Redirect(string Url)
{
try
{
HttpContext.Current.Response.Redirect(Url, false);
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
catch (Exception ex)
{
if (ex.GetType() != typeof(System.Threading.ThreadAbortException))
{
throw;
}
}
}
}
Redirect raises a ThreadAbortException specifically in order to stop any following code from being run.
You are handling the ThreadAbortException.
Thus the following code is being run.
If you don't want the following code to be run, don't handle the ThreadAbortException.
Simply make the following call to make a redirect:
HttpContext.Current.Response.Redirect(Url);
There are two problems with your code:
You use an overload of Redirect where you decide to not end the response by supplying false for the endResponse parameter. Hence the code after the redirect executes.
You try to catch ThreadAbortException. When using the normal redirect as described above this exception is thrown. It is not an error condition but simply a way for ASP.NET to ensure proper termination of the current request. You can catch the exception but it is rethrown at the end of the catch block so your catch block will not do anything useful.
Because an exception is thrown when redirecting you should be aware of the following explained in the comment:
void HandleRequest() {
try {
Response.Redirect(" ... url ... ");
}
catch (Exception) {
// Code here will execute after the redirect.
}
}
To avoid problems the best thing is to catch a more specific exception type in the catch handler or at least not do anything in the handler that interferes with the redirect (like writing to the response stream).
I protected the redirection using the code below. It's working.
public static class SBA
{
public static void Redirect(string Url)
{
try
{
//redirect only when 'IsRequestBeingRedirected' is false
if (!HttpContext.Current.Response.IsRequestBeingRedirected)
{
Uri uri = null;
bool isUriValid = Uri.TryCreate(Url, UriKind.RelativeOrAbsolute, out uri);
if (!isUriValid)
{
throw new SecurityException("Invalid uri " + Url);
}
//Below check is not required but checked
//to make obsolate security check
if (uri.OriginalString == null)
{
throw new SecurityException("Invalid uri " + Url);
}
// check if host is from configured trusted host list
if (uri.IsAbsoluteUri)
{
var tempAppSetting = ConfigBLL.GetAppSetting(AppSettingSectionType.OtherSetting).Other;
if (!tempAppSetting.RedirectTrustedUrls.Contains(uri.Host))
{
throw new SecurityException("Untrusted url redirection detected. Can not redirect.");
}
}
var tempUrl = uri.OriginalString;
//Few more logical check
HttpContext.Current.Response.Redirect(tempUrl, true);
}
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
catch (Exception ex)
{
if (ex.GetType() != typeof(System.Threading.ThreadAbortException))
{
throw;
}
}
}
}

How do I determine what is the issue with a 500 error on a HttpClient.PostAsJsonAsync call?

I am making a call to a Web API that I wrote. I am working through the bugs on both sides and am getting a 500 error. I want to see that is in that error message to see what might be the problem. How do I find that?
using (var client = new HttpClient())
{
var fooURL = Url.RouteUrl("PayrollApi", new { httproute = string.Empty, controller = "LeaveRequest" }, Request.Url.Scheme);
var repsonse = client.PostAsJsonAsync(fooURL, leaveRequest).Result;
}
I don't see where that text might be stored so I can figure out what other bugs need to be fixed. How do I get that text?
Update: something I didn't clarify. I put a breakpoint on the WebAPI I am calling and the break point is never hit. However, when I call it from Poster, I hit the break point. The URL is correct.
public HttpResponseMessage Post([FromBody]LeaveRequest value)
{
if (ModelState.IsValid)
{
// code here
}
}
I was able to make a change to get the error message:
var repsonse = client.PostAsJsonAsync(fooURL, leaveRequest).Result.Content.ReadAsStringAsync();
instead of
var repsonse = client.PostAsJsonAsync(fooURL, leaveRequest).Result;
I was then able to see my error and fix it.
I am making a call to a Web API that I wrote. I want to see that is
in that error message to see what might be the problem.
You might want to put the code you have in PayrollApi.LeaveRequest in a try-catch block, something like:
public HttpResponseMessage LeaveRequest()
{
try {
// do something here
}
catch(Exception ex) {
// this try-catch block can be temporary
// just to catch that 500-error (or any unexpected error)
// you would not normally have this code-block
var badResponse = request.CreateResponse(HttpStatusCode.BadRequest);
badResponse.ReasonPhrase = "include ex.StackTrace here just for debugging";
}
}
Then make a call inside a try-catch block to capture that extra error:
using (var client = new HttpClient())
{
// rest of your code goes here
try
{
var repsonse = client.PostAsJsonAsync(fooURL, leaveRequest).Result;
}
catch(HttpRequestException httpEx)
{
// determine error here by inspecting httpEx.Message
}
}
httpEx.Message can have this message:
Response status code does not indicate success: 500
({stack_trace_goes_here}).

Categories