I use the following code to call a web method and get some info. To tidy things up a bit and get some code reuse, I introduced the InvokeWebMethod routine.
private T InvokeWebMethod<T>(webServiceDelegate d)
{
return (T)base.invokeWebMethod(d);
}
internal XmlElement GetInfo(string url)
{
return this.InvokeWebMethod<XmlElement>(() => { return this.myService.GetInfo(url); });
}
If the web method does not exist on the server, a 404 Exception is raised inside the delegate.
The bit that I don't understand, is that the Lambda function ignores the 404 exception and instead raises an XmlElement Cast exception.
Can any one explain to me why the 404 Exception isn't raised up the stack until it is handled?
Thanks
You gotta check the InnerException property of the exception you are getting, chances are the 404 exception is somewhere down the stack.
Related
I would like to be able to access properties on the original exception that is thrown from a Consumer inside a Fault Consumer. For example, if the unhandled exception is a ValidationException with a collection of Errors, am I able to access that collection from a Fault Consumer?
The only thing I seem to have access to is the ExceptionType and the Message. I suppose I could parse the exception message to get the Errors collection, but is there a way to achieve this without parsing the message and generating the collection?
public async Task Consume(ConsumeContext<Fault<MyMessage>> context)
{
string exceptionType = context.Message.Exceptions[0].ExceptionType;
string exceptionMessage = context.Message.Exceptions[0].Message;
if (exceptionType == "FluentValidation.ValidationException")
{
// here I want to get the Errors collection on the exception of type ValidationException
}
}
MassTransit does not serialize Exception, it encapsulates the exception details in an ExceptionInfo type that is included with the Fault event.
There is no access to the original Exception type, and for good reason. Serializing exceptions as part of a message contract is just bad practice, in my opinion.
I have written an HttpModule for ASP.NET which will inspect the current exception (if any) and wrap it in a new exception with a unique identifier. Then I want Elmah to log that wrapping exception.
My module is working and is sitting in front of the Elmah module, however I can't work out how to change the Server.GetLastError() so that my new exception will be logged!
I have tried:
var originalException = context.Server.GetLastError();
var app = (HttpApplication)sender;
var context = app.Context;
context.ClearError();
context.AddError(new WrapperException(originalException));
But doing so makes the context.Error property return null.
In fact ClearError doesn't do what it's advertised to do: "Clears all errors for the current HTTP request."
It doesn't do this. The AllErrors array still contains exceptions; all it does make GetLastError return null.
You cannot set context.AllErrors, nor poke something into the array (it's a copy).
You also cannot throw a new exception: the error page only sees the original exception, and Elmah doesn't even log it.
I'm beginning to think that it's not possible.
If the only reason you want to replace Server.GetLastError() is to make sure that ELMAH logs the right exceptions, there may be a better approach. ELMAH's filtering feature can be used to override the logged exception. To do so, add the following code to your Global.asax.cs file:
void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs args)
{
var httpContext = args.Context as HttpContext;
ErrorLog.GetDefault(httpContext).Log(new Error(new WrapperException(args.Exception)));
args.Dismiss();
}
The ErrorLog_Filtering method is called by ELMAH just before logging any uncaught exceptions to the configured error log. In the example, I pull the information about the error happening (args.Context and args.Exception) and wrap the thrown exception in a new exception (WrapperException). By logging the new exception using the ErrorLog.GetDefault(...).Log(...) method, I make sure that ELMAH doesn't call the ErrorLog_Filtering method recursively (it would if you used ErrorSignal...Raise()). Finally, I dismiss the original exception, to avoid the error being logged twice.
Is there anyway to capture handled exceptions when CreateErrorResponse() is used to return Error response with API methods?
I currently have registered a simple global exception loggger for any exceptions, however for some api responses including Model Filter attribute, I'm CreateErrorResponse() to return error responses but this doesn't to enter the Log() method in the Global Exception Logger.
Example usage of CreateErrorResponse in an API method :
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
Global Exception Logger:
public class GlobalExceptionLogger : ExceptionLogger
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public override void Log(ExceptionLoggerContext context)
{
log.Error(context.Exception);
}
}
The only way to get Log() method to be called is rather than use CreateErrorResponse, throw an Exception instead.
Is this correct?
You should log the error and any trace info in the same negative flow where you are constructing and returning the Error Response.
Using exceptions to control application flow is bad practice, avoid it.
The global handlers use is to handle anything that you haven't already handled and should be for Very exceptional cases, you also log there and present the user with a generic error message, while you sort out the chaos in the back of course :)
If I misunderstood your question please correct me.
I was trying to explain to a colleague why async void functions are bad and that exceptions won't get caught, but it turns out I might not be understanding them right. We have a piece of code that looks a bit like this:
public ActionResult EmailCandidates(List<string> identityTokens, string subject, string content)
{
// generate list of recipients here
SendEmail(recipients, subject, content); //not awaited
return new AjaxResponse { // AjaxResponse is a wrapper around JSONResponse
IsSuccess = true,
Data = recipients.Select(r=>r.Name)
};
}
private async void SendEmail(List<EmailAddress> recipients, string subject, string content)
{
await Task.Delay(10000); // simulate async send email
throw new Exception(); // manually added
}
What I was expecting, and what I was trying to explain is that if the SendEmail function throws an exception it won't be caught properly because the main function EmailCandidates has already returned to the client. Only that's not what happens. The code above executes in exactly the order I expect:
a call is made from the client to EmailCandidates
SendEmail is called
the email is sent asynchronously (simulated here via an async wait)
control returns to EmailCandidates, and the return is executed
and then it gets kind of weird:
At this point, I expected to get a response to the client but I don't, even though EmailCandidates has returned
10 seconds later the exception is thrown
the exception is caught by the global error handler, and now the client receives a 500 error (unsurprisingly)
So why even though EmailCandidates has returned, does the response not get sent to the client. How does is know to wait for the async SendEmail function?
ASP.NET provides a SynchronizationContext that does keep track of the number of asynchronous operations in flight, and will not send a result until they have all completed. Note that this SynchronizationContext has been removed in ASP.NET Core.
However, you shouldn't be seeing this behavior even on ASP.NET. In the case of a synchronous method calling an async void method, you should see an InvalidOperationException with the message "An asynchronous operation cannot be started at this time.". In the case of an asynchronous method calling an async void method (that doesn't complete before the handler returns), you should see an InvalidOperationException with the message "An asynchronous module or handler completed while an asynchronous operation was still pending."
Since neither of these safety nets are triggering, I suspect that your ASP.NET code is using an old version of ASP.NET. Those safety nets were added in .NET 4.5, which you have to not only have as a build target but you also have to add targetFramework in your web.config.
A new .NET 4.5.2 ASP.NET MVC app with the following code immediately throws an InvalidOperationException, as expected:
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
Test();
return View();
}
private async void Test()
{
await Task.Delay(20000);
throw new Exception("Blah");
}
Your application is working fine, actually is following the default behavior that MVC has. If you put explicitly the exception, then this kind of errors(500) arise when the request is originated from the same machine where the application is on(localhost) if you want to see what the actual user want to see you need to change the value on the webconfig for the By default it is set to RemoteOnly.
If you go to your FilterConfig. You will see this line of code
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
Try to change the value to "On" and you will see that you ended on that error page because the handler error attribute, it is providing post processing logic on an action and when it sees that an exception has escaped from an action it will display an error view instead of the yellow screen of death. The error View is on your application by default inside View/Shared/Error.cshtml
Your Off option
Specifies that custom errors are disabled. This allows display of
detailed errors.
for reference go here: https://msdn.microsoft.com/en-us/library/h0hfz6fc(v=vs.71).aspx
If you put remote only then you will continue seeing the error if you are debugging the website in your localmachine, but if you host the application the final user will not see that error.
Async void methods are kinda different beasts from the "normal" async methods.They have different error handling logic. When an exception is thrown out of an async Task or async Task<T> method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown by an async void method will be raised directly on the SynchronizationContext that was active when the async void method has been called. These exceptions can be observed using UnhandledException event handler.
I've a few web methods that I use to call some external services like the Google Calendar API, obviously these can be extremely brittle.
Unfortunately I now realise that any error thrown on these methods are not causing an exception to bubble up to Global.asax which is where errors are getting logged in this application.
I have seen suggestions to wrap the method in a try/catch, which is a stupid way of doing it as there are a variety of errors that ASP.Net will silently swallow still.
In trying to find a solution I've seen a lot of references to SoapExtension, which is exactly what I want to do but doesn't get fired as I'm returning Json. What I really want is a way to catch the error just like that.
Any pointers appreciated, I still can't understand how the ASP.Net team could have thought that silently swallowing errors like this was a bright idea.
So for example a method like this:
[WebMethod]
[ExceptionHandling] //can I write a handler like this to catch exceptions from JSON webservices?
static public void DeleteItem(string id)
{
var api = new GoogleCalendarAPI(User.InternalUser());
api.DeleteEvent(id);
return "success";
}
There is no equivalent to SoapExtension for JSON WebMethods and having custom errors turned on in your production site will result in a generic error message being returned to the client, no error is ever raised on the server. You cannot circumvent this.
If you inspect the code using something like ILSpy, there is no way to pass a method or class to page WebMethods like SoapExtension. The error is swallowed by ASP.Net as it invokes the web method, the only notification you will get is a HTTP 500 error sent to the client with a total generic error message.
In 4.0, WebMethods get called by this:
// System.Web.Script.Services.RestHandler
internal static void ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)
{
try
{
//snip irrelevant code
RestHandler.InvokeMethod(context, methodData, rawParams);
}
catch (Exception ex)
{
RestHandler.WriteExceptionJsonString(context, ex);
}
}
So if invoking your method throws an error it will call the following code with a statusCode of 500, there's no re-throw in there and nothing else you can pass in called so unless I'm being blind it just gets swallowed silently. Even worse if you've got custom errors turned on, which any sane person will, it'll completely obfuscate the original cause:
// System.Web.Script.Services.RestHandler
internal static void WriteExceptionJsonString(HttpContext context, Exception ex, int statusCode)
{
//snip code setting up response
context.Response.TrySkipIisCustomErrors = true;
using (StreamWriter streamWriter = new StreamWriter(context.Response.OutputStream, new UTF8Encoding(false)))
{
if (ex is TargetInvocationException)
{
ex = ex.InnerException;
}
if (context.IsCustomErrorEnabled)
{
streamWriter.Write(JavaScriptSerializer.SerializeInternal(RestHandler.BuildWebServiceError(AtlasWeb.WebService_Error, string.Empty, string.Empty)));
}
else
{
streamWriter.Write(JavaScriptSerializer.SerializeInternal(RestHandler.BuildWebServiceError(ex.Message, ex.StackTrace, ex.GetType().FullName)));
}
streamWriter.Flush();
}
}
I can't see a way around it, looks like WebMethod is not ready for production code, shame.
It's not so much they get disappeared, it's more that they get passed out to the calling client. Since however you don't always want to (or should) reveal such intimate details of your service, you can prevent errors bubbling out of your service. This gives the impression of them disappearing.
Wrapping the inner detail in a try-catch is about the best way to cope with any errors. Within the method you're dealing with standard error trapping. So I think you'd want something like:
[WebMethod]
static public string DeleteItem(string id)
{
try
{
var api = new GoogleCalendarAPI(User.InternalUser());
api.DeleteEvent(id);
return "success";
}
catch(Exception ex)
{
log.fatal(ex);
return "error";
}
}
If anything throws an exception within the try-catch it'll be caught. ASP.Net won't interfere with it, unless the methods you are calling have been specifically coded to do so.
Edit
If the GoogleCalendarAPI class is in turn calling a method, such as ExecuteWebServiceCall with catches the Exception, then you'd have to parse the response. I'd hope they gave you some other clue, like a response code, to indicate an error state. You could then wrap that in an Exception, throw it have it caught by your default error handler.