这是我的设置。它不会重定向,它会在同一个地方同时处理应用程序和一些配置的 IIS 错误。您还可以将任何您想要的信息传递给错误控制器。
在 Web.config 中:
<system.web>
<customErrors mode="Off" />
...
</system.web>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Auto">
<remove statusCode="403" />
<remove statusCode="404" />
<remove statusCode="500" />
<error statusCode="403" responseMode="ExecuteURL" path="/Error/Display/403" />
<error statusCode="404" responseMode="ExecuteURL" path="/Error/Display/404" />
<error statusCode="500" responseMode="ExecuteURL" path="/Error/Display/500" />
</httpErrors>
...
</system.webServer>
在 ErrorController 中(为了简洁而显示方法签名):
// This one gets called from Application_Error
// You can add additional parameters to this action if needed
public ActionResult Index(Exception exception)
{
...
}
// This one gets called by IIS (see Web.config)
public ActionResult Display([Bind(Prefix = "id")] HttpStatusCode statusCode)
{
...
}
另外,我有一个ErrorViewModel 和一个Index 视图。
在Application_Error中:
protected void Application_Error(object sender, EventArgs e)
{
var exception = Server.GetLastError();
var httpContext = new HttpContextWrapper(Context);
httpContext.ClearError();
var routeData = new RouteData();
routeData.Values["controller"] = "Error";
routeData.Values["action"] = "Index";
routeData.Values["exception"] = exception;
// Here you can add additional route values as necessary.
// Make sure you add them as parameters to the action you're executing
IController errorController = DependencyResolver.Current.GetService<ErrorController>();
var context = new RequestContext(httpContext, routeData);
errorController.Execute(context);
}
到目前为止,这是我的基本设置。这不会执行重定向(错误控制器操作从 Application_Error 执行),它会处理控制器异常以及 IIS 404(例如 yourwebsite.com/blah.html)。
从现在开始,ErrorController 内部发生的任何事情都将取决于您的需求。
作为一个例子,我将添加一些关于我的实现的额外细节。正如我所说,我有一个ErrorViewModel。
我的ErrorViewModel:
public class ErrorViewModel
{
public string Title { get; set; }
public string Text { get; set; }
// This is only relevant to my business needs
public string ContentResourceKey { get; set; }
// I am including the actual exception in here so that in the view,
// when the request is local, I am displaying the exception for
// debugging purposes.
public Exception Exception { get; set; }
}
我的ErrorController(相关部分):
public ActionResult Index(Exception exception)
{
ErrorViewModel model;
var statusCode = HttpStatusCode.InternalServerError;
if (exception is HttpException)
{
statusCode = (HttpStatusCode)(exception as HttpException).GetHttpCode();
// More details on this below
if (exception is DisplayableException)
{
model = CreateErrorModel(exception as DisplayableException);
}
else
{
model = CreateErrorModel(statusCode);
model.Exception = exception;
}
}
else
{
model = new ErrorViewModel { Exception = exception };
}
return ErrorResult(model, statusCode);
}
public ActionResult Display([Bind(Prefix = "id")] HttpStatusCode statusCode)
{
var model = CreateErrorModel(statusCode);
return ErrorResult(model, statusCode);
}
private ErrorViewModel CreateErrorModel(HttpStatusCode statusCode)
{
var model = new ErrorViewModel();
switch (statusCode)
{
case HttpStatusCode.NotFound:
// Again, this is only relevant to my business logic.
// You can do whatever you want here
model.ContentResourceKey = "error-page-404";
break;
case HttpStatusCode.Forbidden:
model.Title = "Unauthorised.";
model.Text = "Your are not authorised to access this resource.";
break;
// etc...
}
return model;
}
private ErrorViewModel CreateErrorModel(DisplayableException exception)
{
if (exception == null)
{
return new ErrorViewModel();
}
return new ErrorViewModel
{
Title = exception.DisplayTitle,
Text = exception.DisplayDescription,
Exception = exception.InnerException
};
}
private ActionResult ErrorResult(ErrorViewModel model, HttpStatusCode statusCode)
{
HttpContext.Response.Clear();
HttpContext.Response.StatusCode = (int)statusCode;
HttpContext.Response.TrySkipIisCustomErrors = true;
return View("Index", model);
}
在某些情况下,我需要在发生错误时显示自定义消息。为此,我有一个自定义例外:
[Serializable]
public class DisplayableException : HttpException
{
public string DisplayTitle { get; set; }
public string DisplayDescription { get; set; }
public DisplayableException(string title, string description)
: this(title, description, HttpStatusCode.InternalServerError, null, null)
{
}
public DisplayableException(string title, string description, Exception exception)
: this(title, description, HttpStatusCode.InternalServerError, null, exception)
{
}
public DisplayableException(string title, string description, string message, Exception exception)
: this(title, description, HttpStatusCode.InternalServerError, message, exception)
{
}
public DisplayableException(string title, string description, HttpStatusCode statusCode, string message, Exception inner)
: base((int)statusCode, message, inner)
{
DisplayTitle = title;
DisplayDescription = description;
}
}
那我这样用:
catch(SomeException ex)
{
throw new DisplayableException("My Title", "My custom display message", "An error occurred and I must display something", ex)
}
在我的ErrorController 中,我分别处理这个异常,从这个DisplayableException 设置ErrorViewModel 的Title 和Text 属性。