I've developed silverlight client with makes async web services calls to a asmx web service. The problem is, I want to handle exceptions, so far as to be able to tell in the client application whether there was an exception in the webservice (and therefore will be logged local to the webservice) or whether there was a communication problem (i.e. the endpoint for the webservice was wrong).
In testing both types of exceptions in my project I get the same generic exception:
System.ServiceModel.CommunicationException: The remote server returned an error: NotFound.
This exception is amazingly useless when an exception occured in the webservice as it clearly has been found.
Is the presence of this generic error to do with security (not being allowed to see true errors)? It can't be the fact that I don't have debug strings as I'm running on a dev PC.
Either way, my question is, what's the best way to handle async errors in a commercial silverlight application?
Any links or ideas are most welcome! :)
Thanks a lot!
Andy.
Yes, the generic error deals with security. The idea being that if an attacker does find a fault in the page etc. The person doesn't know what the cause of the error was.
Have you turned on remote debugging in the serviceDebug tag?
http://www.mostlydevelopers.com/mostlydevelopers/blog/post/2009/01/14/Debugging-Tips-ndash3b-The-remote-server-returned-an-error-NotFound.aspx
This should return a less general error.
I think you may actually be getting a 404 error here. If there were an exception in the service but includeDetailsInException were set to false, then you'd get a FaultException with nothing but the Exception.Message.
I think you need to go look on the machine the service is running on to see if there were any errors or warnings at around the time your client received the exception. In particular, if the service is running on .NET 2.0 or above, then the default configuration of ASP.NET Health Monitoring will log a warning message to the Application event log when an unhandled exception occurs.
I was attempting to find the "correct" solution for this as well. It isn't a good idea to return the raw exceptions, so this is a simplified version of what I came up with:
public class AjaxResponse
{
public string Message { get; set; }
public string FullError { get; set; }
public AjaxResponse() { }
public AjaxResponse(Exception e)
{
Message = e.Message;
FullError = e.ToString();
}
}
public class AjaxResponse<T> : AjaxResponse
{
public T Response { get; set; }
public AjaxResponse() { }
public AjaxResponse(Exception e) : base(e) { }
public AjaxResponse(T resp)
{
Response = resp;
}
}
Usage:
[WebMethod]
public AjaxResponse<T> DoStuff(...)
{
try
{
T value = new T(...);
return new AjaxResponse<T>(value);
}
catch (Exception ex)
{
return new AjaxResponse<T>(ex);
}
}
Related
Is there a way of correctly handling WebExceptions within a Data Access Layer?
Below is a method SendReceive within our DAL used to communicate with our remote server, if there is a communication issue, such as endpoint being inaccessible and therefore no data can be retrieved, I would like the user to be redirected to a View, informing the user to please try again later.
private static TResult SendReceive<TResult, TPayLoad>(string method, string route, TPayLoad payload, bool post, bool authentication, string hashedPassword)
{
var subject = "WebApplication1 - " + method + " Error";
using (var webClient = new WebClient())
{
try
{
var uri = new Uri("http://ourdomain/ourwebapicontroller/" + route);
webClient.Headers[HttpRequestHeader.ContentType] = "application/json";
if (authentication)
{
var hashedPasswordAsBytes = Encoding.UTF8.GetBytes(hashedPassword);
webClient.Headers.Add(HttpRequestHeader.Authorization, "Basic " + Convert.ToBase64String(hashedPasswordAsBytes));
}
var response = post ? webClient.UploadString(uri, JsonConvert.SerializeObject(payload)) : webClient.DownloadString(uri);
var parsedResponse = JsonConvert.DeserializeObject<TResult>(response);
return parsedResponse;
}
catch (WebException webException)
{
SendEmail(subject, MvcApplication.To, MvcApplication.From, "<p>WebException [" + webException.Message + "]</p>");
// Issue with endpoint
}
catch (Exception exception)
{
SendEmail(subject, MvcApplication.To, MvcApplication.From, "<p>Exception [" + exception.Message + "]</p>");
}
}
return default(TResult);
}
public Models.WebApplication1.Test GetTest(int id)
{
return SendReceive<Models.WebApplication1.Test, int?>("GetTest", "get-test/" + id, null, false, false, null);
}
public int SetTest(Models.WebApplication1.Test test)
{
return SendReceive<int, Models.WebApplication1.Test>("SetTest", "set-test", test, true, false, null);
}
As the DAL is referenced from a Controller I don't believe it is possible to use throw new HttpException(), this can however be handled like so:
public ViewResult Test(int id)
{
var test = Dal.GetTest(id);
if (test == null)
{
throw new HttpException(404, "Please try again later.");
}
return View(test);
}
Would prefer to handle the communication issue within SendReceive as opposed to handling at Controller level for each method referencing SendReceive.
Everything depends on what you mean by "handle" and even "exception."
Controller
Within the controller, what do you want to do if the client requests something that doesn't exist? A 404 is a good response. But what if the DAL throws an exception? Would it make sense to return the exact same result to the client? A 500 error which tells the client something went wrong might make more sense.
That mismatch is indicated here:
throw new HttpException(404, "Please try again later.");
If the request threw an exception (for any reason, including the DAL) then returning a 500 error with "try again later" makes sense. You're communicating clearly that the problem is on your end. Sorry, hopefully it won't happen again, and if does we're working on it.
If the client requested something that doesn't exist then that may or may not ever change. Should they try again later? Why? Maybe what they've requested will never be found. That's also not an exception. Someone asking for something that doesn't exist and getting nothing means that your application is working correctly. The 404 tells them that our application is working - we just don't have what they want.
Based on that, bubbling up an actual exception to the controller probably makes sense. The DAL doesn't know about the controller or even a website. It's not in a good position to know whether or not the caller should know that there was an exception.
DAL
"Handling" an exception can mean different things. (I'll leave out my opinion about which is right because it's not relevant.)
If your DAL throws an exception, you can do a few things. Some are maybe better than others, but again, that depends on opinion and needs.
- Do nothing. Let the exception bubble up.
- Log it and rethrow it.
- Log it then wrap it in another exception that provides some context, and throw the new exception. (Whether to wrap an exception or not is a whole discussion.)
Some would say that "handling" an exception is something different that involves somehow reacting to the exception in a way that solves a problem, something we're less likely to do. For example, if our application retrieves a daily Chuck Norris joke from an API but it throws an exception, we might log it so we know something went wrong but then replace it with a backup Chuck Norris joke.
The most important thing I wouldn't do is "hide" the exception so that, to the caller, an exception and "nothing found" look the same. If something has gone wrong, the controller needs to know that - even if it doesn't understand the specifics - so it (not the DAL) - can determine what should be communicated to the caller.
The relationship between the controller and the DAL is similar to that between the browser client and the controller. If it's not just working, we communicate that. If there's no data, we communicate that.
I don't recommend putting writing code in the DAL that sends an email. That's very specific, and it couples all of your code to that decision and possibly to an implementation of sending mail.
An alternative is defining an interface like this:
public interface ILog
{
void LogException(Exception ex);
void LogMessage(string message);
}
...and injecting into the DAL class. When an exception occurs, call _log.LogException(ex);. Your DAL doesn't know what the implementation is. You could log it or even send an email if you want to.
Preamble: This question is different from "Exception Handling in ASP.net Web API" as the OP was looking for custom Error Handling, not global handling. It also differs from other earlier questions that were answered, as those were for earlier versions of Web API, not version 2. Also note I am going to self answer this question. It took quite some searching to find the correct answer.
The question is: How do I globally handle errors in Web API 2.0? The error handling I have set up for MVC does not get activated for web api calls and I need to generically handle any error that is thrown so that relevant error information is returned to the client.
Global Error handling is correctly answered in this asp.net article. However the articles is missing a few important points to make the code actually work.
The article covers the details, but here it is in a nutshell:
Global Error Handling is already included in System.Web.Http.ExceptionHandling The classes in the article are already in this library, so there is no need to rewrite them.
The only class you need to write is the one which is customized for your app. In the article, they call it the "OopsExceptionHandler" However, the one written in the article does not compile. This is the updated code that does work:
public class OopsExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
context.Result = new TextPlainErrorResult
{
//If you want to return the actual error message
//Content = context.Exception.Message
Request = context.ExceptionContext.Request,
Content = "Oops! Sorry! Something went wrong." +
"Please contact support#contoso.com so we can try to fix it."
};
}
private class TextPlainErrorResult : IHttpActionResult
{
public HttpRequestMessage Request { get; set; }
public string Content { get; set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response =
new HttpResponseMessage(HttpStatusCode.InternalServerError);
response.Content = new StringContent(Content);
response.RequestMessage = Request;
return Task.FromResult(response);
}
}
}
You then need to register the ExceptionHandler. An example of this is not given in the article, so here it is:
config.Services.Replace(typeof(IExceptionHandler), new OopsExceptionHandler());
This line goes in the WebApiConfig file, in the register method. Note that it is using 'Replace' not 'Add' as it will error out on Add. Only one is allowed by the framework.
That is all that is required. To test, throw an error from your web API and you will see the error message returned as the content of the webAPI call. Note that there are security implications to returning error messages to the client, so make sure this is what you really want.
I have been debugging an issue with my newly minted WCF services Fault contract and finally found out what was breaking it.
I defined the service like so:
[ServiceContract]
public interface IService1
{
[OperationContract]
[FaultContract(typeof(ApplicationException))]
string GetData();
}
in my service I was handling exception in the service like so:
public string GetData()
{
try
{
// do stuff
}
catch(Exception e)
{
ApplicationException ae = new ApplicationException("oh dear!", e );
throw new FaultException<ApplicationException>( ae,
new FaultReason(ae.Message));
}
}
However, the client would never receive the fault exception, instead it would get an exception which said:
An error occurred while receiving the HTTP response to ... This could
be due to the service endpoint binding not using the HTTP protocol.
This could also be due to an HTTP request context being aborted by the
server ( possibly due to the service shutting down).See server logs
for more details
If I changed my code on the service like so (ie: do NOT set the inner exception when constructing the ApplicationException) it works as expected.
public string GetData()
{
try
{
// do stuff
}
catch(Exception e)
{
ApplicationException ae = new ApplicationException("oh dear!");
throw new FaultException<ApplicationException>( ae,
new FaultReason(ae.Message));
}
}
Can anyone explain why this might fail if the inner exception is set? I could not see it anywhere in the documentation.
When the ApplicationException is sent to the client via FaultException<T> without the InnerException, then it is sent only as a string. However, when the InnerException is set, then the ApplicationException itself is sent.
The Exception type is serializable in .NET (it is often incorrectly cited as not being serializable), however, frequently the contents of the Data property are not serializable. This will cause a serialization issue which is what I believe you are experiencing.
There are a few workarounds to this issue: You can set the Data property to null by using reflection or you can create your own class.
I'm writing a WCF Service, and was wondering if there is a recommended pattern for returning various messages from a service method.
For example, if I call the public User Login(string userName, string passWord) method in my service, I want it to return my user object when they are valid users. However, if the details are not correct, or the account is disabled, I want to be able to return that data to the client.
I initially thought I would throw a LogonFailureException (custom exception class) when one of these two scenarios occurred. In the client, I could then catch this type and process it as I wanted. However, if I'm debugging the application, it breaks when the exception is thrown (even if I wrap it in a FaultException).
I could also create a ServiceResponse class that all my methods use as a return type - in there I could then have a "Messages" collection, but that seems potentially counter-intuitive. I'd rather the returntypes of my methods were simply what I expect to get back in normal circumstances.
So, bottom line, is there a particular pattern of dealing with messages coming back from the service method that is recommended?
Thanks
You need to throw a strongly-typed soap fault:
[DataContract]
public class ConnectionFault
{
[DataMember]
public string Issue { get; set; }
[DataMember]
public string Details { get; set; }
}
[FaultContract(typeof(ConnectionFault))]
[FaultContract(typeof(DataReaderFault))]
[OperationContract]
Int16 GetInStock(int productId);
//when it's time to throw the exception
var connectionFault = new ConnectionFault();
connectionFault.Issue = "Problem connecting to the database";
connectionFault.Details = ex.Message;
throw new FaultException<ConnectionFault>(connectionFault);
Then the client can catch it.
In my opinion, There are 2 scenarios.
1. You wish to throw a sort of exception on the Service side.
2. You wish to return a class that contains error data of something went wrong in Service.
What is the difference ? The first would be just like yaron wrote, you throw and catch a FaultException.
The second would be just to return an object, for example if something went wrong in your service, you always return ErrorData - this is a data contract that contains message and exception stack as string - you can add ErrorData.Update method, and every time you catch a new exception in service, update the ErrorData message.. this is good for long running calls that might catch several exception but you dont want to abort on the first one.
No matter which of these you choose, DO NOT INHERIT from Exception, I have read this is bad practice, you dont want to serialize and transfer Exception based data contracts over WCF.
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.