I'm testing centralized exception handling in my ASPNetCore application and wanted to see if an unexpected exception is thrown it is going to be correctly handled by app.UseExceptionHandler() middleware and logged to a specific logging target. I disconnected the SQL database expecting to see Database.EnsureCreated() method in the DbContext class constructor throwing System.Data.SqlClient.SqlException. The problem is that it does throw such an exception, but it appears locally instead of being handled by centrelized error handler. The final result is that a client never gets a response message explaining what happened with the status code 500.
It seems to be strange as the handler works correctly with an exception which I throw inside controllers.
Here is my centralized exception handler configuration:
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
var errorFeature = context.Features.Get<IExceptionHandlerFeature>();
if (errorFeature != null)
{
var exception = errorFeature.Error;
logger.LogError(exception.ToString());
await context.Response.WriteAsync("An unexpected error occurred! Try again later");
}
});
});
Could anyone tell me give me a hint on what I might be doing wrong? Has anyone come across a similar problem?
Your code looks fine to me. What you are experiencing is a First-Chance-Exception. This means that an exception has been thrown that might eventually get handled. During runtime your ExceptionHandler should perfectly handle your exception.
At debugging time however, Visual Studio breaks for that exception. The behaviour of Visual Studio can be configured in the Exception Settings (Debug > Windows > Exception Settings). See Microsoft Docs for more information on that.
So what you basically have to do is to tell Visual Studio to continue debugging on a SqlException
You can use own middleware to handle exception as first-person
public class ExceptionFilter: IExceptionFilter
{
public void OnException(ExceptionContext context)
{
String message = String.Empty;
Type exceptionType = context.Exception.GetType();
if (exceptionType == typeof(NotImplementedException))
{
message = "A server error occurred.";
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.NotImplemented;
context.Result = new RedirectResult("/Home/Index");
}
else if (exceptionType == typeof(AppException))
{
message = context.Exception.ToString();
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Result = new RedirectResult("/Home/Index");
}
//HttpResponse response = context.HttpContext.Response;
//response.StatusCode = (int)status;
//context.Result = new RedirectResult("/Home/Index");
}
}
And in your Startup.cs
app.UseMiddleware(typeof(ExceptionFilter));
Related
I use HttpAysnClient to do http requests, and I found when I throw an exception in the failed callback, the next request always be failed, how to fix it?
I use maven dependency: 'org.apache.httpcomponents:httpasyncclient:4.1.5'.
my java test code:
CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
try {
httpclient.start();
AtomicBoolean fireException = new AtomicBoolean(false);
while (true) {
try {
String url;
if (fireException.compareAndSet(false, true)) {
url = "http://localhost:8080"; // throw Connection refused
} else {
url = "http://www.apache.org/";
}
final HttpGet request2 = new HttpGet(url);
httpclient.execute(request2, new FutureCallback<HttpResponse>() {
public void completed(final HttpResponse response2) {
System.out.println("completed, " + request2.getRequestLine() + "->" + response2.getStatusLine());
}
public void failed(final Exception ex) {
System.out.println("failed, " + request2.getRequestLine() + "->" + ex);
throw new RuntimeException();
}
public void cancelled() {
System.out.println(request2.getRequestLine() + " cancelled");
}
});
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
TimeUnit.SECONDS.sleep(1);
}
}
} finally {
httpclient.close();
}
exception in the next requests: java.util.concurrent.CancellationException: Request execution cancelled
I can confirm same behavior with version 4.1.5.
I must confess it is quite surprising to see an application uncontrolled exception shutting down the whole client unexpectedly. In the context of an application reusing same client instance in multiple places, means the application client gets completely unsuable, with catastrophic consequences for the service.
You can use the "isRunning" method to evaluate if the client is under this situation, and potentially try to recreate the client again. But it is definately incovenient to see the client being shutdown like this.
After exercising the client with different conditions (error responses, slow responses...), the only way to reproduce this is to point to an invalid endpoint where no server is running. This is the condition presented in the original example.
I think I found the issue here https://jar-download.com/artifacts/org.apache.httpcomponents/httpasyncclient/4.1.5/source-code/org/apache/http/impl/nio/client/InternalIODispatch.java
You can see onException doesn't have a try/catch block to properly handle exceptions from the application.
I have confirmed this issue is fixed in Httpclient5 5.1.3. So other than fixing your application code to avoid uncontrolled exceptions, the solution is to migrate into the new Httpclient5 lib version.
you can see doc in https://hc.apache.org/httpcomponents-client-5.1.x/migration-guide/migration-to-async-simple.html
and if you want to use CloseableHttpClient you must start it client.start();
I have a WCF service that receives messages from the Microsoft Message Queue (netMsmqBinding).
I want my service to recover if the message queue is unavailable. My code should fail to open the service, but then try again after a delay.
I have code to recognize the error when the queue is unavailable:
static bool ExceptionIsBecauseMsmqNotStarted(TypeInitializationException ex)
{
MsmqException msmqException = ex.InnerException as MsmqException;
return ((msmqException != null) && msmqException.HResult == (unchecked((int)(0xc00e000b))));
}
So this should be straightforward: I call ServiceHost.Open(), catch this exception, wait for a second or two, then repeat until my Open call is successful.
The problem is, if this exception gets thrown once, it continues to be thrown. The message queue might have become available, but my running process is in a bad state and I continue to get the TypeInitializationException until I shut down my process and restart it.
Is there a way around this problem? Can I make WCF forgive the queue and genuinely try to listen to it again?
Here is my service opening code:
public async void Start()
{
try
{
_log.Debug("Starting the data warehouse service");
while(!_cancellationTokenSource.IsCancellationRequested)
{
try
{
_serviceHost = new ServiceHost(_dataWarehouseWriter);
_serviceHost.Open();
return;
}
catch (TypeInitializationException ex)
{
_serviceHost.Abort();
if(!ExceptionIsBecauseMsmqNotStarted(ex))
{
throw;
}
}
await Task.Delay(1000, _cancellationTokenSource.Token);
}
}
catch (Exception ex)
{
_log.Error("Failed to start the service host", ex);
}
}
And here is the stack information. The first time it is thrown the stack trace of the inner exception is:
at System.ServiceModel.Channels.MsmqQueue.GetMsmqInformation(Version& version, Boolean& activeDirectoryEnabled)
at System.ServiceModel.Channels.Msmq..cctor()
And the top entries of the outer exception stack:
at System.ServiceModel.Channels.MsmqChannelListenerBase`1.get_TransportManagerTable()
at System.ServiceModel.Channels.TransportManagerContainer..ctor(TransportChannelListener listener)
Microsoft have made the source code to WCF visible, so now we can work out exactly what's going on.
The bad news: WCF is implemented in such a way that if the initial call to ServiceModel.Start() triggers a queueing error there is no way to recover.
The WCF framework includes an internal class called MsmqQueue. This class has a static constructor. The static constructor invokes GetMsmqInformation, which can throw an exception.
Reading the C# Programming Guide on static constructors:
If a static constructor throws an exception, the runtime will not invoke it a second time, and the type will remain uninitialized for the lifetime of the application domain in which your program is running.
There is a programming lesson here: Don't put exception throwing code in a static constructor!
The obvious solution lies outside of the code. When I create my hosting service, I could add a service dependency on the message queue service. However, I would rather fix this problem with code then configuration.
Another solution is to manually check that the queue is available using non-WCF code.
The method System.Messaging.MessageQueue.Exists returns false if the message queue service is unavailable. Knowing this, the following works:
private const string KNOWN_QUEUE_PATH = #".\Private$\datawarehouse";
private static string GetMessageQueuePath()
{
// We can improve this by extracting the queue path from the configuration file
return KNOWN_QUEUE_PATH;
}
public async void Start()
{
try
{
_log.Debug("Starting the data warehouse service");
string queuePath = GetMessageQueuePath();
while(!_cancellationTokenSource.IsCancellationRequested)
{
if (!(System.Messaging.MessageQueue.Exists(queuePath)))
{
_log.Warn($"Unable to find the queue {queuePath}. Will try again shortly");
await Task.Delay(60000, _cancellationTokenSource.Token);
}
else
{
_serviceHost = new ServiceHost(_dataWarehouseWriter);
_serviceHost.Open();
return;
}
}
}
catch(System.OperationCanceledException)
{
_log.Debug("The service start operation was cancelled");
}
catch (Exception ex)
{
_log.Error("Failed to start the service host", ex);
}
}
Scenario is a Silverlight client using Wcf service & custom authentication. To mitigate the 500/200 status code problem (avoid EndPointNotFound exception) I've applied the SilverLightFaultBehaviour. However, this does not work with UserNamePasswordValidator - When a FaultException is thrown from Validate(), it is not caught by the SilverLightFaultMessageInspector's implementation of BeforeSendReply.
So far, the only workaround I've found is using the alternative client stack instead ( WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);), but there are complications with using it which can no longer be ignored as a lot of our clients are on virtual machines, the silverlight client keeps crashing ( Silverlight 5 - Debugging npctrl.dll crash , http://communities.vmware.com/thread/394306?tstart=0 ).
My primary motivation is that I want to be able to distinguish a failed login from a connection error (the following code is from a client-side async callback method, and only works with the Client stack):
if (e.Error is MessageSecurityException)
{
this.HasLoginFailed.Value = Captions.Login_FailedLogin;
}
else
{
this.HasLoginFailed.Value = Captions.Login_FailedConnection;
}
Is there any other way of modifying the message sent when throwing a FaultException from UserNamePasswordValidator? Or any conceptually different way of doing custom authentication rather than what I am using which enables me to modify the message status or to keep it 200, or just to be able to distinguish a connection failure from bad credentials?
my server-side code for usernamepassword reg:
var serviceCredential = host.Description.Behaviors.Find<ServiceCredentials>();
serviceCredential.UserNameAuthentication.UserNamePasswordValidationMode =
UserNamePasswordValidationMode.Custom;
serviceCredential.UserNameAuthentication.CustomUserNamePasswordValidator =
new MyValidator();
When you throw a FaultException from MyValidator, it is wrapped as the InnerException of a MessageSecurityException, that's probably why you weren't able to catch it directly as a FaultException.
To add some information to the fault you are throwing, what you can do is adding a FaultCode:
throw new FaultException(
"Invalid user name or bad password.",
new FaultCode("BadUserNameOrPassword")
);
Then, catch the exception client-side and retrieve your FaultCode:
try { ... }
catch (MessageSecurityException e)
{
FaultException fault = (FaultException) e.InnerException;
String faultCode = fault.Code.Name;
// you can now display a meaningful error with the faultCode
}
I hope it will help!
I have a web service that runs perfectly when i reference it from within the project solution. As soon as i upload it to the remote server, it starts blowing up. Unfortunately, the only error message I get is on the client side "faultexception was unhandled by user code". Inside of the web service, I have exceptions handled in all of the methods, so I'm pretty sure it's getting caught somewhere, but I don't know how to see it. I suspect that the problem is permissions related, but I can't see where it's happening.
I tried placing an error message into object returns, but it's still not making it out; something like this:
public bool SetDirectReports(ADUser user)
{
try
{
var adEntry = new DirectoryEntry(string.Format("LDAP://<GUID={0}>", user.Guid), "administrator", "S3cur1ty");
if (adEntry.Properties["directReports"].Count > 0)
{
user.DirectReports = new List<ADUser>();
foreach (string directReport in adEntry.Properties["directReports"]) //is being returned as full distinguished name
{
var dr = new DirectoryEntry(string.Format("LDAP://{0}", directReport), "administrator", "S3cur1ty");
user.DirectReports.Add(GetUserByGuid(dr.NativeGuid));
}
return true;
}
else
{
user.DirectReports = new List<ADUser>();
return false;
}
}
catch (Exception ex)
{
user.HasError = true;
user.ErrorMessage = "Error setting direct reports: " + ex.Message;
return false;
}
}
but its' still not catching. I was hoping for a better approach. I'm not sure if I could add something that would output the exception to the console or what. Any help would be appreciated. TIA
P.S. this isn't necessarily the method thats crashing, there's a web of them in the service.
You should dump all of your exceptions to a log file on the server side; exposing error information to the client is a potential security risk, which is why it's turned off by default.
If you really want to send exception information to the client, you can turn it on. If you are using a WCF service you should set the "includeExceptionDetailsInFaults" property on for the service behavior, as described in this MSDN article on dealing with unhandled exceptions in WCF. Once you do so, you will have a property on the FaultException called Detail that should itself be a type of Exception.
For better error handling you should also take a look at typed faults using the FaultContract and FaultException<> class; these have the benefit that they don't throw the channel into a faulted state and can be handled correctly:
try
{
// do stuff here
}
catch (Exception ex)
{
var detail = new CustomFaultDetail
{
Message = "Error setting direct reports: " + ex.Message
};
throw new FaultException<CustomFaultDetail>(detail);
}
If you are using an ASP.NET Web Service, you should set the customErrors mode to "Off" in your web.config. This will send back the entire exception detail as HTML, which the client should receive as part of the SOAP exception that it receives.
The error your are seeing ("faultexception was unhandled by user code") is happening because this is a remote exception and it is standard behavior to only display exceptions on the local computer by default. In order to make it work how you intend, you need to change the customErrors section of the web.config and set it to Off
UPDATE: I found a related question: c# exception not captured correctly by jquery ajax
(Three years later..)
Here's the solution I came up with, along with some sample WCF code, and Angular code to catch, and display the exception message:
Catching exceptions from WPF web services
Basically, you just need to wrap your WCF service in a try..catch, and when something goes wrong, set a OutgoingWebResponseContext value.
For example, in this web service, I've slipped in an Exception, which will make my catch code set the OutgoingWebResponseContext value.
It looks odd... as I then return null, but this works fine.
public List<string> GetAllCustomerNames()
{
// Get a list of unique Customer names.
//
try
{
throw new Exception("Oh heck, something went wrong !");
NorthwindDataContext dc = new NorthwindDataContext();
var results = (from cust in dc.Customers select cust.CompanyName).Distinct().OrderBy(s => s).ToList();
return results;
}
catch (Exception ex)
{
OutgoingWebResponseContext response = WebOperationContext.Current.OutgoingResponse;
response.StatusCode = System.Net.HttpStatusCode.Forbidden;
response.StatusDescription = ex.Message.Replace("\r\n", "");
return null;
}
}
What is brilliant about this try..catch is that, with minimal changes to your code, it'll add the error text to the HTTP Status, as you can see here in Google Chrome:
If you didn't have this try..catch code, you'd just get an HTTP Status Error of 400, which means "Bad Request".
So now, with our try..catch in place, I can call my web service from my Angular controller, and look out for such error messages coming back.
$http.get('http://localhost:15021/Service1.svc/getAllCustomerNames')
.then(function (data) {
// We successfully loaded the list of Customer names.
$scope.ListOfCustomerNames = data.GetAllCustomerNamesResult;
}, function (errorResponse) {
// The WCF Web Service returned an error
var HTTPErrorNumber = errorResponse.status;
var HTTPErrorStatusText = errorResponse.statusText;
alert("An error occurred whilst fetching Customer Names\r\nHTTP status code: " + HTTPErrorNumber + "\r\nError: " + HTTPErrorStatusText);
});
Cool, hey ?
Incredibly simple, generic, and easy to add to your services.
Shame some readers thought it was worth voting down. Sorry about that.
You have several options:
1) If you are using WCF, throw a FaultException on the server and catch it on the client. You could, for instance, implement a FaultContract on your service, and wrap the exception in a FaultException. Some guidance to this here.
2) You could use the Windows Server AppFabric which would give you more details to the exception within IIS. (requires some fiddling to get it working, though)
3) Why not implement some sort of server-side logging for the exceptions? Even if to a file, it would be invaluable to you to decipher what is really happening. It is not a good practice (especially for security reasons) to rely on the client to convey the inner workings of the server.
I have a WCF service that's hosted in IIS, and uses a WS HTTP binding (the external service). This service ends up calling a second WCF service that's hosted in a Windows service, over Net TCP (the internal service). When the internal service throws a FaultException, the external service crashes rather than throwing it to the client. All the client sees is the connection being forcibly closed.
The internal service uses the Enterprise Library Validation Application Block to validate the incoming messages. When validation errors occur, the service throws a FaultException<ValidationFault>.
Both the internal and external service have a [FaultContract(typeof(ValidationFault)] attribute in the service contract. If I change the external service to just immediately throw a new FaultException<ValidaitonFault>, this gets back to the client fine. I can catch the exception from the internal service in the external service, but if I try to re-throw it, or even wrap it in a new exception and throw that, the whole Application Pool in IIS crashses. I can't see anything useful in the event log, so I'm not sure exactly what the problem is.
The client object the external service uses to communicate with the internal service is definitely being closed and disposed of correctly. How can I get the internal service's faults to propagate out to the client?
updated:
Below is a simplified version of the external service code. I can catch the validation fault from the internal service call. If I throw a brand new FaultException<ValidationFault>, everything is fine. If I use the caught exception, the connection to the external client is broken. The only difference I can see is when debugging the service - trying to use the caught exception results in a message box appearing when exiting the method, which says
An unhandled exception of type
'System.ServiceModel.FaultException`1'
occurred in mscorlib.dll
This doesn't appear if I throw a brand new exception. Maybe the answer is to manually copy the details of the validation fault into a new object, but this seems crazy.
public class ExternalService : IExternalService
{
public ExternalResponse DoSomething(ExternalRequest)
{
try
{
var response = new ExternalResponse();
using (var internalClient = new InternalClient())
{
response.Data = internalClient.DoSomething().Data;
}
return response;
}
catch (FaultException<ValidationFault> fEx)
{
// throw fEx; <- crashes
// throw new FaultException<ValidationFault>(
// fEx.Detail as ValidationFault); <- crashses
throw new FaultException<ValidationFault>(
new ValidationFault(new List<ValidationDetail> {
new ValidationDetail("message", "key", "tag") }),
"fault message", new FaultCode("faultCode"))); // works fine!
}
}
}
I have almost the exact design as you and hit a similar issue (not sure about a crash, though!).
If I remember correctly, even though the ValidationFault is a common class when the Fault travels over the wire the type is specific to the WCF interface. I think this is because of the namespace qualifiers on the web services (but this was a while back so I could be mistaken).
It's not terribly elegant, but what I did was to manually re-throw the exceptions:
try
{
DoStuff();
}
catch (FaultException<ValidationFault> fe)
{
HandleFault(fe);
throw;
}
...
private void HandleFault(FaultException<ValidationFault> fe)
{
throw new FaultException<ValidationFault>(fe.Detail as ValidationFault);
}
Well, it works if I do this, but there must be a better way...
This only seems to be a problem for FaultException<ValidationFault>. I can re-throw FaultException and FaultException<SomethingElse> objects with no problems.
try
{
DoStuff();
}
catch (FaultException<ValidationFault> fe)
{
throw this.HandleFault(fe);
}
...
private FaultException<ValidationFault> HandleFault(
FaultException<ValidationFault> fex)
{
var validationDetails = new List<ValidationDetail>();
foreach (ValidationDetail detail in fex.Detail.Details)
{
validationDetails.Add(detail);
}
return new FaultException<ValidationFault>(
new ValidationFault(validationDetails));
}