zoukankan      html  css  js  c++  java
  • MVC之前的那点事儿系列(8):UrlRouting的理解

    文章内容

    根据对Http Runtime和Http Pipeline的分析,我们知道一个ASP.NET应用程序可以有多个HttpModuel,但是只能有一个HttpHandler,并且通过这个HttpHandler的BeginProcessRequest(或ProcessRequest)来处理并返回请求,前面的章节将到了再MapHttpHandler这个周期将会根据请求的URL来查询对应的HttpHandler,那么它是如何查找的呢?

    一起我们在做自定义HttpHandler的时候,需要执行URL以及扩展名匹配规则,然后查找HttpHandler的时候就是根据相应的规则来查找哪个HttpHandler可以使用。另一方面我们本系列教材讲的MVC就是通过注册路由(Route)来匹配到对应的Controller和Action上的,例如Global.asax里的代码:

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }

    但是在匹配这个之前,MVC首先要接管请求才能处理,也就是说我们要有对应MVC的HttpHandler(后面知道它的名字叫MvcHandler)被MapRequestHandler周期的处理引擎查找到并且应用上才行,然后后面才能由 Controller/Action执行。另外一方面,由于该URL地址没有扩展名,所以无法进入ASP.NET的RunTime,MVC2的实现方式是:注册通配符(*.*)映射到aspnet_ISPAI.dll,然后通过一个自定义的UrlRoutingModuel来匹配Route规则,再继续处理,但是MVC3的时候,匹配Route规则的处理机制集成到ASP.NET4.0里了,也就是今天我们这篇文章所要讲的主角(UrlRoutingModule)的处理机制。

    先来看UrlRoutingModule的源码,无容置疑地这个类是继承于IHttpModule,首先看一下Init方法的代码:

    protected virtual void Init(HttpApplication application) {
    
        ////////////////////////////////////////////////////////////////// 
        // Check if this module has been already addded
        if (application.Context.Items[_contextKey] != null) { 
            return; // already added to the pipeline 
        }
        application.Context.Items[_contextKey] = _contextKey; 
    
        // Ideally we would use the MapRequestHandler event.  However, MapRequestHandler is not available
        // in II6 or IIS7 ISAPI Mode.  Instead, we use PostResolveRequestCache, which is the event immediately
        // before MapRequestHandler.  This allows use to use one common codepath for all versions of IIS. 
        application.PostResolveRequestCache += OnApplicationPostResolveRequestCache;
    }

    该代码在PostResolveRequestCache周期事件上添加了我们需要执行的方法,用于URL匹配规则的设置,但是为什么要在这个周期点上添加事件呢?看了注释,再结合我们前面对Pipeline的了解,释然了,要像动态注册自己的HttpHandler,那就需要在MapRequestHandler之前进行注册自己的规则(因为这个周期点就是做这个事情的),但由于IIS6不支持这个事件,所以为了能让IIS6也能运行MVC3,所以我们需要在这个周期之前的PostResolveRequestCache的事件点上去注册我们的规则,也许如果IIS6被微软废弃以后,就会将这个事件添加到真正的开始点MapRequestHandler上哦。

    我们继续来看注册该事件的OnApplicationPostResolveRequestCache方法的代码:

    public virtual void PostResolveRequestCache(HttpContextBase context) { 
        // Match the incoming URL against the route table
        RouteData routeData = RouteCollection.GetRouteData(context);
    
        // Do nothing if no route found 
        if (routeData == null) {
            return; 
        } 
    
        // If a route was found, get an IHttpHandler from the route's RouteHandler 
        IRouteHandler routeHandler = routeData.RouteHandler;
        if (routeHandler == null) {
            throw new InvalidOperationException(
                String.Format( 
                    CultureInfo.CurrentUICulture,
                    SR.GetString(SR.UrlRoutingModule_NoRouteHandler))); 
        } 
    
        // This is a special IRouteHandler that tells the routing module to stop processing 
        // routes and to let the fallback handler handle the request.
        if (routeHandler is StopRoutingHandler) {
            return;
        } 
    
        RequestContext requestContext = new RequestContext(context, routeData); 
     
        // Dev10 766875    Adding RouteData to HttpContext
        context.Request.RequestContext = requestContext; 
    
        IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
        if (httpHandler == null) {
            throw new InvalidOperationException( 
                String.Format(
                    CultureInfo.CurrentUICulture, 
                    SR.GetString(SR.UrlRoutingModule_NoHttpHandler), 
                    routeHandler.GetType()));
        } 
    
        if (httpHandler is UrlAuthFailureHandler) {
            if (FormsAuthenticationModule.FormsAuthRequired) {
                UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this); 
                return;
            } 
            else { 
                throw new HttpException(401, SR.GetString(SR.Assess_Denied_Description3));
            } 
        }
    
        // Remap IIS7 to our handler
        context.RemapHandler(httpHandler); 
    }

    我已经加粗了4行重要的代码,第一行是通过传递HttpContext参数,从RouteCollection找到对应的静态属性RouteData( GetRouteData方法里会先判断真实文件是否存在,如果不存在才去找RouteData),第二行然后从RouteData的属性RouteHandler获取一个IRouteHandler的实例,第三行是从该实例里获取对应的IHttpHandler实例,第4行是调用HttpContext的RemapHandler方法重新map新的handler(这行代码的注释虽然说是remap IIS7,其实IIS6也是用了,只不过判断该方法里对IIS7集成模式多了一点特殊处理而已),然后可以通过HttpContext. RemapHandlerInstance属性来得到这个实例。

    关于Route/RouteData/RouteCollection/IRouteHandler的作用主要就是定义URL匹配到指定的IHttpHandler,然后注册进去,具体实现我们稍后再讲,现在先看一下Http Pipeline里是如何找到这个IHttpHandler实例的,由于IIS6和IIS7集成模式是差不多的,前面的文章我们提到了都是最终调用到IHttpHandlerFactory的实例,然后从中获取IHttpHandler,所以我们这里只分析IIS6和IIS7经典模式的实现。

    先来看BuildSteps里查找HttpHandler的方法MapHandlerExecutionStep的代码,只有几行代码,最重要的是:

    context.Handler = _application.MapHttpHandler(
        context,
        request.RequestType,
        request.FilePathObject, 
        request.PhysicalPathInternal,
        false /*useAppConfig*/); 

    MapHttpHandler就是我们要查找Handler的方法了,来仔细看看代码:

    internal IHttpHandler MapHttpHandler(HttpContext context, String requestType, VirtualPath path, String pathTranslated, bool useAppConfig) { 
        // Don't use remap handler when HttpServerUtility.Execute called
        IHttpHandler handler = (context.ServerExecuteDepth == 0) ? context.RemapHandlerInstance : null;
    
        using (new ApplicationImpersonationContext()) { 
            // Use remap handler if possible
            if (handler != null){ 
                return handler; 
            }
     
            // Map new handler
            HttpHandlerAction mapping = GetHandlerMapping(context, requestType, path, useAppConfig);
    
            // If a page developer has removed the default mappings with <httpHandlers><clear> 
            // without replacing them then we need to give a more descriptive error than
            // a null parameter exception. 
            if (mapping == null) { 
                PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_NOT_FOUND);
                PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_FAILED); 
                throw new HttpException(SR.GetString(SR.Http_handler_not_found_for_request_type, requestType));
            }
    
            // Get factory from the mapping 
            IHttpHandlerFactory factory = GetFactory(mapping);
     
     
            // Get factory from the mapping
            try { 
                // Check if it supports the more efficient GetHandler call that can avoid
                // a VirtualPath object creation.
                IHttpHandlerFactory2 factory2 = factory as IHttpHandlerFactory2;
     
                if (factory2 != null) {
                    handler = factory2.GetHandler(context, requestType, path, pathTranslated); 
                } 
                else {
                    handler = factory.GetHandler(context, requestType, path.VirtualPathString, pathTranslated); 
                }
            }
            catch (FileNotFoundException e) {
                if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated)) 
                    throw new HttpException(404, null, e);
                else 
                    throw new HttpException(404, null); 
            }
            catch (DirectoryNotFoundException e) { 
                if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated))
                    throw new HttpException(404, null, e);
                else
                    throw new HttpException(404, null); 
            }
            catch (PathTooLongException e) { 
                if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated)) 
                    throw new HttpException(414, null, e);
                else 
                    throw new HttpException(414, null);
            }
    
            // Remember for recycling 
            if (_handlerRecycleList == null)
                _handlerRecycleList = new ArrayList(); 
            _handlerRecycleList.Add(new HandlerWithFactory(handler, factory)); 
        }
     
        return handler;
    }

    从代码可以看出,首先如果当前页面使用了HttpServerUtility.Execute进行页面内跳转,就不使用我们通过路由设置的HttpHandler(也就是HttpContent.RemapHandlerInstance属性),如果没有跳转,就使用,并且优先级是第一的,只有当不设置任何基于Route的HttpHandler,才走剩余的匹配规则(也就是之前ASP.NET默认的按照扩展名类匹配的,这部分和我们关系不大就不做详细分析了)。

    好了,知道了UrlRouteModuel的大概机制,我们再回头看看如何通过Route/RouteData/RouteCollection/IRouteHandler这几个类来实现动态注册Route规则的,先来看Route的代码:

    [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
    public class Route : RouteBase
    {    
        public Route(string url, IRouteHandler routeHandler)
        {
            Url = url;
            RouteHandler = routeHandler;
        }
         
        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler) { 
                Url = url;
                Defaults = defaults; 
                Constraints = constraints; 
                RouteHandler = routeHandler;
            }
    
        //省略部分代码
        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            // Parse incoming URL (we trim off the first two chars since they're always "~/")
            string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
    
            RouteValueDictionary values = _parsedRoute.Match(requestPath, Defaults);
    
            if (values == null)
            {
                // If we got back a null value set, that means the URL did not match
                return null;
            }
    
            RouteData routeData = new RouteData(this, RouteHandler);
    
                    // Validate the values
            if (!ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest)) { 
                return null; 
            }
     
            // Copy the matched values
            foreach (var value in values) {
                routeData.Values.Add(value.Key, value.Value);
            } 
    
            // Copy the DataTokens from the Route to the RouteData 
            if (DataTokens != null) { 
                foreach (var prop in DataTokens) {
                    routeData.DataTokens[prop.Key] = prop.Value; 
                }
            }
            return routeData;
        }       
        }

    Route代码提供了一系列的构造函数重载(我们这里只列出了两个),构造函数主要是传入URL和对应的IRouteHandler实例以及约束规则(比如正则等),然后提供了一个最重要的GetRouteData方法,用于将Route自身和IRouteHandler组装成RouteData,然后返回(中途也会验证相应的约束条件,比如是否符合某个正则表达式),RouteData类本身没有什么逻辑,只是暴露了Route和RouteHandler属性。

    我们再来看RouteCollection,该类保存了所有的Route规则(即URL和对应的IRouteHandler),通过静态属性RouteTable.Routes来获取RouteCollection实例,通过UrlRoutingModule里暴露的RouteCollection属性我们可以验证这一点:

    public RouteCollection RouteCollection {
        get { 
            if (_routeCollection == null) { 
                _routeCollection = RouteTable.Routes;
            } 
            return _routeCollection;
        }
        set {
            _routeCollection = value; 
        }
    } 

    还有一个需要注意的,RouteHandler继承的IRouteHandler的代码:

    public interface IRouteHandler
    {
         IHttpHandler GetHttpHandler(RequestContext requestContext);
    }

    该代码只提供了一个GetHttpHandler方法,所有实现这个接口的类需要实现这个方法,MVCHandler就是这么实现的(下一章节我们再细看)。

    至此,我们应该有一个清晰的认识了,我们通过全局静态属性集合(RouteTable.Routes)去添加各种各样的Route(但应该在HttpModule初始化周期之前),然后通过UrlRoutingModule负责注册Route以及对应的IRouteHandler实例(IRouteHandler实例可以通过GetHttpHandler获取IHttpHandler),最终实现根据不同URL来接管不同的HttpHandler。

    MVC正是利用HttpApplication创建的周期(Application_Start方法)来添加了我们所需要的Route规则,当然在添加规则的时候带上了MVCHandler这个重要的HttpHandler,

    代码如下:

    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);
    }
    
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.MapRoute(
            "Default",
            "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                    );
    }

    MapRoute方法是一个扩展方法,通过该扩展方法注册Route是个不错的方法,下一章节,我们讲讲解MVC是如何注册自己的MVCRouteHandler实例以及如何实现MVCHandler的调用的。

    参考资料:

    http://www.cnblogs.com/me-sa/archive/2009/06/01/MVCLifecycle.html

    http://www.cnblogs.com/zhaoyang/archive/2011/11/16/2251200.html

     1、概要

    当我们新建一个MVC项目时,打开他的Web.Config文件可以发现

        <httpModules>
          <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> 我们知道ScriptModule 类就是管理用于 ASP.NET 中 AJAX 功能的 HTTP 模块,在此我们不做介绍
          <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> //这个UrlRoutingModule类才是重点
        </httpModules>

    这个HttpModule,拦截所有请求,对请求进行处理,最终创建和执行合适的处理请求的HttpHandler(MVC3之后,这个UrlRoutingModule集成到MVC程序集中了)。

      当客户端在本地浏览器上输入网址来请求咱们的一个MVC程序时,服务端接收到请求.....此处省略N个字(和asp.net处理一样).....

      HttpApplication的事件注册,即将 UrlRoutingModule 注册到HttpApplication的事件中

    public class UrlRoutingModule : IHttpModule
    {
        protected virtual void Init(HttpApplication application)
        {   //开始只是把要执行的具体方法注册到事件中,等待事件被触发时,在执行已被注册的方法。
            application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache); 
            application.PostMapRequestHandler += new EventHandler(this.OnApplicationPostMapRequestHandler);
        }
    }

      注册完事件之后,那么就要开始执行HttpApplication事件。

    1、执行Global.asax文件中Application_Start方法。
    即:在此处将一个自己定义的路由规则注册到路由集合中。这个路由集合可以由RouteTable.Routes获得。

     protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
                RegisterRoutes(RouteTable.Routes);
            }
    
    public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
                routes.MapRoute(
                    "Default", // 路由名称
                    "{controller}/{action}/{id}", // 带有参数的 URL
                    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值
                ); //在路由表里添加一条路由规则
            }

    2、依次执行HttpApplication的事件。  

    BeginRequest
    AuthenticateRequest
    PostAuthenticateRequest
    AuthorizeRequest
    PostAuthorizeRequest
    ResolveRequestCache
    PostResolveRequestCache 在UrlRoutingModule类中,在此事件中注册了一个执行方法,即:OnApplicationPostResolveRequestCache
    PostMapRequestHandler                                                       OnApplicationPostMapRequestHandler
    AcquireRequestState
    PostAcquireRequestState
    PreRequesHandlerExecute
    PostRequeshandlerExecute
    ReleaseRequesState
    PostReleaseRequestState
    UpdateRequestCache
    PostUpdateRequestCach
    LogRequest
    PostLogRequest
    EndRequest
    OnApplicationPostResolveRequestCache方法
    private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
    {
        HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context);
        this.PostResolveRequestCache(context);
    }
    //这里用HttpContextWrapper类包装当前的HttpContext,实质上也是一个请求的上下文。他可以使用诸如Typemock Isolator或Rhino Mocks的Mock对象框进行模拟变得更简单。
    //并把这个包装之后的上下文作为PostResolveRequestCache的参数
    
    ------------------------------------------------------------------------------------------------------------------
    
    public virtual void PostResolveRequestCache(HttpContextBase context)
    {
        RouteData routeData = this.RouteCollection.GetRouteData(context);
    //GetRouteData方法内部遍历路由集合中的每个路由对象去和上下文中指定的请求URL去匹配。如成功,就返回当前的路由对象RouteData,如不成功,返回null
    //this.RouteCollection就是RouteTable.Routes,即:路由集合。
    
        if (routeData != null)----这里便是判断是否匹配成功
        {
            IRouteHandler routeHandler = routeData.RouteHandler;----//获取一个处理当前匹配成功的路由的对象
         //这个routeHandler其实就是一个MvcRouteHandle类
         ----因为在第一句中执行的GetRouteData方法中,为RouteData的RouteHandler属性赋值为MvcRouteHandler if (routeHandler == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoRouteHandler, new object[0])); } if (!(routeHandler is StopRoutingHandler)) { RequestContext requestContext = new RequestContext(context, routeData);//把当前的请求的信息和与当前请求匹配成功的路由信息再包装起来。 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); //根据包装后的请求信息,最终得到一个MvcHandler
           ---MvcRouteHandler中只有一个方法,GetHttpHandler方法,返回MvcHandler,Mvc中处理请求的类。
           ---PageRouteHandler中也只有一个方法,GetHttpHandler方法,返回的是Page,asp.net中处理请求的类。 
    MvcRouteHandler
              if (httpHandler == null)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoHttpHandler, new object[] { routeHandler.GetType() }));
                }
                //RequestData类是UrlRoutingModule类中嵌套的一个私有类,把处理请求的类和当前请求的虚拟路径
                RequestData data2 = new RequestData {
                    OriginalPath = context.Request.Path,
                    HttpHandler = httpHandler
                };
                context.Items[_requestDataKey] = data2;
                //把封装的处理类MvcHandler和请求的虚拟路径,赋值到 HttpContextWrapper类中。(这样在用到处理类时,就需要实例化,直接取值即可)
                //HttpContextWrapper类包装当前的HttpContext,实质上也是一个请求的上下文。
                context.RewritePath("~/UrlRouting.axd");
    context.RemapHandler(httpHandler);//博客是里这里是这么一句,但是我反编译没找到,可能是版本的问题吧!
    //将MvcHandler 实例 映射到管线中(通常我们是利用web.config 进行配置的,但是MvcHandler 没有默认无参构造函数,所以直接通过向其传递一个实例进行映射)
    } } } 
    OnApplicationPostMapRequestHandler方法
    该方法做的事情很简单,就是重写下请求路径,让输出的路径和输入的路径相同,在这里用来记忆输入路径的是context.Items[],从上下两段代码中可以看到.
    这个事件负责根据文件扩展名映射到具体的httphandle处理类,而MVC的URL信息没有具体的文件后缀名 为了使处理模块能够在iis7中实现路由,则采取了这么一种简单的解决办法。先把路径指向~/UrlRouting.axd,在此事件中会设置一个UrlRouting.axd类型的Handler避免报错,并在下一步事件中替换掉此处的Handler再把~/UrlRouting.axd这个路径给改回来。
    View Code

    上文中得到了一个MvcHandler类实例,MvcHandler继承实现了IHttpAsyncHandler, IHttpHandler, IRequiresSessionState三个接口。而这三个接口如果都实现了,在MVC框架下是不是任何http请求就可以通吃了吗?从MSDN我们得知,事实不是这样的:注意,即使 MvcHandler 实现 IHttpHandler,也不能将其映射为处理程序(例如.mvc 文件扩展名),因为该类不支持无参数构造函数。 (它唯一的构造函数需要一个 RequestContext 对象)
    但是,还好,我们还有MvcHttpHandler。

    如你所知,MvcHttpHandler可以“弥补”MvcHandler的不足,为什么这样说呢?因为MvcHandler没有无参的构造函数,因此即使MvcHandler实现了 IHttpHandler接口,在IIS中也不能将其映射为某类文件扩展名的处理程序,而MvcHttpHandler就提供了不通过路由模块的情况下直接处理映射的处理程序。

    • MvcHttpHandler.使用此处理程序可便于实现直接处理程序映射(不通过路由模块)。如果要将文件扩展名(如 .mvc)直接映射到一个 MVC 处理程序,此处理程序将非常有用。在内部,MvcHttpHandler 将执行 ASP.NET 路由通常执行(通过 MvcRouteHandler 和 MvcHandler)的任务。但是,它是作为处理程序而不是作为模块来执行这些任务的。对所有请求启用UrlRoutingModule 时,通常不使用此处理程序。

    • MvcHandler.此处理程序负责启动 MVC 应用程序的 ASP.NET 管道。它从 MVC 控制器工厂接收 Controller 实例;此控制器处理请求的进一步处理。请注意,即使 MvcHandler 实现了IHttpHandler,它也不能映射为处理程序(例如,针对 .mvc 文件扩展名),因为该类不支持无参数构造函数(而处理程序要求是无参数构造函数)。(其唯一的构造函数需要RequestContext 对象。)

     3、HttpApplication事件继续执行

    BeginRequest
    AuthenticateRequest
    PostAuthenticateRequest
    AuthorizeRequest
    PostAuthorizeRequest
    ResolveRequestCache
    PostResolveRequestCache 
    PostMapRequestHandler                                                       
    AcquireRequestState
    PostAcquireRequestState
    PreRequesHandlerExecute
    PostRequeshandlerExecute
    ReleaseRequesState
    PostReleaseRequestState
    UpdateRequestCache
    PostUpdateRequestCach
    LogRequest
    PostLogRequest
    EndRequest

    在11-12个事件的时候拿到第7个事件的时候创建的MVCHandler对象执行他的ProcessRequest方法。

    public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
    {
        protected virtual void ProcessRequest(HttpContext httpContext)
        {
            //使用HttpContextWrapper对HttpContext进行封装,封装的目的是为了解耦以获得可测试性.然后从RequestContext.RouteData中提取Controller名称.
            HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
            this.ProcessRequest(httpContext2);
        }
        
        protected internal virtual void ProcessRequest(HttpContextBase httpContext)
        {
            IController controller;
            IControllerFactory controllerFactory;
            this.ProcessRequestInit(httpContext, out controller, out controllerFactory); //获取到Controler实例       ----下节详细介绍
            try
            {
                    controller.Execute(this.RequestContext); //当前Controler对象的Action的创建与执行                   ----下节详细介绍   
    执行包括:加载TempData, 创建及执行Action,处理Action返回的ActionResult ,保存TempData数据。 } finally { controllerFactory.ReleaseController(controller); //释放当前Controler对象 } } }

    流程如下图,MvcHandler实例来处理请求,他做为处理的主干,当完成之后,释放当前的Controler实例,继续执行HttpApplication事件

    MvcHandler_1

    重定向(Redirect)用于将用户从一个URL重新路由到另一个URL。重定向有很多种...301和302是最常见的两种。通常针对
    HTML文档进行重定向,但通常也可能用在请求页面中的组件(图片,脚本等)时。实现重定向可能有很多不同的原因,包
    括网站重新设计、跟踪流量、记录广告点击和建议记忆的URL等。

    重定向的类型:

    300 Multiple Choices :可选重定向,表示客户请求的资源已经被转向到另外的地址了,但是没有说明是否是永久重定向
    还是临时重定向。

    301 Moved Permancently :永久重定向,同上,但是这个状态会告知客户请求的资源已经永久性的存在在新的重定向的
    URL上。

    302 Moved Temporarily : 临时重定向,在HTTP1.1中状态描述是Found,这个和300一样,但是说明请求的资源临时被转
    移到新的URL上,在以后可能会再次变动或者此URL会正常请求客户的连接。

    303 See Other : 类似于301/302,不同之处在于,如果原来的请求是POST,Location头指定的重定向目标文档应该通过
    GET提取(HTTP 1.1新)。

    304 Not Modified : 并不真的是重定向 - 它用来响应条件GET请求,避免下载已经存在于浏览器缓存中的数据。

    305 Use Proxy : 客户请求的文档应该通过Location头所指明的代理服务器提取(HTTP 1.1新)。

    306 (废弃,不在使用)

    307 Temporary Redirect : 和302(Found)相同。许多浏览器会错误地响应302应答进行重定向,即使原来的请求是POST
    ,即使它实际上只能在POST请求的应答是303时 才能重定向。由于这个原因,HTTP 1.1新增了307,以便更加清除地区分几
    个状态代码:当出现303应答时,浏览器可以跟随重定向的GET和POST请求;如果是307应答,则浏览器只 能跟随对GET请求
    的重定向。(HTTP 1.1新) 

    因为常见的重定向为301和302,所以下面重点说说这两种重定向对于搜索引擎的优化和实现方法。


    对于搜索引擎的优化:

    1、首先来说下301:301是永久性的,适用于网站域名或者网页存储目录永久性的更改的情况,这种永久性的重定向对于搜
    索引擎无疑是比较友好的。
    在搜索优化之中,301重定向往往还用于实现URL静态化。

    2、再来说下302:302区别于301的永久性,它(302重定向)属于暂时性的转移,这种临时性的重定向适用于临时更换域名
    或者目录名称等情况。
    这种重定向因为是临时性质的,所以对搜索引擎的友好程度就不如301那么友好,请大家使用的时候还需慎重。

    实现方法:

    因为302可能会有URL规范化问题,其方法都是常用的作弊手法,如上所说对搜索引擎不是很友好,所以下面会以301为例。

    首先说下301重定向的必要性:

    当网页A用301重定向转到网页B时,搜索引擎可以肯定网页A永久的改变位置,或者说实际上不存在了,搜索引擎就会把网
    页B当作唯一有效目标。好处是:

    第一、没有网址规范化问题 

    第二、网页A的PR网页级别会传到网页B 

    第三、收录不会因为域名更换没有

    其次301在各种环境下的实现:

    1、IIS下301设置
    Internet信息服务管理器 -> 虚拟目录 -> 重定向到URL,输入需要转向的目标URL,并选择"资源的永久重定向"。

    2、ASP下的301重定向代码
    <%@ Language=VBScript %> 
    <% Response.Status="301 Moved Permanently" Response.AddHeader "Location", "http://www.newurl.com" %>

    3、ASP.Net下的301重定向代码
    <script runat="server"> 
    private void Page_Load(object sender, System.EventArgs e) 

      Response.Status = "301 Moved Permanently"; Response.AddHeader    
      ("Location","http://www.newurl.com"); 

    </script>

    4、PHP下的301重定向代码
    header("HTTP/1.1 301 Moved Permanently"); 
    header("Location: http://www.newurl.com"); exit();

    5、CGI Perl下的301重定向代码
    $q = new CGI; print $q->redirect("http://www.newurl.com/");

    6、JSP下的301重定向代码
    <% 
    response.setStatus(301); 
    response.setHeader( "Location", "http://www.newurl.com/" ); 
    response.setHeader( "Connection", "close" ); 
    %>


    注:重定向会使你的页面变慢。

  • 相关阅读:
    ping 带时间戳
    普通用户使用docker
    docker权限问题Got permission denied while trying
    Linux下离线安装Docker
    Linux启动流程和服务管理(init和systemd)
    CentOS 7 巨大变动之 systemd 取代 SysV的Init
    Linux系统常用的关机或重启命令shutdown、reboot、halt、poweroff、init 0及init 6的联系与区别
    linux的init.d
    linux service命令
    Linux下安装MySQL数据库(压缩包方式安装)
  • 原文地址:https://www.cnblogs.com/micro-chen/p/5821472.html
Copyright © 2011-2022 走看看