首页 > 解决方案 > 如何重定向所有与控制器无关的 url (Asp.Net MVC)

问题描述

www.example/bla/qwerty.hm如果有人把类似的东西(它没有控制器并且它不存在)放到我想总是重定向到我的主页www.example.com.

我想将所有没有控制器的 url 重定向到我的主页并且不显示错误。例如:(www.example.com/auto自动不是控制器)它将被重定向到我的主页。我怎么做?

  1. 我尝试了路由配置

        routes.MapRoute(
            name: "MyRedirect"
            , url: "{contextRedirect}/{*tasks}"
            , defaults: new { controller = "Redirect", action = "TotalRedirect", contextRedirect = "" }
            );
    

    ...

        public ActionResult TotalRedirect(string contextRedirect)
    {
        return RedirectToAction("Index", "Home");
    }
    

但是每次都会调用它并且它会产生一个无限循环(每次调用redirectToAction时都会调用它)

RouteMaps如果我在此之前为所有现有控制器编写所有内容,问题就会消失MapRoute,但我有很多控制器,我想避免RouteMaps为所有控制器编写内容。

  1. 而不是 MapRoute 我尝试了 Web.config 并更改错误

        <customErrors mode="On">
              <error redirect="/Error" statusCode="404" />
              <error redirect="/Error" statusCode="500" />
            </customErrors>
    

    错误是返回的控制器,RedirectToAction我得到与第 1 点(无限循环)相同的结果。但是当我更改它/Error/Home它正在工作(因为主页返回视图),但是在 Url 路径上保存了错误文本Home?aspxerrorpath=/auto。重定向后我不想显示文本,这意味着如果页面将被重定向到www.example.com,它不会显示www.example/Home?aspxerrorpath=/auto

我是 Asp.net MVC 的新手,我不知道正确的方法。


更新

经过一番研究,我认为有两种方法可以做到这一点。

  1. (感谢 KevinLamb)使用 Application_Error 和 Web.Confing httpError 从错误中重定向。这对我有用:

    此设置放在项目级别的 Web.Confing 中,这意味着您在项目资源管理器中看到的第一个 Web.Config(在 View 文件夹中有第二个 Web.Config)。然后您必须使用 ActionResult ErrorHandle 创建名为 Error 的控制器

    <system.webServer>
        <httpErrors errorMode="Custom" existingResponse="Replace">
          <remove statusCode="404"/>
          <error statusCode="404" responseMode="ExecuteURL" path="/Error/ErrorHandle"/>
          <remove statusCode="400"/>
          <error statusCode="400" responseMode="ExecuteURL" path="/Error/ErrorHandle"/>
          <remove statusCode="500"/>
          <error statusCode="500" responseMode="ExecuteURL" path="/Error/ErrorHandle"/>
    </httpErrors>
    

    ...

    // Error Controller .cs
    namespace MyWebApp.Controllers
        {
            public class ErrorController : Controller
            {
                // GET: Error
                public ActionResult Index()
                {
                    return RedirectToAction("Home", "Index");
                }
    
                public ActionResult ErrorHandle()
                {
                    return RedirectToAction("Index", "Home");
                }
            }
        }
    

    ...

    // code inside Global.asax.cs MvcApplication class
    protected void Application_Error(object sender, EventArgs e)
    {
        Exception ex = Server.GetLastError();
    
        //Add some logging here
    
        if(ex.GetType().IsAssignableFrom(typeof(HttpException)))
        {
                //Possibly log that you're redirecting the user
                Response.Clear();
                Response.Redirect("~/");
        }
    }
    

    这是容易的部分。


  1. 我发现的另一种方法是创建 HttpHandler 或 HttpModule。我是 MVC 和 Asp.Net 世界的新手,而 HttpHandler 对我来说并不总是有效,因为它只工作一次,然后只在应用程序更改页面时工作,但它不检测用户创建的 Url(仅第一次)。HttpModule 总是为我工作,但我不知道它是好是坏。它比 1. 难一点,但你不需要 Web.Config 中的 Application_Error 和 httpErrors。

    如果您有 httpErrors 和 Application_Error,请将其删除并创建模块(右键单击它使用和方法Project > Add new Item > In search put "module" > and select Asp.Net Module.创建模块类。然后创建自己的方法并将其注册到.InitDisposeBeginRequest

这是我的 HttpModule 的代码

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Reflection;
using System.Linq;

namespace MyWebApp
{
    public class ErrorHttpModule : IHttpModule
    {
        #region IHttpModule Members

        public void Dispose()
        {
            //clean-up code here.
        }

        public void Init(HttpApplication context)
        {
            // Below is an example of how you can handle LogRequest event and provide 
            // custom logging implementation for it
            // context.LogRequest += new EventHandler(OnLogRequest);
            context.BeginRequest += new EventHandler(BR); // register your own method in to Event where you check Url
        }

        #endregion

        private HttpContext context = null;

        public void BR(Object source, EventArgs e)
        {
            context = System.Web.HttpContext.Current;
            // collect all controllers in web application
            Assembly asm = Assembly.GetAssembly(typeof(MyWebApp.MvcApplication)); // need using System.Reflection;

            var controlleractionlist = asm.GetTypes()
                    .Where(type => typeof(System.Web.Mvc.Controller).IsAssignableFrom(type)) // need using System.Linq;
                    .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public))
                    .Where(m => !m.GetCustomAttributes(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), true).Any())
                    .Select(x => new { Controller = x.DeclaringType.Name, Action = x.Name, ReturnType = x.ReturnType.Name, Attributes = String.Join(",", x.GetCustomAttributes().Select(a => a.GetType().Name.Replace("Attribute", ""))) })
                    .OrderBy(x => x.Controller).ThenBy(x => x.Action).ToList();


            // Get Url
            string page = "";
            if (context != null)
            {
                page = context.Request.Url.PathAndQuery;
            }

            string newUrl;
            if (!String.IsNullOrEmpty(page))
            {
                bool continute = true;
                // does url contain controller or action?
                foreach (var controller in controlleractionlist)
                {
                    string cpath = "/" + controller.Controller.Replace("Controller", "") + (controller.Action == "Index" ? "" : "/" + controller.Action);
                    if (cpath == page)
                    {
                        // Yes, don't continue to redirect
                        continute = false;
                        break;
                    }
                    else if (page == ("/" + controller.Action))
                    {
                        // Yes, don't continue to redirect
                        continute = false;
                        break;
                    }
                }
                // does page load your content, script etc.. ?
                if (page.Contains("Content/") == true
                    || page.Contains("Scripts/") == true
                    || page.Contains(".ico") == true
                    || page == "/"
                    )
                {
                    // Yes, don't redirect.
                    continute = false;
                }
                if (continute)


   {
                    // anything else will redirect to Home page
                    var urlHelper = new UrlHelper(context.Request.RequestContext); // nned using System.Web.Mvc;
                    newUrl = urlHelper.Action("About", "Home");
                    context.Response.Status = "301 Moved Permanently";
                    context.Response.AddHeader("Location", newUrl);
                    context.Response.End();
                }
            }
        }

        public void OnLogRequest(Object source, EventArgs e)
        {
            //custom logging logic can go here
        }
    }
}

最后将模块添加到 Web.Config(在项目级别(ProjectName > Web.Config),而不是在项目下的文件夹内(ProjectName > View > Web.Config))

<system.webServer>
    <modules>
      <add name="MyHttpErrorModule" type="MyWebApp.ErrorHttpModule, MyWebApp"/>
    </modules>
  </system.webServer>

扩展问题

无论是第一点还是第二点,我都有问题,当在 url 中放置这样的字符时/abc=45%$#r。它导致Bad Request - Invalid URL HTTP Error 400. The request URL is invalid.并且这不会检测到 Web.Config 中的 Application_Error、httpErrors 或带有 BeginRequest 的我的 HttpModule。因此我认为这是在 IIS 设置上,但我需要设置什么?看起来这些字符使 IIS 混乱%$

标签: asp.net-mvc

解决方案


首先,您需要设置要在 IIS 中传递的错误,以便您的应用程序可以处理文件/参数链接而不是 IIS。将以下内容添加到您的Web.config

<system.webServer>
    <httpErrors existingResponse="PassThrough" />
</system.webServer>

如果您想在遇到用户访问错误控制器/操作的问题时重定向到您的主页(URL 中没有任何额外参数),请将以下代码添加到您的Global.asax.cs.

protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();

    //Add some logging here

    if(ex.GetType().IsAssignableFrom(typeof(HttpException)))
    {
            //Possibly log that you're redirecting the user
            Response.Clear();
            Response.Redirect("~/");
    }
}

基本上,这样做的目的是让 ASP.NET MVC 网站中的所有错误都通过此方法。然后它检查异常是否为HttpException. 如果是,它将重定向到您的家庭控制器和操作。如果不是HttpException,它将继续执行错误处理过程。这样您就可以继续正确处理重要错误。


推荐阅读