中文字幕日韩一区二区_国产一区二区av_国产毛片av_久久久久国产一区_色婷婷电影_国产一区二区精品

一個(gè)完整的ASP.NET 2.0 URL重寫方案[翻譯]

這篇文章描述了一個(gè)完整的 ASP.NET 2.0 URL 重寫方案。這個(gè)方案使用正則表達(dá)式來定義重寫規(guī)則并解決通過虛擬 URLs 訪問頁(yè)面產(chǎn)生回發(fā)事件的一些可能的困難。

為什么要重寫 URL ?

將 URL 重寫方法應(yīng)用到你的 ASP.NET 應(yīng)用程序的兩個(gè)主要原因是:可用性和可維護(hù)性。

可用性

誰都知道,相對(duì)于難于辨認(rèn)的帶參數(shù)的長(zhǎng)的查詢路徑,用戶更喜歡一些短的、簡(jiǎn)潔的 URL。任何時(shí)候,一個(gè)容易記住和敲入的路徑比添加到收藏夾更有用。其次,當(dāng)一個(gè)瀏覽器的收藏夾不可用時(shí),記住的地址總比在搜索引擎中輸入關(guān)鍵字進(jìn)行搜索,然后再查找要強(qiáng)的多。比較下面的兩個(gè)地址:

 

(1)

http://www.somebloghost.com/Blogs/Posts.ASPx?Year=2006&Month=12&Day=10

(2)

http://www. somebloghost.com/Blogs/2006/12/10/

 

第一個(gè) URL 包含了查詢字符串;第二個(gè)URL包含的信息可以讓用戶清楚的看到他看的東西,它還可以使用戶更容易的修改地址欄的內(nèi)容,如:http://www.somehost.com/Blogs/2006/12/.

可維護(hù)性

在很多WEB應(yīng)用程序中,開發(fā)人員經(jīng)常會(huì)將頁(yè)面從一個(gè)目錄移到另一個(gè)目錄,讓我們假設(shè)一開始有兩個(gè)可用頁(yè)面: http://www.somebloghost.com/Info/Copyright.ASPx 和 http://www.somebloghost.com/Support/Contacts.ASPx,但是后來開發(fā)者將 Copyright.ASPx 和 Contacts.ASPx 移到了 Help 目錄,用戶收藏起來地址就需要重新定位。這個(gè)問題雖然可以簡(jiǎn)單的用 Response.Redirect(new location) 來解決,但是如果有成百上千的頁(yè)面呢?應(yīng)用程序中就會(huì)包含大量的無效鏈接。

使用 URL 重寫,允許用戶只需修改配置文件,這種方法可以讓開發(fā)者將web應(yīng)用程序邏輯結(jié)構(gòu)與物理結(jié)構(gòu)獨(dú)立開來。

ASP.NET 2.0 中的原有的URL 映射

ASP.NET 2.0 為 web 應(yīng)用程序提供了一個(gè)開箱即用的映射靜態(tài) URL 的解決方案。這個(gè)方案不用編寫代碼就可以在 web.config 中將舊的 URLs 映射到新的地址。 要使用 URL 映射,只需在 web.config 文件的 system.web 節(jié)中創(chuàng)建一個(gè)新的 urlMappings 節(jié) ,并添加要映射的地址 (“ ~/ ”指向應(yīng)用程序的根目錄):

<urlMappings enabled="true">

   <add url="~/Info/Copyright.ASPx" mappedUrl="~/Help/Copyright.ASPx" />

   <add url="~/Support/Contacts.ASPx" mappedUrl="~/Help/Contacts.ASPx" />

</urlMappings>

這樣,如果用戶輸入 http://www.somebloghost.com/Support/Contacts.ASPx, 它將看到 http://www.somebloghost.com/Help/Contacts.ASPx , 而他并不知道那個(gè)頁(yè)已經(jīng)移除。

這個(gè)方案對(duì)于只有兩個(gè)頁(yè)面被移到其它位置的情況是足夠的。但它對(duì)有一打的需要重定位的頁(yè)或者需要?jiǎng)?chuàng)建一個(gè)整潔的URL來說,它是不合適的。另一個(gè)使用ASP.NET 的原有的URL映射技術(shù)的不太好的地方是:如果 Contacts.ASPx 頁(yè)包含的元素在回發(fā)到服務(wù)器時(shí)(這是非??赡艿?, 用戶將會(huì)驚奇的發(fā)現(xiàn)地址 http://www.somebloghost.com/Support/Contacts.ASPx 卻變成了 http://www.somebloghost.com/Help/Contacts.ASPx

。  這是因?yàn)?a href=/itjie/ASPjishu/ target=_blank class=infotextkey>ASP.NET 引擎用頁(yè)面的實(shí)際地址修改了表單formaction 屬性 ,所以表單就變成了下面的樣子:

<form name="formTest" method="post"
action="http://www.simple-talk.com/Help/Contacts.ASPx" id="formTest">

</form>

這樣看來,URL 映射在ASP.NET 2.0 中幾乎是無用的。我們應(yīng)當(dāng)能夠使用一個(gè)映射規(guī)則來指定一系列相似的 URL。最好的解決方案就是使用正則表達(dá)式 ( Wikipedia 上可以查看概覽,and 在 .NET 下的實(shí)現(xiàn)可以查看 MSDN), 但由于 ASP.NET 2.0 映射不支持正則表達(dá)式,所以我們需要開發(fā)一個(gè)內(nèi)建到 URL 映射的不同的方案- URL 重寫模塊。 最好的方法就是創(chuàng)建一個(gè)可重用的、簡(jiǎn)單的配置模塊來實(shí)現(xiàn),顯然我們應(yīng)創(chuàng)建一個(gè) HTTP 模塊 (關(guān)于 HTTP 模塊的詳細(xì)信息請(qǐng)查看 MSDN 雜志) 并在獨(dú)立的程序集中實(shí)現(xiàn)。要使這個(gè)程序集簡(jiǎn)單易用,我們應(yīng)實(shí)現(xiàn)這個(gè)重寫引擎的可配置性,即能夠在 web.config 中指定規(guī)則。

 

 

在開發(fā)過程中,我們應(yīng)能使這個(gè)重寫模塊打開或關(guān)閉 (比如你有一個(gè)較難捕獲的bug,而它可能是由不正確的重寫模塊引起的)這樣在 web.config 中對(duì)重寫模塊的配置節(jié)進(jìn)行打開或關(guān)閉就成為一個(gè)選擇。這樣,在 web.config 中,一個(gè)配置節(jié)的示例如下:

<rewriteModule>

  <rewriteOn>true</rewriteOn>

  <rewriteRules>

      <rule source="(/d+)/(/d+)/(/d+)/"
         destination="Posts.ASPx?Year=$1&Month=$2&Day=$3"/>

      <rule source="(.*)/Default.ASPx"
         destination="Default.ASPx?Folder=$1"/>

  </rewriteRules>

</rewriteModule>

這樣,所有像: http://localhost/Web/2006/12/10/ 這樣的請(qǐng)示,將會(huì)在內(nèi)部將會(huì)用帶參數(shù)的請(qǐng)求重定向到 Posts.ASPx 。

請(qǐng)注意: web.config 是一個(gè)結(jié)構(gòu)良好的 XML 文件, 它禁止在屬性值中使用 & 符號(hào),所以在例子中,應(yīng)當(dāng)使用 & 代替。

要在配置文件中使用這個(gè)重寫模塊,還需要注冊(cè)節(jié)和指定處理模塊,像下面這樣增加一個(gè)configSections配置節(jié):

 <configSections>

    <sectionGroup name="modulesSection">

      <section name="rewriteModule" type="RewriteModule.
RewriteModuleSectionHandler, RewriteModule"/>

    </sectionGroup>

  </configSections>

這樣你就可以在 configSections 節(jié)的后面這樣使用了:

<modulesSection>

    <rewriteModule>

      <rewriteOn>true</rewriteOn>

      <rewriteRules>

              <rule source="(/d+)/(/d+)/(/d+)/" destination="Post.ASPx?Year=$1&Month=$2&Day=$3"/>

              <rule source="(.*)/Default.ASPx" destination="Default.ASPx?Folder=$1"/>

      </rewriteRules>

    </rewriteModule>

  </modulesSection>

另一個(gè)我們?cè)陂_發(fā)重寫模塊過程中要做的就是還需要允許在虛擬路徑中傳遞參數(shù),象這樣: http://www.somebloghost.com/2006/12/10/?Sort=Desc&SortBy=Date 。所以我們還需要有一個(gè)檢測(cè)通過虛擬 URL 傳遞參數(shù)的解決方案。

 

 

接下來讓我們來創(chuàng)建類庫(kù)。首先,我們要引用 System.Web 程序集,這樣我們可以實(shí)現(xiàn)一些基于 web 特殊功能。如果要使我們的模塊能夠訪問 web.config,還需要引用 System.Configuration 程序集。

 

處理配置節(jié)

要能處理 web.config 中的配置,我們必需創(chuàng)建一個(gè)實(shí)現(xiàn)了 IConfigurationSectionHandler 接口的類 (詳情查看 MSDN )。如下:

using System;

using System.Collections.Generic;

using System.Text;

using System.Configuration;

using System.Web;

using System.Xml;

 

 

namespace RewriteModule

{

    public class RewriteModuleSectionHandler : IConfigurationSectionHandler

    {

 

        private XmlNode _XmlSection;

        private string _RewriteBase;

        private bool _RewriteOn;

 

        public XmlNode XmlSection

        {

            get { return _XmlSection; }

        }

 

        public string RewriteBase

        {

            get { return _RewriteBase; }

        }

 

        public bool RewriteOn

        {

            get { return _RewriteOn; }

        }

        public object Create(object parent,
                            object configContext,
                            System.Xml.XmlNode section)

        {

            // set base path for rewriting module to

            // application root

            _RewriteBase = HttpContext.Current.Request.ApplicationPath + "/";

 

            // process configuration section

            // from web.config

            try

            {

                _XmlSection = section;

                _RewriteOn = Convert.ToBoolean(
                            section.SelectSingleNode("rewriteOn").InnerText);

            }

            catch (Exception ex)

            {

                throw (new Exception("Error while processing RewriteModule
configuration section.", ex));

            }

            return this;

        }

    }

}

RewriteModuleSectionHandler 類將在 web.config 中的 XmlNode 通過調(diào)用 Create 方法初始化。XmlNode 類的 SelectSingleNode 方法被用來返回模塊的配置值。

使用重寫的 URL 的參數(shù)

在處理象 http://www. somebloghost.com/Blogs/gaidar/?Sort=Asc (這是一個(gè)帶參數(shù)的虛擬 URL ) 虛擬的 URLS 時(shí),能夠清楚的辨別通過虛擬路徑傳遞的參數(shù)是非常重要的,如下:

<rule source="(.*)/Default.ASPx" destination="Default.ASPx?Folder=$1"/>,

你可能使用這樣的 URL:

http://www. somebloghost.com/gaidar/?Folder=Blogs

它的效果和下面的相似:

http://www. somebloghost.com/Blogs/gaidar/

要處理這個(gè)問題,我們需要對(duì)'虛擬路徑參數(shù)' 進(jìn)行包裝。這可以是通過一個(gè)靜態(tài)的方法去訪問當(dāng)前的參數(shù)集:

using System;

using System.Collections.Generic;

using System.Text;

using System.Collections.Specialized;

using System.Web;

 

namespace RewriteModule

{

 

    public class RewriteContext

    {

        // returns actual RewriteContext instance for

        // current request

        public static RewriteContext Current

        {

            get

            {

                // Look for RewriteContext instance in

                // current HttpContext. If there is no RewriteContextInfo

                // item then this means that rewrite module is turned off

                if(HttpContext.Current.Items.Contains("RewriteContextInfo"))

                    return (RewriteContext)
HttpContext.Current.Items["RewriteContextInfo"];

                else

                    return new RewriteContext();

            }

        }

 

        public RewriteContext()

        {

            _Params = new NameValueCollection();

            _InitialUrl = String.Empty;

        }

 

        public RewriteContext(NameValueCollection param, string url)

        {

            _InitialUrl = url;

            _Params = new NameValueCollection(param);

           

        }

 

        private NameValueCollection _Params;

 

        public NameValueCollection Params

        {

            get { return _Params; }

            set { _Params = value; }

        }

 

        private string _InitialUrl;

 

        public string InitialUrl

        {

            get { return _InitialUrl; }

            set { _InitialUrl = value; }

        }

    }

}

可以看到,這樣就可以通過RewriteContext.Current 集合來訪問 “虛擬路徑參數(shù)”了,所有的參數(shù)都被指定成了虛擬路徑或頁(yè)面,而不是像查詢字符串那樣了。

重寫 URL

接下來,讓我們嘗試重寫。首先,我們要讀取配置文件中的重寫規(guī)則。其次,我們要檢查那些在 URL 中與規(guī)則不符的部分,如果有,進(jìn)行重寫并以適當(dāng)?shù)捻?yè)執(zhí)行。

創(chuàng)建一個(gè) HttpModule:

class RewriteModule : IHttpModule
{

public void Dispose() { }
public void Init(HttpApplication context)

{}

}

當(dāng)我們添加 RewriteModule_BeginRequest 方法以處理不符合規(guī)則的 URL時(shí),我們要檢查給定的 URL 是否包含參數(shù),然后調(diào)用 HttpContext.Current.RewritePath 來進(jìn)行控制并給出合適的 ASP.NET 頁(yè)。

using System;

using System.Collections.Generic;

using System.Text;

using System.Web;

using System.Configuration;

using System.Xml;

using System.Text.RegularExpressions;

using System.Web.UI;

using System.IO;

using System.Collections.Specialized;

 

namespace RewriteModule

{

    class RewriteModule : IHttpModule

    {

 

        public void Dispose() { }

 

        public void Init(HttpApplication context)

        {

            // it is necessary to

            context.BeginRequest += new EventHandler(
                 RewriteModule_BeginRequest);

        }

 

        void RewriteModule_BeginRequest(object sender, EventArgs e)

        {

 

            RewriteModuleSectionHandler cfg =
(RewriteModuleSectionHandler)
ConfigurationManager.GetSection
("modulesSection/rewriteModule");

 

            // module is turned off in web.config

            if (!cfg.RewriteOn) return;

 

            string path = HttpContext.Current.Request.Path;

 

            // there us nothing to process

            if (path.Length == 0) return;

 

            // load rewriting rules from web.config

            // and loop through rules collection until first match

            XmlNode rules = cfg.XmlSection.SelectSingleNode("rewriteRules");

            foreach (XmlNode xml in rules.SelectNodes("rule"))

            {

                try

                {

                    Regex re = new Regex(
                     cfg.RewriteBase + xml.Attributes["source"].InnerText,
                     RegexOptions.IgnoreCase);

                    Match match = re.Match(path);

                    if (match.Success)

                    {

                        path = re.Replace(
                             path,
                             xml.Attributes["destination"].InnerText);

                        if (path.Length != 0)

                        {

                            // check for QueryString parameters

                       if(HttpContext.Current.Request.QueryString.Count != 0)

                       {

                       // if there are Query String papameters

                       // then append them to current path

                       string sign = (path.IndexOf('?') == -1) ? "?" : "&";

                       path = path + sign +
                          HttpContext.Current.Request.QueryString.ToString();

                       }

                       // new path to rewrite to

                       string rew = cfg.RewriteBase + path;

                       // save original path to HttpContext for further use

                       HttpContext.Current.Items.Add(

                         "OriginalUrl",

                         HttpContext.Current.Request.RawUrl);

                       // rewrite

                       HttpContext.Current.RewritePath(rew);

                       }

                       return;

                    }

                }

                catch (Exception ex)

                {

                    throw (new Exception("Incorrect rule.", ex));

                }

            }

            return;

        }

 

    }

}

 

這個(gè)方法必須注冊(cè):

public void Init(HttpApplication context)

{

 context.BeginRequest += new EventHandler(RewriteModule_BeginRequest);

}

但這些僅僅完成了一半,因?yàn)橹貙懩K還要處理表單的回發(fā)和虛擬路徑參數(shù)集合,而這段代碼中你會(huì)發(fā)現(xiàn)并沒處理這些。讓我們先把虛擬路徑參數(shù)放到一邊,先來正確地處理最主要的回發(fā)。

如果我們運(yùn)行上面的代碼,并通過查看 ASP.NET 的 HTML 源代碼 的 action 會(huì)發(fā)現(xiàn),它竟然包含了一個(gè) ASP.NET 的實(shí)際路徑頁(yè)。例如,我們使用頁(yè) ~/Posts.ASPx 來處理像 http://www. somebloghost.com/Blogs/2006/12/10/Default.ASPx 的請(qǐng)求, 發(fā)現(xiàn) action="/Posts.ASPx"。這意味著用戶并沒有使用虛擬路徑進(jìn)行回發(fā),而是使用了實(shí)際的 http://www. somebloghost.com/Blog.ASPx. 這個(gè)并不是我們需要的。所以,需要加一段代碼來處理這些不希望的結(jié)果。

首先,我們要在 HttpModule 注冊(cè)和實(shí)現(xiàn)一個(gè)另外的方法:

        public void Init(HttpApplication context)

        {

            // it is necessary to

            context.BeginRequest += new EventHandler(
                 RewriteModule_BeginRequest);

            context.PreRequestHandlerExecute += new EventHandler(
                 RewriteModule_PreRequestHandlerExecute);

        }

 

      void RewriteModule_PreRequestHandlerExecute(object sender, EventArgs e)

        {

            HttpApplication app = (HttpApplication)sender;

            if ((app.Context.CurrentHandler is Page) &&
                 app.Context.CurrentHandler != null)

            {

                Page pg = (Page)app.Context.CurrentHandler;

                pg.PreInit += new EventHandler(Page_PreInit);

            }

        }

這個(gè)方法檢查用戶是否請(qǐng)求了一個(gè)正常的 ASP.NET 頁(yè),然后為該頁(yè)的 PreInit 事件增加處理過程。這兒 RewriteContext 將處理實(shí)際參數(shù),然后二次重寫URL。二次重寫是必需的,以使 ASP.NET 能夠在它的表單的action屬性中使用一個(gè)虛擬路徑。

void Page_PreInit(object sender, EventArgs e)

        {

            // restore internal path to original

            // this is required to handle postbacks

            if (HttpContext.Current.Items.Contains("OriginalUrl"))

            {

              string path = (string)HttpContext.Current.Items["OriginalUrl"];

 

              // save query string parameters to context

              RewriteContext con = new RewriteContext(
                HttpContext.Current.Request.QueryString, path);

 

              HttpContext.Current.Items["RewriteContextInfo"] =  con;

 

              if (path.IndexOf("?") == -1)

                  path += "?";

              HttpContext.Current.RewritePath(path);

            }

        }

最后,我們來看一下在我們的重寫模塊程序集中的三個(gè)類:

 

在 web.config 中注冊(cè)重寫模塊

要使用重寫模塊,需要在配置文件中的 httpModules 節(jié)注冊(cè)重寫模塊,如下:

<httpModules>

<add name="RewriteModule" type="RewriteModule.RewriteModule, RewriteModule"/>

</httpModules>

使用重寫模塊

在使用重寫模塊時(shí),需要注意:

  • 在 web.config 中來使用一些特殊字符是不可能的,因?yàn)樗且粋€(gè)結(jié)構(gòu)良好的 XML 文件,因此,你只能用 HTML 編碼的字符代替,如:使用 & 代替 &。
  • 要在你的 ASPX 中使用相對(duì)路徑,需要在HTML標(biāo)簽調(diào)用 ResolveUrl 方法,如: <img src="<%=ResolveUrl("~/Images/Test.jpg")%>" />。
  • Bear in mind the greediness of regular expressions and put rewriting rules to web.config in order of their greediness, for instance:
<rule source="Directory/(.*)/(.*)/(.*)/(.*).ASPx"
destination="Directory/Item.ASPx?
Source=$1&Year=$2&ValidTill=$3&Sales=$4"/>
<rule source="Directory/(.*)/(.*)/(.*).ASPx"
destination="Directory/Items.ASPx?
Source=$1&Year=$2&ValidTill=$3"/>
<rule source="Directory/(.*)/(.*).ASPx"
destination="Directory/SourceYear.ASPx?
Source=$1&Year=$2&"/>
<rule source="Directory/(.*).ASPx"
destination="Directory/Source.ASPx?Source=$1"/>
  • 如果你要在頁(yè)面中使用 RewriteModule 而不使用 .ASPx,就必須在 IIS 中進(jìn)行配置以使用期望的擴(kuò)展映射到請(qǐng)求頁(yè),如下節(jié)所述:

IIS 配置: 使用帶擴(kuò)展的重寫模塊代替 .ASPx

要使用帶擴(kuò)展的重寫模塊代替 .ASPx (如 .html or .xml), 必須配置 IIS ,以使這些擴(kuò)展映射到 ASP.NET 引擎 (ASP.NET ISAPI 擴(kuò)展)。要進(jìn)行這些設(shè)置,需要以管理員身份登錄。

打開 IIS 管理控制臺(tái),并選擇你要配置的站點(diǎn)的虛擬路徑:

Windows XP (IIS 5)
Virtual Directory "RW"                                

 

Windows 2003 Server (IIS 6)
Default Web Site

然后在虛擬路徑標(biāo)簽上點(diǎn)擊 Configuration… 按鈕  (或如果要使用整個(gè)站點(diǎn)都做映射就選擇主目錄標(biāo)簽)。

Windows XP (IIS 5)                     

Windows 2003 Server (IIS 6)

接下來,點(diǎn)擊添加按鈕,并輸入一個(gè)擴(kuò)展,你還需要指定一個(gè) ASP.NET ISAPI 擴(kuò)展,注意去掉選項(xiàng)的對(duì)勾以檢查文件是否存在。

如果你要把所有的擴(kuò)展都映射到 ASP.NET,對(duì)Windows XP上的 IIS 5 來說只需要設(shè)置 .* 到 ASP.NET ISAPI ,但對(duì) IIS 6 就不一樣了,點(diǎn)擊“添加”然后指定 ASP.NET ISAPI 擴(kuò)展。

總結(jié)

現(xiàn)在,我們已經(jīng)創(chuàng)建了一個(gè)簡(jiǎn)單的但非常強(qiáng)大的 ASP.NET 重寫模塊,它支持可基于正則表達(dá)式的 URLs 和頁(yè)面回發(fā),這個(gè)解決方案是容易實(shí)現(xiàn)的,并且提供給用戶的例子也是可用的,它可以用簡(jiǎn)短的、整潔的URL來替代查詢字符串參數(shù)。  要使用這個(gè)模塊,只需簡(jiǎn)單在你的應(yīng)用程序中對(duì) RewriteModule 進(jìn)行引用,然后在 web.config 文件中添加幾行代碼以使你不想顯示的 URL 通過正則表達(dá)式代替。這個(gè)重寫模塊是很容易部署的,因?yàn)橹恍枰趙eb.config中修改任何“虛擬”的URL即可,如果你需要進(jìn)行測(cè)試,還可以對(duì)重寫模塊進(jìn)行關(guān)閉。

要想對(duì)重寫模塊有一個(gè)深入的了解,你可以查看本文提供的原代碼。我相信你會(huì)發(fā)現(xiàn)這是一個(gè)比ASP.NET提供的原始映射更好的體驗(yàn)。

AspNet技術(shù)一個(gè)完整的ASP.NET 2.0 URL重寫方案[翻譯],轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 亚洲日韩中文字幕一区 | 毛片免费看 | 亚洲视频一区二区三区四区 | 国内自拍视频在线观看 | 日韩精品一区二区三区四区视频 | 看片天堂| 成人免费视频网站在线看 | 国产精品久久久亚洲 | 久久国产欧美一区二区三区精品 | 91福利影院| 亚洲精品黄 | 国产午夜精品一区二区三区嫩草 | 国产a级黄色录像 | 久久精品中文 | 精品欧美一区二区在线观看视频 | 国产盗摄视频 | 国产不卡视频 | 国产精品三级久久久久久电影 | 中文字幕av网址 | 亚洲黄色网址视频 | 久久青 | 亚洲 欧美 激情 另类 校园 | 国产特一级黄色片 | 久久一区视频 | 欧美成人精品 | 91精产国品一二三区 | 国产成人精品高清久久 | 国产一区二| 日日操操 | 日韩在线播放网址 | 国产乱码久久久久久 | 日韩视频一区二区 | 欧美性视频在线播放 | 欧美aaa一级片 | 国产精品免费一区二区三区 | 九九av | 99久久精品一区二区成人 | 亚洲另类春色偷拍在线观看 | 性做久久久久久免费观看欧美 | 国产精品久久久久久久7电影 | 国产ts人妖一区二区三区 |