Asp.Net WebApi catch exceptions generated in controller constructor - c#

In my base API controller class constructor, I am populating user permissions based on windows authentication. I am using something like
var ctx = new PrincipalContext(ContextType.Domain, System.Environment.UserDomainName.ToUpper());
var userInfo = UserPrincipal.FindByIdentity(ctx, System.Environment.UserName.ToUpper());
to get user information from AD. The problem is that when the user is locked out the application is failing with 500 since the exception occurs in the constructor. Is there a way to handle the exception of these calls and bubble it up as an Unauthorized exception or something else? I tried to surround it with try catch and throw httpresponse exception with unauthorized but it still bubbles up as 500.
edit #1
There are few more things happening in constructor and when it is failing I do not want to fail with 500. I need to catch the exception and throw something else. Authorization was just an example of one of those few things.
edit #2
To all who write that I should not have any logic in the constructor:
Constructors are used to initialize the object to a valid state. The controller of this part of the application has to have a database connection, user information and some other properties populated because all of the requests are using all this information. If any of those is failing I want to return a different type of the error to the user. In the case of a standard class it would be different type of exceptions (SqlException, DbConnectionException, Some sort of AD Exception). In the case of WebApi I want it to be a different type of the response codes (Unauthorized, Not Implemented (501) etc). Copy pasting the same code to every request in that section of the application represents a maintenance issue.

Since the original exception is wrapped into other exceptions few times the only way I found so far is to have a global exception filter and then navigate through the inner exceptions of the context. Exception till it is null or I didn't reach the exception of the type I am looking for.
namespace SomeWebApi
{
public class GlobalExceptionHandler : ExceptionHandler
{
public override async Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
if (context.Exception != null)
{
Exception filteredException = context.Exception;
while (
(filteredException != null)
&&
(filteredException.GetType() != typeof(HttpResponseException)
)
{
filteredException = filteredException.InnerException ;
}
if (
(filteredException != null)
&&
(filteredException != context.Exception)
)
{
var httpResponseException = (HttpResponseException) filteredException;
var response = context.Request.CreateErrorResponse(
httpResponseException.Response.StatusCode,
httpResponseException
);
context.Result = new ResponseMessageResult(response);
}
}
}
}
}
Next I needed to register it in WebApiConfig.Register:
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());

The eseaiest way is to not use user / sesion related logic in controllers constructor.
For authentication You can use attribute filters, for geting user information just create helper class and use it when needed.
edit
one last thing: try to use dependency injection (like structure map) it will force You to make proper changes in constructor and it will make initializing easier. https://www.exceptionnotfound.net/setting-up-dependency-injection-in-web-api-with-structuremap/

Related

How to setup an exception handler in EmbedIO Web API

I'm using EmbedIO with the Web API module.
I'd like to have an exception handler that will catch all the unhandled exceptions and return a suited HTTP error code according to the exception type. However, it's not clear if this can be achieved.
The class WebModuleBase exposes a property called OnUnhandledException that can be set to an ExceptionHandlerCallback, but when the callback is invoked, the response's status code has already been set to HttpStatusCode.InternalServerError, as stated in the code comments.
This is not convenient since I want to set the response code myself.
WebModuleBase exposes a different property called OnHttpException that can be set to a HttpExceptionHandlerCallback. This can be set to
HttpExceptionHandler.DataResponse(ResponseSerializer.Json) which partially solves the issue.
The main concern now is that the application exceptions must be converted to HttpException in the controllers.
I'd like to throw custom exceptions from the domain code, get them in an exception handler and just return a HTTPException in there, according to the initial exception.
Basically something similar to Exception Filters in ASP.NET Web API.
Here's the code to setup the web server:
var webApiModule = new WebApiModule("/api", ResponseSerializer.Json)
.WithController<MyController>();
webApiModule.OnUnhandledException = ExceptionHandler.DataResponseForException();
webApiModule.OnHttpException = ExceptionHandler.DataResponseForHttpException();
WebServerEmbedded = new EmbedIO.WebServer(
opt => opt
.WithUrlPrefix(url)
.WithMode(HttpListenerMode.EmbedIO))
.WithModule(null, webApiModule);
These are the delegates used for exception handlers:
internal static class ExceptionHandler
{
public static ExceptionHandlerCallback DataResponseForException()
{
return (context, exception) => ResponseSerializer.Json(context, exception.Message);
}
public static HttpExceptionHandlerCallback DataResponseForHttpException()
{
return (context, httpException) => ResponseSerializer.Json(context, httpException.Message);
}
}
Thanks.
Exceptions, as well as HTTP exceptions, are handled by EmbedIO at both module and server level (each nested module group introduces a further level, but that's beyond the point).
The catch clause for HTTP exceptions always comes before the "general-purpose" catch clause, for the obvious reason that HTTP exceptions are exceptions themselves and need to be sorted out. Therefore, if an exception handler throws a HTTP exception, the latter must be handled at an outer level.
In opther words, you can write a module-level exception handler that throws a HTTP exception, then use a server-level HTTP exception handler to generate the appropriate response.
var webApiModule = new WebApiModule("/api", ResponseSerializer.Json)
.WithController<MyController>()
.HandleUnhandledException(ExceptionHandler.DataResponseForException));
WebServerEmbedded = new EmbedIO.WebServer(
opt => opt
.WithUrlPrefix(url)
.WithMode(HttpListenerMode.EmbedIO))
.WithModule(webApiModule)
.HandleHttpException(ExceptionHandler.DataResponseForHttpException);
internal static class ExceptionHandler
{
public static Task DataResponseForException(IHttpContext context, Exception exception)
{
// Replace ANY_VALID_STATUS CODE with, well, any valid status code.
// Of course you can use different status codes according to, for example,
// the type of exception.
throw new HttpException(ANY_VALID_STATUS_CODE, exception.Message);
}
public static Task DataResponseForHttpException(IHttpContext context, IHttpException httpException)
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
return ResponseSerializer.Json(context, httpException.Message);
}
}
EDIT: There's an even simpler way, if you need it for custom exceptions: just have your exceptions implement IHttpException.
Here you can see how IHttpException methods are used by the HTTP exception handling code.
Here is an example of probably the most obscure method, PrepareResponse.
EDIT: I added setting the status code in DataResponseForHttpException.

Ensure that a specific exception always results in a given HTTP response status code

Requirement
I have an ASP.Net MVC application that works with a number of different libraries. As with most libraries, various function calls may cause one of many different exceptions to be thrown.
Currently, whenever any exception is thrown, then MVC application 'handles' them and returns an "internal server error" (code 500) back to the client (eg. web browser).
This is fine for most cases, however, there is one specific exception type (in this case UnauthorizedAccessException) that I would like to result in an "Unauthorized" (code 401) status being sent in the response, instead of the usual 500 error.
Current Attempt
I did a fair bit of research and it looks like the bets way to 'catch' all exceptions and process them is by using the Application_Error method. So I tried the following implementation of Application_Error in the MvcApplication class:
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
if(ex is UnauthorizedAccessException)
{
Response.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
}
}
Problem
The problem here is that although I can debug and see that Response.StatusCode is being set to 401, the client/browser is still receiving a 500 error.
I expect that there is a very good reason why this is happening, but I have exhausted my brain thinking of search terms that will get me the answer I need.
Question
In short, what do I need to do in order to get the behaviour I am looking for?
For additional information, ultimately what I want is for an UnauthorizedAccessException to have the same behaviour as how MVC handles unauthenticated requests (which redirects to login page). However, I also need it to work for AJAX requests in that my javascript can check for 401 error and do some specific logic (in which case a response redirect to login page is not workable)
A clever way to go about doing that is to create a Base Controller that your controllers inherit over the default Controller. There you inherit the default Controller class and override the OnException method.
public abstract class BaseController : Controller
{
protected override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
var responseCode = Response.StatusCode;
var exception = filterContext.Exception;
switch (exception.GetType().ToString())
{
case "UnauthorizedAccessException":
responseCode = 401;
filterContext.ExceptionHandled = true;
break;
}
Response.StatusCode = responseCode;
base.OnException(filterContext);
}
}
The trick that makes it work is filterContext.ExceptionHandled = true; if you don't set this to true, the server will return 500.
Your controllers will inherit the BaseController;
public class UserController : BaseController
{
public ActionResult Index(){
return View();
}
}
What you'll need to add to this code is your redirect to the login page, to your OnException method (if needed). I would do it for you but I don't have enough time to write and test it for you.. currently waiting for automated tests to finish.. :-)
Edit:
I did not realize your view could throw errors too, that obviously won't be handled by the controller.
In this case we can revert to the original Application_Error method on Global.asax.
What we need is two lines of code..
Response.StatusCode = 401;
Response.End();
First line sets the status code to 401,
Second line ends the execution at this point and triggers EndRequest event, so the StatusCode won't be modified to 500.
If you want to attach a message with your response:
Response.Write("Oops, you're not authorized...");
It would be a good idea to call Response.Clear(); before starting to modify the response object inside your Error Handler.

How to make elmah.io work well with ASP.NET Core and error handing middleware?

Note: this question is regarding elmah.io (https://elmah.io/), the cloud based exception logging service, and not the traditional Elmah .Net library.
I'm using ASP.NET Core and have a simple exception handling middleware.
public class HandleExceptionMiddleware
{
public HandleExceptionMiddleware(RequestDelegate next)
{
Next = next;
}
RequestDelegate Next { get; }
public async Task Invoke(HttpContext httpContext)
{
try
{
await Next(httpContext);
}
catch (Exception ex)
{
await HandleExceptionAsync(httpContext, ex);
}
}
Task HandleExceptionAsync(HttpContext context, Exception ex)
{
var code = HttpStatusCode.InternalServerError;
if (ex is ArgumentException)
code = HttpStatusCode.BadRequest;
var result = JsonConvert.SerializeObject(new { message = ex.Message });
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
}
This middleware will return the following JSON responses depending on the exceptions that it sees:
For ArgumentException:
HTTP/1.1 400 Bad Request
{"message":""}
For all other exceptions:
HTTP/1.1 500 Internal Server Error
{"message":""}
I'd like elmah.io to log 500 responses and ignore 400 responses (which is the default elmah.io configuration). However, when the exception handler and elmah.io are registered in this order in Startup's Configure hook, nothing gets logged in elmah.io:
app.UseElmahIo("API_KEY", new Guid("LOG_ID"));
app.UseMiddleware<HandleExceptionMiddleware>()
If, however, I change the registration order to the following, everything gets logged (including 400 responses). This makes sense as elmah.io handles the exceptions before HandleExceptionMiddleware gets a chance to change the response:
app.UseMiddleware<HandleExceptionMiddleware>()
app.UseElmahIo("API_KEY", new Guid("LOG_ID"));
What is the best way to configure these services so that elmah.io logs 500 responses and ignores 400 responses?
The only workaround I came up with is to create and register 2 exception handing middlewares instead of just 1. One that registers before and one that registers after elmah.io. It works, but seems a bit ugly:
app.UseMiddleware<HandleInternalExceptionMiddleware>() // set 500 responses (these will have already been logged in elmah.io)
app.UseElmahIo("API_KEY", new Guid("LOG_ID"));
app.UseMiddleware<HandleExternalExceptionMiddleware>() // set 400 responses but ignore exceptions that should return 500 (these won't be logged in elmah.io)
I created a sample project here to demonstrate this behavior:
https://github.com/johnnyoshika/elmah-io-experiment
The correct way to configure elmah.io, is using behavior 2. You want to call the UseElmahIo method after calling other methods dealing with exceptions. This is because a lot of error handling middleware (including your HandleExceptionMiddleware) swallow all exceptions and convert the result to something else. In your case, HandleExceptionMiddleware catches all exceptions and set a new response. In this case, our middleware is never notified about the exception (as you mention as well).
We have a couple of different ways to solve this:
Solution 1
Call UseElmahIo after calling UseMiddleware and add a custom ignore filter to ignore the errors eventually becomming bad requests:
app.UseMiddleware<HandleExceptionMiddleware>();
app.UseElmahIo("API_KEY", new Guid("LOG_ID"), new ElmahIoSettings
{
OnFilter = msg => msg.Type == typeof(ArgumentException).Name
});
The downside of this approach is, that you will need to maintain a set of similar rules in both HandleExceptionMiddleware and in your elmah.io config.
Solution 2
Call UseElmahIo before calling UseMiddleware and specify which status codes to log, even though an exception isn't thrown (swallowed by HandleExceptionMiddleware in this case):
app.UseElmahIo("API_KEY", new Guid("LOG_ID"), new ElmahIoSettings
{
HandledStatusCodesToLog = new List<int> { 404, 500, ... }
});
app.UseMiddleware<HandleExceptionMiddleware>();
The downside if this approach is, that you will need to specify all status codes manually and that the information from the actual exception thrown, isn't available on elmah.io. The reason for this again is, that HandleExceptionMiddleware make it impossible for elmah.io to see that an exception were thrown.
I personally prefer solution 1, since that makes sure that all exceptions are catched, including information like stacktrace and the exception type.

Handle 'underlying provider failed on open' in one place

I have an MVC Razor application that uses entity framework 6.0. However, if the DB is down or something, my code starts throwing exceptions at all kinds of random places like when I start evaluating my IEnumerable<T>s and IQueryable<T>s.
The model constructor is generated code that will be overwritten if I modify it which wouldn't help anyway because the constructor doesn't throw exceptions. Instead, the exceptions come in places like this
using (var dataContext = new ArchiveVMADDatabase.ArchiveDatabaseModel())
{
IQueryable<HDeploy> deploys = Helpers.GetProdDeploysFromArchive(dataContext);
var query = getBranchSelectListQuery(deploys);
listItems.AddRange(query);// EXCEPTION IF DB IS DOWN
}
Is there a good way for me to handle this in one place and avoid wrapping nearly 100% of my code in giant try catch blocks? I would really like to have it just return empty sets if it can't talk to the DB.
I'm not sure whether you are working with MVC or Web API, but in Web API one would use Exception Filters to centralize exception handling.
An exception filter is basically a derivative of the ExceptionFilterAttribute that can create a specific response depending on the caught exception:
public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
}
}
}
You don't need to add try {} catch {} blocks anywhere -- Web API triggers the configured exception filters automatically as soon as an exception reaches the outer most layer of your system (i.e. the controller level).
You can activate an exception filter only for specific ApiControllers or globally for every controller.

Exception handling in Controller (ASP.NET MVC)

When an exception is thrown by your own code that's called from an action in a controller how should that be handled? I see a lot of examples of best practices where there are no try-catch statements at all. For example, accessing data from a repository:
public ViewResult Index()
{
IList<CustomModel> customModels = _customModelRepository.GetAll();
return View(customModels);
}
Clearly this code could throw an exception if the call is to a database that it can't access and we are using an ORM like Entity Framework for example.
However all that I can see will happen is that the exception will bubble up and show a nasty error message to the user.
I'm aware of the HandleError attribute but I understand it's mostly used to redirect you to an error page if an exception that's unhandled occurs.
Of course, this code could be wrapped in a try-catch but doesn't separate nicely, especially if you have more logic:
public ViewResult Index()
{
if (ValidationCheck())
{
IList<CustomModel> customModels = new List<CustomModel>();
try
{
customModels = _customModelRepository.GetAll();
}
catch (SqlException ex)
{
// Handle exception
}
if (CustomModelsAreValid(customModels))
// Do something
else
// Do something else
}
return View();
}
Previously I have extracted out all code that could throw exceptions like database calls into a DataProvider class which handles errors and returns messages back for showing messages to the user.
I was wondering what the best way of handling this is? I don't always want to return to an error page because some exceptions shouldn't do that. Instead, an error message to the user should be displayed with a normal view. Was my previous method correct or is there a better solution?
I do three things to display more user-friendly messages:
Take advantage of the global exception handler. In the case of MVC: Application_Error in Global.asax. Learn how to use it here: http://msdn.microsoft.com/en-us/library/24395wz3(v=vs.100).aspx
I subclass Exception into a UserFriendlyException. I do my very best in all of my underlying service classes to throw this UserFriendlyException instead of a plain old Exception. I always try to put user-meaningful messages in these custom exceptions. The main purpose of which is to be able to do a type check on the exception in the Application_Error method. For the UserFriendlyExceptions, I just use the user-friendly message that I've set deep down in my services, like "Hey! 91 degrees is not a valid latitude value!". If it's a regular exception, then it's some case I haven't handled, so I display a more generic error message, like "Oops, something went wrong! We'll do our best to get that fixed!".
I also create an ErrorController that is responsible for rendering user-friendly views or JSON. This is the controller whose methods will be called from the Application_Error method.
EDIT:
I thought I'd give a mention to ASP.NET Web API since it's closely related. Because the consumer of Web API endpoints won't necessarily be a browser, I like to deal with errors a little differently. I still use the "FriendlyException" (#2 above), but instead of redirecting to an ErrorController, I just let all my endpoints return some kind of base type that contains an Error property. So, if an exception bubbles all the way up to the Web API controllers, I make sure to stick that error in the Error property of API response. This error message will either be the friendly message that has bubbled up from the classes the API controller relies on, or it will be a generic message if the exception type is not a FriendlyException. That way, the consuming client can simply check whether or not the Error property of the API response is empty. Display a message if the error is present, proceed as usual if not. The nice thing is that, because of the friendly message concept, the message may be much more meaningful to the user than a generic "Error!" message. I use this strategy when writing mobile apps with Xamarin, where I can share my C# types between my web services and my iOS/Android app.
With Asp.Net MVC you can also override the OnException method for you controller.
protected override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled)
{
return;
}
filterContext.Result = new ViewResult
{
ViewName = ...
};
filterContext.ExceptionHandled = true;
}
This allow you to redirect to a custom error page with a message that refer to the exception if you want to.
I used an OnException override because I have several projects referenes to one that have a Controller that handle errors:
Security/HandleErrorsController.cs
protected override void OnException(ExceptionContext filterContext)
{
MyLogger.Error(filterContext.Exception); //method for log in EventViewer
if (filterContext.ExceptionHandled)
return;
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult
{
Data = new
{
Success = false,
Error = "Please report to admin.",
ErrorText = filterContext.Exception.Message,
Stack = filterContext.Exception.StackTrace
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
filterContext.ExceptionHandled = true;
}
All questions like this are not very constructive, because the answer is always "it depends", because there are so many ways of dealing with error handling.
Many people like to use the HandleError method, because any exception is basically non-recoverable. I mean, what are you going to do if you can't return the objects? You're going to show them an error anyways, right?
The question becomes, how you want to show them the error. If showing them an error page is acceptable, than HandleError works fine, and provides an easy place to log the error. If you're using Ajax or want something fancier, then you need to develop a way to do that.
You talk about a DataProvider class. That's basically what your Repository is. Why not build that into your repository?

Categories

Resources