当某个操作(asp.net mvc 5)无法在数据库中找到某些内容时,用户必须看到一个包含简短自定义错误消息的页面,例如"Invoice 5 does not exist"
。此外,响应必须具有404
HTTP代码。
另一个例子:当不正确地调用动作时,用户必须看到例如"Parameter 'invoiceId' is required"
。此外,响应必须具有400
HTTP代码。
我尝试将自定义消息存储在HTTP状态描述中,并将其显示在自定义错误页面中。有两种方法(afaik):
A
mvc action:
return HttpNotFound("Invoice 5 does not exist"); OR
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "Parameter 'invoiceId' is required");
web.config:
<httpErrors errorMode="Custom">
<remove statusCode="404" />
<error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
<remove statusCode="400" />
<error statusCode="400" path="/Error/BadRequest" responseMode="ExecuteURL" />
</httpErrors>
乙
mvc action:
throw new HttpException(404, "Invoice 5 does not exist"); OR
throw new HttpException(400, "Parameter 'invoiceId' is required");
web.config:
<customErrors mode="On">
<error statusCode="404" redirect="~/Error/NotFound" />
<error statusCode="400" redirect="~/Error/BadRequest" />
</customErrors>
无论哪种方式,HTTP状态描述如何显示在自定义页面中?如何在NotFound / BadRequest操作或视图中访问它?
如果这是不可能的,4xx响应如何包含数据驱动的消息,很好地呈现给用户?
更新
我很痴迷于使用配置,但似乎无法将自定义字符串推送到web.config中声明的错误页面。 @Carl和@ V2Solutions都只回答了我的后备问题(从“如果这是不可能的......”开始)。我总结一下这样的方法:
C(@Carl)
不要使用web.config或HttpStatusCodeResult。
抛出异常,在Application_Error中捕获它们并以编程方式呈现自定义错误页面。
D(@ V2Solutions)
请勿使用web.config,exception或HttpStatusCodeResult。
直接在Response中设置状态代码/描述,并返回您喜欢的任何视图。
答案 0 :(得分:2)
创建ErrorController - 这允许您定制最终用户错误页面和状态代码。每个操作结果都接受一个异常,您可以在global.asax的application_error方法中将该异常添加到路由数据中。它不一定是异常对象,它可以是你喜欢的任何东西 - 只需将它添加到application_error中的routedata即可。
[AllowAnonymous]
public class ErrorController : Controller
{
public ActionResult PageNotFound(Exception ex)
{
Response.StatusCode = 404;
return View("Error", ex);
}
public ActionResult ServerError(Exception ex)
{
Response.StatusCode = 500;
return View("Error", ex);
}
public ActionResult UnauthorisedRequest(Exception ex)
{
Response.StatusCode = 403;
return View("Error", ex);
}
//Any other errors you want to specifically handle here.
public ActionResult CatchAllUrls()
{
//throwing an exception here pushes the error through the Application_Error method for centralised handling/logging
throw new HttpException(404, "The requested url " + Request.Url.ToString() + " was not found");
}
}
您的错误视图:
@model Exception
@{
ViewBag.Title = "Error";
}
<h2>Error</h2>
@Model.Message
添加一条路线以捕获路线配置末尾的所有网址 - 这会捕获所有尚未通过匹配现有路线捕获的404:
routes.MapRoute("CatchAllUrls", "{*url}", new { controller = "Error", action = "CatchAllUrls" });
在你的global.asax中:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
//Error logging omitted
HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
IController errorController = new Controllers.ErrorController();
routeData.Values.Add("controller", "Error");
routeData.Values.Add("area", "");
routeData.Values.Add("ex", exception);
if (httpException != null)
{
//this is a basic example of how you can choose to handle your errors based on http status codes.
switch (httpException.GetHttpCode())
{
case 404:
Response.Clear();
// page not found
routeData.Values.Add("action", "PageNotFound");
Server.ClearError();
// Call the controller with the route
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
break;
case 500:
// server error
routeData.Values.Add("action", "ServerError");
Server.ClearError();
// Call the controller with the route
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
break;
case 403:
// server error
routeData.Values.Add("action", "UnauthorisedRequest");
Server.ClearError();
// Call the controller with the route
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
break;
//add cases for other http errors you want to handle, otherwise HTTP500 will be returned as the default.
default:
// server error
routeData.Values.Add("action", "ServerError");
Server.ClearError();
// Call the controller with the route
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
break;
}
}
//All other exceptions should result in a 500 error as they are issues with unhandled exceptions in the code
else
{
routeData.Values.Add("action", "ServerError");
Server.ClearError();
// Call the controller with the route
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
}
然后当你抛出
throw new HttpException(404, "Invoice 5 does not exist");
您的信息将被传送并显示给用户。您可以在此时指定要使用的状态代码,并在application_error中扩展switch语句。
答案 1 :(得分:0)
BaseController:
using System.Web;
using System.Web.Mvc;
namespace YourNamespace.Controllers
{
public class BaseController : Controller
{
public BaseController()
{
ViewBag.MetaDescription = Settings.metaDescription;
ViewBag.MetaKeywords = Settings.metaKeywords;
}
protected new HttpNotFoundResult HttpNotFound(string statusDescription = null)
{
return new HttpNotFoundResult(statusDescription);
}
protected HttpUnauthorizedResult HttpUnauthorized(string statusDescription = null)
{
return new HttpUnauthorizedResult(statusDescription);
}
protected class HttpNotFoundResult : HttpStatusCodeResult
{
public HttpNotFoundResult() : this(null) { }
public HttpNotFoundResult(string statusDescription) : base(404, statusDescription) { }
}
protected class HttpUnauthorizedResult : HttpStatusCodeResult
{
public HttpUnauthorizedResult(string statusDescription) : base(401, statusDescription) { }
}
protected class HttpStatusCodeResult : ViewResult
{
public int StatusCode { get; private set; }
public string StatusDescription { get; private set; }
public HttpStatusCodeResult(int statusCode) : this(statusCode, null) { }
public HttpStatusCodeResult(int statusCode, string statusDescription)
{
this.StatusCode = statusCode;
this.StatusDescription = statusDescription;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
context.HttpContext.Response.StatusCode = this.StatusCode;
if (this.StatusDescription != null)
{
context.HttpContext.Response.StatusDescription = this.StatusDescription;
}
// 1. Uncomment this to use the existing Error.ascx / Error.cshtml to view as an error or
// 2. Uncomment this and change to any custom view and set the name here or simply
// 3. (Recommended) Let it commented and the ViewName will be the current controller view action and on your view (or layout view even better) show the @ViewBag.Message to produce an inline message that tell the Not Found or Unauthorized
//this.ViewName = "Error";
this.ViewBag.Message = context.HttpContext.Response.StatusDescription;
base.ExecuteResult(context);
}
}
}
}
在你的行动中使用:
public ActionResult Index()
{
// Some processing
if (...)
return HttpNotFound();
// Other processing
}