一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C# - C# WebApi 路由機制剖析

C# WebApi 路由機制剖析

2022-02-25 14:26懶得安分 C#

這篇文章主要介紹了C# WebApi 路由機制剖析,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

前言:從mvc到webapi,路由機制一直是伴隨著這些技術的一個重要組成部分。

它可以很簡單:如果你僅僅只需要會用一些簡單的路由,如/home/index,那么你只需要配置一個默認路由就能簡單搞定;

它可以很神秘:你的url可以千變萬化,看到一些看似“無厘頭”的url,感覺很難理解它如何找到匹配的action,例如/api/user/1/detail,這樣一個url可以讓你糾結半天。

它可以很晦澀:當面試官提問“請簡單分析下mvc路由機制的原理”,你可能事先就準備好了答案,然后噼里啪啦一頓(型如:urlroutingmodule→routes→routedata→requestcontext→controller),你可能回答很流利,但并不一定能理解這些個對象到底是啥意思。兩年前的面試,博主也這樣做過。

博主覺得,究竟路由機制在你的印象中處于哪一面,完全取決于你的求知欲。路由機制博大精深,博主并未完全理解,但博主是一個好奇心重的人,總覺得神秘的東西就得探索個究竟。今天,博主根據自己的理解,分享下webapi里面路由的原理以及使用,如有考慮不周,歡迎園友們指正。

一、mvc和webapi路由機制比較

1、mvc里面的路由

在mvc里面,默認路由機制是通過url路徑去匹配對應的action方法,比如/home/getuser這個url,就表示匹配home這個controller下面的getuser方法,這個很好理解,因為在mvc里面定義了一個默認路由,在app_start文件夾下面有一個routeconfig.cs文件

C# WebApi 路由機制剖析

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public class routeconfig
 {
  public static void registerroutes(routecollection routes)
  {
   routes.ignoreroute("{resource}.axd/{*pathinfo}");
 
   routes.maproute(
    name: "default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "department", action = "index", id = urlparameter.optional }
   );
  }
 }

url: "{controller}/{action}/{id}"這個定義了我們url的規則,{controller}/{action}定義了路由的必須參數,{id}是可選參數

2、webapi里面的路由

和mvc里面的路由有點不同,webapi的默認路由是通過http的方法(get/post/put/delete)去匹配對應的action,也就是說webapi的默認路由并不需要指定action的名稱。還是來看看它的默認路由配置,我們新建一個webapi項目,在app_start文件夾下面自動生成一個webapiconfig.cs文件:

C# WebApi 路由機制剖析

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static class webapiconfig
 {
  public static void register(httpconfiguration config)
  {
   // web api 路由
   config.maphttpattributeroutes();
 
   config.routes.maphttproute(
    name: "defaultapi",
    routetemplate: "api/{controller}/{id}",
    defaults: new { id = routeparameter.optional }
   );
  }
 }

和mvc類似,routetemplate: "api/{controller}/{id}"這個定義了路由的模板,api/{controller}是必選參數,{id}是可選參數,那么問題就來了,如果我們的url不包含action的名稱,那么如何找到請求的方法呢?我們先來簡單看一個例子:

?
1
2
3
4
5
6
7
8
public class ordercontroller : apicontroller
 {
  [httpget]
  public object getall()
  {
   return "success";
  }
 }

我們通過url來訪問

C# WebApi 路由機制剖析

說明請求能夠成功。

為什么這個請求能夠成功呢?那是因為,當我們訪問http://localhost:21528/api/order這個路徑的時候,webapi的路由引擎會自動去匹配"api/{controller}/{id}"這個模板,于是找到了控制器是order這個,那么問題來了?它是如何定位到getall()這個方法的呢?這里就是和mvc不同的地方,前面說過,webapi的路由規則是通過http方法去匹配對應的action,那么,我們通過瀏覽器訪問http://localhost:21528/api/order這個路徑的時候,瀏覽器默認通過url訪問的都是get請求,于是webapi的路由引擎就會去找order這個控制器里面的get請求的方法,由于沒有參數,所以自動匹配到了無參數的get請求→getall()方法,所以請求成功!

當然,webapi也支持mvc里面的路由機制,但restful風格的服務要求請求的url里面不能包含action,所以,在webapi里面是并不提倡使用mvc路由機制的。

這是一個最簡單的例子,下面我們就來詳細看看webapi里面的路由原理以及使用。

二、webapi路由基礎

1、默認路由

上面我們提到了,新建一個webapi服務的時候,會自動在webapiconfig.cs文件里面生成一個默認路由:

?
1
2
3
4
5
config.routes.maphttproute(
   name: "defaultapi",
   routetemplate: "api/{controller}/{id}",
   defaults: new { id = routeparameter.optional }
  );

將maphttproute()方法轉到定義可以,它有四個重載方法:

C# WebApi 路由機制剖析

分別來看看各個參數的作用:

name:"defaultapi"→表示此路由的名稱,這里只需要保證路由名稱不重復就ok了。

routetemplate: "api/{controller}/{id}"→表示路由的url規則,“api”是固定部分,主要用來標識當前請求的url是一個api服務的接口,區別mvc的路由,當然,這里并不是一定要寫成“api”,如果你改成“apiserver”,那么你請求的url里面也需要寫成“apiserver”;“{controller}”是控制器的占位符部分,在真實的url里面,該部分對應的是具體的控制器的名稱,這個和mvc里面一致;“{id}”是參數的占位符部分,表示參數,一般這個參數都會在default里面設置可選。有了這個路由模板約束請求的url,比如:我們請求的url寫成http://localhost:21528/order,那么肯定是找不到對應的路由的,因為“api”這個參數必選。如果請求的url匹配不到對應的路由,則會向客戶端返回一個404的狀態碼。

defaults: new { id = routeparameter.optional }→表示路由的默認值,比如上面的routetemplate,{controller}和{id}部分都可以設置默認值,比如:defaults改成new { controller="order", id = routeparameter.optional },那么我們請求http://localhost:21528/api這個url仍然能訪問到getall()方法。

constraints→表示路由約束,一般是一個約束路由模板的正則表達式。比如:我們加入約束條件 constraints: new { id = @"\d+" } ,這就約束必須要匹配一到多個參數id,那么,我們在ordercontroller里面加入另一個方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ordercontroller : apicontroller
 {
 
  [httpget]
  public object getall()
  {
   return "success";
  }
 
  [httpget]
  public object getbyid(int id)
  {
   return "success" + id ;
  }
 }

我們通過http://localhost:21528/api/order/2來訪問,得到結果:

C# WebApi 路由機制剖析

我們再通過http://localhost:21528/api/order/a來訪問,得到結果:

C# WebApi 路由機制剖析

這個是很好理解的,id的值不匹配正則表達式。

而我們訪問http://localhost:21528/api/order。結果:

C# WebApi 路由機制剖析

竟然連getall()方法都找不到了。這是為什么呢?原來就是這個約束在作怪,正則\d+表示匹配一個或多個數字,所以如果請求的url里面沒有傳數字,則自動匹配不到。所以,如果需要匹配無參的方法,我們把約束改成這樣:constraints: new { id = @"\d*" },這個表示匹配0個或多個數字,再來試試

C# WebApi 路由機制剖析

這樣就ok了。

上述說了那么多都是約束id的,其實你也可以使用表達式去約束controller、action等等,但一般不常用,我們就不做過多講解。

2、自定義路由

上面介紹了這么多,都是關于默認路由原理的介紹。除了默認路由,我們也可以自定義路由,我們將webapiconfig.cs里面改成這樣:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static class webapiconfig
 {
  public static void register(httpconfiguration config)
  {
   // web api 路由
   config.maphttpattributeroutes();
 
   //1.默認路由
   config.routes.maphttproute(
    name: "defaultapi",
    routetemplate: "api/{controller}/{id}",
    defaults: new { id = routeparameter.optional }
   );
 
   //2.自定義路由一:匹配到action
   config.routes.maphttproute(
    name: "actionapi",
    routetemplate: "actionapi/{controller}/{action}/{id}",
    defaults: new { id = routeparameter.optional }
   );
 
   //3.自定義路由二
   config.routes.maphttproute(
    name: "testapi",
    routetemplate: "testapi/{controller}/{ordertype}/{id}",
    defaults: new { ordertype="aa", id = routeparameter.optional }
   );
  }
 }

除了默認路由,我們再加入另外兩個自定義路由規則

2.1、自定義路由一:匹配到action

第一個自定義路由很好理解,和mvc里面的路由機制保持一致,只不過為了區別默認路由,我們將路由模板的前綴改成了“actionapi”。我們通過這個自定義的路由也能找到匹配的方法。

比如我們訪問http://localhost:21528/actionapi/order/getall,得到結果:

C# WebApi 路由機制剖析

通過action的名稱來匹配很好理解,上面的getall()是方法名,webapi會默認它就是action的名稱,如果你想要方法名和action的名稱不一致,你也可以自定義action的名稱,這個可以通過特性actionname來實現,如下:

?
1
2
3
4
5
6
[actionname("testactionname")]
  [httpget]
  public object getbyid(int id)
  {
   return "success" + id ;
  }

測試結果:

C# WebApi 路由機制剖析

之前博主演示參數和返回值的時候都是使用的匹配到action的路由。這種用法和mvc里面保持一致,比較好理解,但是webapi里面并不提倡。

2.2、自定義路由二

第二個自定義路由第一眼看上去是不太好理解的,沒關系,我們先來按照它的路由模板規則使用試試。

C# WebApi 路由機制剖析

通過http://localhost:21528/testapi/order/aa/匹配到getall()方法

C# WebApi 路由機制剖析

通過http://localhost:21528/testapi/order/aa/2匹配到的是getbyid()方法

C# WebApi 路由機制剖析

通過http://localhost:21528/testapi/order/bb/2匹配到的也是getbyid()方法。

什么意思呢?也就是說,只要{ordertype}按照路由規則去配置,都能找到對應的方法。這里的{ordertype}有什么用呢?這個要留在下面介紹特性路由的時候來解釋。

3、路由原理

有了上面的這些理論作為基礎,我們再來分析下webapi里面路由機制的原理以及路由匹配的過程。由于webapi的路由機制和mvc有許多的相似性,所以要想理解webapi的路由機制,有需要搬出來那些asp.net rounting里面的對象。這個過程有點復雜,博主就根據自己的理解,提提一些主要的過程:

1、webapi服務啟動之后,會執行全局配置文件global.asax.cs的protected void application_start(){globalconfiguration.configure(webapiconfig.register);}方法,通過參數委托執行webapiconfig.cs里面的public static void register(httpconfiguration config)這個方法,將所有配置的路由信息添加到httproutecollection對象中(mvc里面可能是routcollection對象)保存起來。這里的httproutcollection對象的實例名是routes,這個很重要,后面要用到。

2、當我們發送請求到webapi服務器的時候,比如我們訪問http://localhost:21528/api/order這個url的時候,請求首先還是會被urlroutingmodule監聽組件截獲,然后,將截獲的請求在routes路由集合中匹配到對應的路由模板(如果匹配不到對應的路由模板,則返回404),得到對應的ihttproute對象。ihttproute對象是routes集合里面匹配到的一個實體。

3、將ihttproute對象交給當前的請求的上下文對象requestcontext處理,根據ihttproute對象里面的url匹配到對應的controller,然后再根據http請求的類型和參數找到對應的action。這樣一個請求就能找到對應的方法了。

這個過程本身是非常復雜的,為了簡化,博主只選擇了最主要的幾個過程。更詳細的路由機制可以參考:。這文章寫得有點深,有興趣的可以看看。

三、webapi路由過程

通過上文路由的過程,我們知道,一個請求過來之后,路由主要需要經歷三個階段

  • 根據請求的url匹配路由模板
  • 找到控制器
  • 找到action

1、根據請求的url匹配路由模板

這點上面已經說了很多了,主要就是路由模板的配置和url的匹配。在此不作過多說明。

2、找到控制器

如果你反編譯路由模塊的代碼,你會發現控制器的選擇主要在ihttpcontrollerselector這個接口的selectcontroller()方法里面處理。

C# WebApi 路由機制剖析

該方法將當前的請求以httprequestmessage對象作為參數傳入,返回httpcontrollerdescriptor對象。這個接口默認由defaulthttpcontrollerselector這個類提供實現

C# WebApi 路由機制剖析

默認實現的方法里面大致的算法機制是:首先在路由字典中找到實際的控制器的名稱(比如“order”),然后在此控制器名稱上面加上字符串“controller”的到請求控制器的全稱(比如“ordercontroller”),最后找到對應的webapi的controller,實例化就得到當前請求的控制器對象。

3、找到action

得到了控制器對象之后,api引擎通過調用ihttpactionselector這個接口的selectaction()方法去匹配action。這個過程主要包括:

  1. 解析當前的http請求,得到請求類型(是get、post、put還是delete)
  2. 如果路由模板配置了{action},則直接取到url里面的action名稱
  3. 解析請求的參數

如果路由模板配置了{action},那么找到對應的action就很簡單,如果沒有配置action,則會首先匹配請求類型(get/post/put/delete等),然后匹配請求參數,找到對應的action。我們看個例子,比如,我們的controller加如下一些方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ordercontroller : apicontroller
 {
  [httpget]
  public ihttpactionresult getall()
  {
   return ok<string>("success");
  }
 
  [httpget]
  public ihttpactionresult getbyid(int id)
  {
   return ok<string>("success" + id );
  }
 
  [httppost]
  public httpresponsemessage postdata(int id)
  {
   return request.createresponse();
  }
 
  [httppost]
  public httpresponsemessage savadata(order order)
  {
   return request.createresponse();
  }
 
  [httpput]
  public ihttpactionresult put(int id)
  {
   return ok();
  }
 
  [httpdelete]
  public ihttpactionresult deletebyid(int id)
  {
   return ok();
  }
 }

匹配action的結果

 

 

url http方法 參數 結果
http://localhost:21528/api/order get none 匹配getall方法
http://localhost:21528/api/order get id 匹配getbyid方法
http://localhost:21528/api/order post order 匹配savadata方法
http://localhost:21528/api/order put id 匹配put方法 
http://localhost:21528/api/order delete id 匹配deletebyid方法 

 

webapi還提供了一個action同時支持多個http方法的請求,使用acceptverbs特性去標記。但博主覺得實際使用并不多,有興趣的可以了解下。

?
1
2
3
4
5
[acceptverbs("get", "post")]
 public ihttpactionresult getbyid(int id)
 {
  return ok<string>("success" + id );
 }

四、webapi特性路由

上面說了這么多都是路由的一些全局配置。并且存在問題:

如果http請求的方法相同(比如都是post請求),并且請求的參數也相同。這個時候似乎就有點不太好辦了,這種情況在實際項目中還是比較多的。比如

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ordercontroller : apicontroller
 {
  //訂單排產
  [httppost]
  public void orderproduct([frombody]string strpostdata)
  {
 
  }
 
  //訂單取消
  [httppost]
  public void ordercancel([frombody]string strpostdata)
  {
   
  }
 
  //訂單刪除
  [httppost]
  public void orderdelete([frombody]string strpostdata)
  {
   
  }
 }

這個時候如果使用我們上面講的restful風格的路由是解決不了這個問題的。當然,有園友可能就說了,既然這樣,我們在路由模板里面加上“{action}”不就搞定了么!這樣確實可行。但還是那句話,不提倡。我們來看看如何使用特性路由解決這個問題。

1、啟動特性路由

如果要使用特性路由,首先在webapiconfig.cs的register方法里面必須先啟用特性路由:

?
1
2
3
4
5
6
7
8
9
10
11
12
public static void register(httpconfiguration config)
  {
   // 啟用web api特性路由
   config.maphttpattributeroutes();
 
   //1.默認路由
   config.routes.maphttproute(
    name: "defaultapi",
    routetemplate: "api/{controller}/{id}",
    defaults: new { id = routeparameter.optional }
   );
  }

一般情況下,當我們新建一個webapi項目的時候,會自動在register方法里面加上這句話。

2、最簡單的特性路由

我們在ordercontroller這個控制器里面加這個action

?
1
2
3
4
5
6
[route("order/savedata")]
  [httppost]
  public httpresponsemessage savadata(order order)
  {
   return request.createresponse();
  }

然后我們通過web里面的ajax調用

?
1
2
3
4
5
6
7
8
9
10
$(function () {
 $.ajax({
  type: 'post',
  url: 'http://localhost:21528/order/savedata',
  data: { id: 2, no:"aaa"},
  success: function (data, status) {
   alert(data);
  }
 });
});

得到結果:

C# WebApi 路由機制剖析

當然,有人可能就有疑義了,這個特性路由的作用和“{action}”的作用一樣嘛,其實不然,如果這里改成[route("test/attrroute")],然后請求的url換成http://localhost:21528/test/attrroute,一樣能找到對應的action。

C# WebApi 路由機制剖析

特性路由的目的是為了解決我們公共路由模板引擎解決不了的問題。一個action定義了特性路由之后,就能通過特性路由上面的路由規則找到。

3、帶參數的特性路由

特性路由的規則可以使用“{}”占位符動態傳遞參數,比如我們有這樣一個特性路由

?
1
2
3
4
5
6
[route("ordertype/{id}/order")]
  [httpget]
  public ihttpactionresult getbyid(int id)
  {
   return ok<string>("success" + id );
  }

在瀏覽器里面調用

C# WebApi 路由機制剖析

調用成功。到此,我們就能看懂本文最開始那個看似“怪異”的路由→/api/user/1/detail這個了。

4、參數的約束和默認值

?
1
2
3
4
5
6
[route("api/order/{id:int=3}/ordertype")]
  [httpget]
  public ihttpactionresult getbyid(int id)
  {
   return ok<string>("success" + id );
  }

這里約束可變部分{id}的取值必須是int類型。并且默認值是3.

看看效果

C# WebApi 路由機制剖析

C# WebApi 路由機制剖析

不滿足約束條件,則直接返回404。

5、路由前綴

在正式項目中,同一個控制器的所有的action的所有特性路由標識一個相同的前綴,這種做法并非必須,但這樣能夠增加url的可讀性。一般的做法是在控制器上面使用特性[routeprefix]來標識。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[routeprefix("api/order")]
 public class ordercontroller : apicontroller
 {
  [route("")]
  [httpget]
  public ihttpactionresult getall()
  {
   return ok<string>("success");
  }
 
  [route("{id:int}")]
  [httpget]
  public ihttpactionresult getbyid(int id)
  {
   return ok<string>("success" + id );
  }
 
  [route("postdata")]
  [httppost]
  public httpresponsemessage postdata(int id)
  {
   return request.createresponse();
  }
 }

那么這個這個控制器的action的時候,都需要/api/order開頭,后面接上action特性路由的規則。

五、第一個restful風格的webapi服務

通過以上,我們就可以構造一個restful風格的webapi服務。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[routeprefix("api/attrorder")]
 public class ordercontroller : apicontroller
 {
  [route("")]
  [httpget]
  public ihttpactionresult getall()
  {
   return ok<string>("success");
  }
 
  [route("{id:int=3}/orderdetailbyid")]
  [httpget]
  public ihttpactionresult getbyid(int id)
  {
   return ok<string>("success" + id );
  }
 
  [route("{no}/orderdetailbyno")]
  [httpget]
  public ihttpactionresult getbyno(string no)
  {
   return ok<string>("success" + no);
  }
 
  [route("{name}/orderdetailbyname")]
  [httpget]
  public ihttpactionresult getbyname(string name)
  {
   return ok<string>("success" + name);
  }
 
  [route("postdata")]
  [httppost]
  public httpresponsemessage postdata(int id)
  {
   return request.createresponse();
  }
 
  [route("test/attrroute")]
  [httppost]
  public httpresponsemessage savadata(order order)
  {
   return request.createresponse();
  }
 }

得到結果

C# WebApi 路由機制剖析

六、總結

整了這么久終于整完了。如果你覺得本文對你有幫助,請幫忙博主推薦,您的支持是博主最大的動力!

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://www.cnblogs.com/landeanfen/p/5501490.html

延伸 · 閱讀

精彩推薦
  • C#C#通過KD樹進行距離最近點的查找

    C#通過KD樹進行距離最近點的查找

    這篇文章主要為大家詳細介紹了C#通過KD樹進行距離最近點的查找,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    帆帆帆6112022-01-22
  • C#C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題實例

    C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題實例

    這篇文章主要介紹了C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題,簡單描述了訪問者模式的定義并結合具體實例形式分析了C#使用訪問者模式解決長...

    GhostRider9502022-01-21
  • C#C#實現XML文件讀取

    C#實現XML文件讀取

    這篇文章主要為大家詳細介紹了C#實現XML文件讀取的相關代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    Just_for_Myself6702022-02-22
  • C#WPF 自定義雷達圖開發實例教程

    WPF 自定義雷達圖開發實例教程

    這篇文章主要介紹了WPF 自定義雷達圖開發實例教程,本文介紹的非常詳細,具有參考借鑒價值,需要的朋友可以參考下...

    WinterFish13112021-12-06
  • C#深入解析C#中的交錯數組與隱式類型的數組

    深入解析C#中的交錯數組與隱式類型的數組

    這篇文章主要介紹了深入解析C#中的交錯數組與隱式類型的數組,隱式類型的數組通常與匿名類型以及對象初始值設定項和集合初始值設定項一起使用,需要的...

    C#教程網6172021-11-09
  • C#C#裁剪,縮放,清晰度,水印處理操作示例

    C#裁剪,縮放,清晰度,水印處理操作示例

    這篇文章主要為大家詳細介紹了C#裁剪,縮放,清晰度,水印處理操作示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    吳 劍8332021-12-08
  • C#Unity3D實現虛擬按鈕控制人物移動效果

    Unity3D實現虛擬按鈕控制人物移動效果

    這篇文章主要為大家詳細介紹了Unity3D實現虛擬按鈕控制人物移動效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一...

    shenqingyu060520232410972022-03-11
  • C#C# 實現對PPT文檔加密、解密及重置密碼的操作方法

    C# 實現對PPT文檔加密、解密及重置密碼的操作方法

    這篇文章主要介紹了C# 實現對PPT文檔加密、解密及重置密碼的操作方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下...

    E-iceblue5012022-02-12
主站蜘蛛池模板: www.羞羞答答| 天天翘 | 久久xxxx | 美女靠逼免费视频 | 9re视频这里只有精品 | 非洲特级特黄aa大片 | 亚洲乱亚洲23p女 | 日韩精品亚洲一级在线观看 | 日本大乳护士的引诱图片 | 亚洲欧美日韩综合一区久久 | 97精品国产自在现线免费观看 | 99精品国产在现线免费 | 久久人妻熟女中文字幕AV蜜芽 | 男人肌肌捅女人肌肌 | 妹妹你插的我好爽 | caopren免费视频国产 | а天堂中文最新版在线官网视频 | 51国产| 隔壁老王国产在线精品 | 久久久久久88色偷偷 | www91在线观看 | 国产精品视频人人做人人爱 | 猫影视tv接口 | 久久囯产精品777蜜桃传媒 | 国产欧美日韩精品高清二区综合区 | 精品亚洲午夜久久久久 | 亚洲AV无码专区国产精品麻豆 | 日本不卡1卡2卡三卡网站二百 | 午夜国产精品福利在线观看 | 国产精品亚洲一区二区久久 | 久久这里只有精品视频e | 日本动漫黄网站在线观看 | 日韩成人免费 | 99福利视频导航 | 97爱sese | 极品奶妈漫画 | 国产乱子伦在线观看不卡 | 日本xxxxxx片免费播放18 | 亚洲欧美午夜 | 好大用力深一点 | 天天操天天爽天天射 |