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.
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.
I am using web api 2.0 and c# in the server side of my application.
I want to create a custom filter (autorization filter i guess) to only authorize post/put requests that are coming from allowed referrers (that i define) and block all other requests.
EDIT: to explain more: I need a good and way to see if Request.Header.Referrer exists in the list of referrers i predefined to allow request, otherwise i bloc it and don't authorise method to be executed
Do i use AuthorizationFilter? why? and how? or do i need other type of filters like OperationFilter or others?
If you may explain to me in addition what the main differences that make me choose to work with this type of filter not another...
I searched for that in google but all i found is detailed very large implementation but i still don't see the difference between the most popular (used) type of filters and the reason on what we base our choices on.
Thanks in advance
EDIT: I tried to implement CORS but the problem is that CORS doesn't bloc requests like mentioned in this post... and the solution given is too much for a simple need like mine
You can make use of custom HttpHandlers to handle identifying the type of incoming requests. You could derive from System.Net.Http.DelegatingHandler and override the SendAsync method as below
public class RequestFilterHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Task<HttpResponseMessage> responseTask;
if (IsAuthorizedPostOrPutCall(request))
{
responseTask = base.SendAsync(request, cancellationToken).ContinueWith(task => task.Result);
}
else
{
responseTask = new Task<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.Unauthorized));
responseTask.Start()
}
return reponseTask;
}
private bool IsAuthorizedPostOrPutCall(HttpRequestMessage request)
{
var referrerList = //Assumption:Predefined list you get through a service
return referrerList.Contains(request.Headers.Referrer) && ( request.Method == HttpMethod.Post || request.Method == HttpMethod.Put);
}
}
In your Global.asax.cs:
GlobalConfiguration.Configuration.MessageHandlers.Add(new RequestFilterHandler());
I'm using an AuthorizeAttribute on various controllers which may need to return 403 or 429 (too many requests) based on certain attributes of the request itself. I implemented it entirely within a custom OnAuthorization implementation then threw a new HttpResponseException with the appropriate response code if necessary. Worked fine on my machine...
At scale (many thousands of requests a minute), this implementation sucks to the point where it was crashing the site. Moving the same logic into the controller action itself and just returning an appropriate HttpResponseMessage works beautifully in terms of perf so it seems that the expense of throwing the exception in OnAuthorization is the root cause of the perf issues.
I like the idea of implementing this in an attribute I can use to decorate multiple controllers and actions and I vehemently dislike moving even small amounts of logic into controller actions that are then repeated many times. Is it possible to return the appropriate HTTP status from an annotation without throwing an exception? Even if it's not inheriting from AuthorizeAttribute, decorating code in this fashion would be far preferable.
Edit: This is Web API 2, not MVC
As you have discovered, throwing exceptions is expensive. The trick in this case is to override the response in the attribute. As MVC and WebAPI are different (at least prior to MVC6) there are two distinct methods.
MVC
Setting the AuthorizationContext.Result allows you to effectively override what action is being performed. Setting this value will prevent the action it is attached to from running at all:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if(Throw403)
{
filterContext.Result = new HttpStatusCodeResult(403);
}
}
WebAPI
Very similar but you must instead set the HttpActionContext.Response property. One handy feature of this, is that you get a nice enum for the HTTP status code:
public override void OnAuthorization(HttpActionContext actionContext)
{
if(Throw403)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
}
}
How about custom message handlers where you could respond back even before hitting the controller? This happens early in the pipeline.
Edit - pasting relevant info from website
A delegating handler can also skip the inner handler and directly create the response:
public class MessageHandler2 :DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Create the response.
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("Hello!")
};
// Note: TaskCompletionSource creates a task that does not contain a delegate.
var tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(response); // Also sets the task state to "RanToCompletion"
return tsc.Task;
}
}
And this is how you register the handler
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());
// Other code not shown...
}
}
Ref here: http://www.asp.net/web-api/overview/advanced/http-message-handlers
sorry for the bad formatting, this is the best I could do via mobile
I'm working with two or more ASP.NET Web APIs. One of them sends a request to the other and gets a response to be processed. It is a chain of response-request calls.
There is a recommended answer that suggests to create an ExceptionFilterAttribute to handle the exceptions in Web API and send a response with some properties data about the error:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new HandleApiExceptionAttribute());
// ...
}
}
public class HandleApiExceptionAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
var request = context.ActionContext.Request;
var response = new
{
//Properties go here...
};
context.Response = request.CreateResponse(HttpStatusCode.BadRequest, response);
}
}
In this case, the client gets the response and handles the exception.
In the context where I'll have two or more Web APIs that communicate between them, if a business rule exception arises (for example invalid parameters, invalid data, no results found) I'll reply a json response with my error message.
What is the best approach to handle this error propagation between two or more web APIs?
My first approach would be to have a set of exceptions (custom exception class), so JSON response will be parsed into this class and will be switched in final client to display a friendly message to user. In this case, the JSON response will only be propagated between all the web APIs. All web APIs and final client will share a common exceptions library to identify the exception type.
Send and propagate only "string" messages between Web APIs and final client switch/match these strings to respective methods to show friendly messages for final user. It seems this will cause duplicate exception code in each web api and isn't a clean solution.
Another clean and best practice to share Business Rule JSON exceptions between Web APIs to avoid duplicity and string message dependency ?
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);
}
}