IIS以纯文本形式提供自定义错误页面,没有内容类型头

17

更新:这里是完整的错误处理解决方案

我有一个普通的MVC4 Web项目。什么都没添加,也没有删除,只是在Visual Studio中创建了一个新项目。

web.config中,我添加了自定义错误页面处理程序:

<customErrors mode="On" defaultRedirect="~/Content/Error.htm" redirectMode="ResponseRewrite" />

~/Content/Error.htm 文件是:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>OOPS! Error Occurred. Sorry about this.</title>
</head>
<body>
    <h2>OOPS! Error Occurred</h2>
</body>
</html>

每当我在网站上遇到404错误时,在Firefox和Chrome中,Error.htm以纯文本形式提供:

HTML is displayed as plain text

Fiddler表示错误页面没有提供content-type头,导致浏览器将页面呈现为纯文本。

No Content-type header

有没有一种方法可以强制IIS服务器错误页面带有content-type头?

附言:实际问题出现在一个复杂的MVC4项目中,它在Global.asax中拥有自己的错误处理方式。但我发现一些错误不通过ASP管道处理,只由IIS处理。比如url末尾的点。使用< httpErrors />解决方案确实提供了正确的响应,但是我们在Global.asax中定制的错误处理方式,即Application_Error()这种方式无法被调用。

更新 看来我无法赢得这场斗争。 IE显示HTML正确呈现,Firefox和Chrome则显示为纯文本。当我切换到纯文本时,Firefox和IE会正确显示空格,而IE会忽略空格并尝试呈现HTML。如果我试图将图像作为错误页面服务,Firefox和Chrome会显示图像,而IE会显示此内容:IE9 is on crack!Facepalm!

4个回答

16

使用.aspx代替.htm作为错误页面(将htm重命名为aspx)。

<customErrors mode="On" defaultRedirect="~/Content/Error.aspx" redirectMode="ResponseRewrite" />

我们正在使用MVC。aspx很糟糕,只有在万不得已的情况下才会退而使用htm。而且aspx还会增加故障点。 - trailmax
1
我在使用MVC时遇到了ResponseRewrite的问题,正如你已经注意到的那样,内容类型不正确,你也有同样的问题。将文件从htm重命名为aspx可以解决这个问题,所以这是一种解决方法(我会说)缺陷。我认为这并不那么混乱,但这取决于你。另一种方法是编写自定义处理程序或类似的东西。 - vvucetic
将HTML重命名为*.aspx是可行的。我明天会试一下! - trailmax
4
我刚刚添加了以下代码,并且在 Azure 上托管的 asp.net mvc 中使用 .htm 文件时工作正常:protected void Application_Error(object sender, EventArgs e) { HttpContext.Current.Response.ContentType = "text/html; charset=UTF-8"; } - Guillaume Roy

11

显然,要让<customErrors>起作用很麻烦。如果你决定使用它,Ben Foster在这个主题上有一篇很好的文章:http://benfoster.io/blog/aspnet-mvc-custom-error-pages

如果你想使用.cshtml页面,你最好放弃<customErrors>,并在Global.asax.cs中处理错误:

protected void Application_Error(object sender, EventArgs e)
{
    var exception = Server.GetLastError();
    if (exception != null)
    {
        Response.Clear();
        HttpException httpException = exception as HttpException;
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "Error");
        if (httpException == null)
        {
            routeData.Values.Add("action", "Unknown");
        }
        else
        {
            switch (httpException.GetHttpCode())
            {
                case 404:               // Page not found.
                    routeData.Values.Add("action", "NotFound");
                    break;

                default:
                    routeData.Values.Add("action", "Unknown");
                    break;
            }
        }


        // Pass exception details to the target error View.
        routeData.Values.Add("Error", exception);
        // Clear the error on server.
        Server.ClearError();
        // Avoid IIS7 getting in the middle
        Response.TrySkipIisCustomErrors = true;
        // Ensure content-type header is present
        Response.Headers.Add("Content-Type", "text/html");
        // Call target Controller and pass the routeData.
        IController errorController = new ErrorController();
        errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    }
}

当然,您还需要添加一个带有适当方法和.cshtml视图的ErrorController。

public class ErrorController : Controller
{
    public ActionResult Index()
    {// your implementation
    }

    public ActionResult Unknown(Exception error)
    {// your implementation 
    }

    public ActionResult NotFound(Exception error)
    {// your implementation         
    }
}

在customErrors标签中,您可以指定一些页面,例如:<customErrors defaultRedirect="Index.cshtml" ...>...</customErrors>问题在于customErrors未能传递Content-Type头信息(无论出于何种原因),而这段代码将确保该头信息存在且正确。 - Daniel
index.cshtml 不是你所引用的控制器动作。 - trailmax
不,我曾经使用过Application_Error:它很麻烦,而且并不总是有效。我看过Ben的文章,我也写了一篇关于这个问题的博客文章。最好避免在这里使用*.cshtml - trailmax
1
你的意见。是否有任何具体例子可以说明它“并不总是有效”。我认为人们应该了解他们的选择,并选择最适合自己的选项。在我们的情况下,这就是在这里使用.cshtml文件。 - Daniel
1
我在Global.asax.cs中使用自定义错误处理,对我的应用程序非常有效。我没有使用Response.Headers.Add("Content-Type", "text/html");,因此我的一个错误页面作为文本提供,这显然解决了这个问题。 - Josh
显示剩余4条评论

6

感谢注意到解决方案 aspx 返回错误的状态码,但是对于所有类型的错误返回400也是不正确的。有没有一种方法可以返回原始状态码(而不必为每个错误创建一个页面)? - Alleo
在我的情况下,我有一个400.aspx、一个500.aspx和一个404.aspx;每个页面返回相应的错误代码。不幸的是,我知道这不是最可重用的代码片段。 - Kevin Farrugia

0

我在system.webServer/httpprotocol部分下添加了content-type头和自定义头,问题得到了解决。

<customHeaders><remove name="Content-Type" /><add name="ContentType"value="text/html" /></customHeaders>

2
这样做不会为每个请求进行头部操作吗? - trailmax

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接