Jest to set-up, które Mam. Nie przekieruje i obsługuje zarówno aplikację, jak i niektóre skonfigurowane błędy IIS w tym samym miejscu. Możesz również przekazać dowolne informacje do kontrolera błędu.
W 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>
W ErrorController (pokazano tylko sygnatury metody dla uproszczenia)
// 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)
{
...
}
Dodatkowo mam ErrorViewModel
i widokiem Index
.
W 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);
}
Jak dotąd, jest to podstawowa konfiguracja, że mam. To nie wykona przekierowania (działanie kontrolera błędu jest wykonywane z Application_Error) i obsługuje wyjątki kontrolera, a także, na przykład, IIS 404 (takie jak twojastrona.com/blah.html).
Od tego momentu wszystko, co stanie się wewnątrz twojego ErrorController
, będzie oparte na twoich potrzebach.
Jako przykład, dodam kilka dodatkowych szczegółów dotyczących mojej implementacji. Jak już mówiłem, mam ErrorViewModel
.
Moja 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; }
}
Moi ErrorController (istotne części):
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);
}
W niektórych przypadkach muszę wyświetlać niestandardowy komunikat, gdy wystąpi błąd.Mam niestandardowy wyjątek do tego celu:
[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;
}
}
Potem używać go tak:
catch(SomeException ex)
{
throw new DisplayableException("My Title", "My custom display message", "An error occurred and I must display something", ex)
}
w moim ErrorController
I obsłużyć ten wyjątek oddzielnie, ustawianie ErrorViewModel
„s Title
i Text
właściwości z tego DisplayableException
.
To jedno podejście, ale z pewnością mniej niż idealne. Na przykład niektóre wyjątki mają wewnętrzne wyjątki. Zawierają również dodatkowe informacje. –