精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

如何讓你的 Express 飛起來

開發
本文阿寶哥將以 Github 上的 OvernightJS 開源項目為例,來介紹一下如何使用 TypeScript 裝飾器來裝飾 Express,從而讓你的 Express 好用得飛起來。

接下來本文的重心將圍繞 裝飾器 的應用展開,不過在分析裝飾器在 OvernightJS 的應用之前,阿寶哥先來簡單介紹一下 OvernightJS。

一、OvernightJS 簡介 

  1. TypeScript decorators for the ExpressJS Server. 

OvernightJS 是一個簡單的庫,用于為要調用 Express 路由的方法添加 TypeScript 裝飾器。此外,該項目還包含了用于管理 json-web-token 和打印日志的包。

[[344477]]

1.1 OvernightJS 特性
OvernightJS 并不是為了替代 Express,如果你之前已經掌握了 Express,那你就可以快速地學會它。OvernightJS 為開發者提供了以下特性:

  • 使用 @Controller 裝飾器定義基礎路由;
  • 提供了把類方法轉化為 Express 路由的裝飾器(比如 @Get,@Put,@Post,@Delete);
  • 提供了用于處理中間件的 @Middleware 和 @ClassMiddleware 裝飾器;
  • 提供了用于處理異常的 @ErrorMiddleware 裝飾器;
  • 提供了 @Wrapper 和 @ClassWrapper 裝飾器用于包裝函數;
  • 通過 @ChildControllers 裝飾器支持子控制器。

出于篇幅考慮,阿寶哥只介紹了 OvernightJS 與裝飾器相關的部分特性。了解完這些特性,我們來快速體驗一下 OvernightJS。

1.2 OvernightJS 入門
1.2.1 初始化項目
首先新建一個 overnight-quickstart 項目,然后使用 npm init -y 命令初始化項目,然后在命令行中輸入以下命令來安裝項目依賴包:

  1. $ npm i @overnightjs/core express -S 

在 Express 項目中要集成 TypeScript 很簡單,只需安裝 typescript 這個包就可以了。但為了在開發階段能夠在命令行直接運行使用 TypeScript 開發的服務器,我們還需要安裝 ts-node 這個包。要安裝這兩個包,我們只需在命令行中輸入以下命令:

  1. $ npm i typescript ts-node -D 

1.2.2 為 Node.js 和 Express 安裝聲明文件
聲明文件是預定義的模塊,用于告訴 TypeScript 編譯器的 JavaScript 值的形狀。類型聲明通常包含在擴展名為 .d.ts 的文件中。這些聲明文件可用于所有最初用 JavaScript 而非 TypeScript 編寫的庫。

幸運的是,我們不需要重頭開始為 Node.js 和 Express 定義聲明文件,因為在 Github 上有一個名為 DefinitelyTyped 項目已經為我們提供了現成的聲明文件。

要安裝 Node.js 和 Express 對應的聲明文件,我們只需要在命令行執行以下命令就可以了:

  1. $ npm i @types/node @types/express -D 

該命令成功執行之后,package.json 中的 devDependencies 屬性就會新增 Node.js 和 Express 對應的依賴包版本信息:

  1.   "devDependencies": { 
  2.      "@types/express""^4.17.8"
  3.      "@types/node""^14.11.2"
  4.      "ts-node""^9.0.0"
  5.      "typescript""^4.0.3" 
  6.   } 

1.2.3 初始化 TypeScript 配置文件
為了能夠靈活地配置 TypeScript 項目,我們還需要為本項目生成 TypeScript 配置文件,在命令行輸入 tsc --init 之后,項目中就會自動創建一個 tsconfig.json 的文件。對于本項目來說,我們將使用以下配置項:

  1.   "compilerOptions": { 
  2.     "target""es6"
  3.     "module""commonjs"
  4.     "rootDir""./src"
  5.     "outDir""./build"
  6.     "esModuleInterop"true
  7.     "experimentalDecorators"true
  8.     "strict"true 
  9.   } 

1.2.4 創建簡單的 Web 服務器
在創建簡單的 Web 服務器之前,我們先來初始化項目的目錄結構。首先在項目的根目錄下創建一個 src 目錄及 controllers 子目錄:

  1. ├── src 
  2. │   ├── controllers 
  3. │   │   └── UserController.ts 
  4. │   └── index.ts 

接著新建 UserController.ts 和 index.ts 這兩個文件并分別輸入以下內容:

UserController.ts

  1. import { Controller, Get } from "@overnightjs/core"
  2. import { Request, Response } from "express"
  3.  
  4. @Controller("api/users"
  5. export class UserController { 
  6.   @Get(""
  7.   private getAll(req: Request, res: Response) { 
  8.     return res.status(200).json({ 
  9.       message: "成功獲取所有用戶"
  10.     }); 
  11.   } 

index.ts

  1. import { Server } from "@overnightjs/core"
  2. import { UserController } from "./controllers/UserController"
  3.  
  4. const PORT = 3000; 
  5.  
  6. export class SampleServer extends Server { 
  7.   constructor() { 
  8.     super(process.env.NODE_ENV === "development"); 
  9.     this.setupControllers(); 
  10.   } 
  11.  
  12.   private setupControllers(): void { 
  13.     const userController = new UserController(); 
  14.     super.addControllers([userController]); 
  15.   } 
  16.  
  17.   public start(port: number): void { 
  18.     this.app.listen(port, () => { 
  19.       console.log(`⚡️[server]: Server is running at http://localhost:${PORT}`); 
  20.     }); 
  21.   } 
  22.  
  23. const sampleServer = new SampleServer(); 
  24. sampleServer.start(PORT); 

完成上述步驟之后,我們在項目的 package.json 中添加一個 start 命令來啟動項目:

  1.   "scripts": { 
  2.     "start""ts-node ./src/index.ts" 
  3.   }, 

添加完 start 命令,我們就可以在命令行中通過 npm start 來啟動 Web 服務器了。當服務器成功啟動之后,命令行會輸出以下消息:

  1. > ts-node ./src/index.ts 
  2.  
  3. ⚡️[server]: Server is running at http://localhost:3000 

接著我們打開瀏覽器訪問 http://localhost:3000/api/users 這個地址,你就會看到 {"message":"成功獲取所有用戶"} 這個信息。

1.2.5 安裝 nodemon
為了方便后續的開發,我們還需要安裝一個第三方包 nodemon。對于寫過 Node.js 應用的小伙伴來說,對 nodemon 這個包應該不會陌生。nodemon 這個包會自動檢測目錄中文件的更改,當發現文件異動時,會自動重啟 Node.js 應用程序。

同樣,我們在命令行執行以下命令來安裝它:

  1. $ npm i nodemon -D 

安裝完成后,我們需要更新一下前面已經創建的 start 命令:

  1.   "scripts": { 
  2.     "start""nodemon ./src/index.ts" 
  3.   } 

好的,現在我們已經知道如何使用 OvernightJS 來開發一個簡單的 Web 服務器。接下來,阿寶哥將帶大家一起來分析 OvernightJS 是如何使用 TypeScript 裝飾器實現上述的功能。

二、OvernightJS 原理分析
在分析前面示例中 @Controller 和 @Get 裝飾器原理前,我們先來看一下直接使用 Express 如何實現同樣的功能:

  1. import express, { Router, Request, Response } from "express"
  2. const app = express(); 
  3.  
  4. const PORT = 3000; 
  5. class UserController { 
  6.   public getAll(req: Request, res: Response) { 
  7.     return res.status(200).json({ 
  8.       message: "成功獲取所有用戶"
  9.     }); 
  10.   } 
  11.  
  12. const userRouter = Router(); 
  13. const userCtrl = new UserController(); 
  14. userRouter.get("/", userCtrl.getAll); 
  15.  
  16. app.use("/api/users", userRouter); 
  17.  
  18. app.listen(PORT, () => { 
  19.   console.log(`⚡️[server]: Server is running at http://localhost:${PORT}`); 
  20. }); 

在以上代碼中,我們先通過調用 Router 方法創建了一個 userRouter 對象,然后進行相關路由的配置,接著使用 app.use 方法應用 userRouter 路由。下面我們用一張圖來直觀感受一下 OvernightJS 與 Express 在使用上的差異:

通過以上對比可知,利用 OvernightJS 提供的裝飾器,可以讓我們開發起來更加便捷。但大家要記住 OvernightJS 底層還是基于 Express,其內部最終還是通過 Express 提供的 API 來處理路由。

接下來為了能更好理解后續的內容,我們先來簡單回顧一下 TypeScript 裝飾器。

2.1 TypeScript 裝飾器簡介
裝飾器是一個表達式,該表達式執行后,會返回一個函數。在 TypeScript 中裝飾器可以分為以下 4 類:

需要注意的是,若要啟用實驗性的裝飾器特性,你必須在命令行或 tsconfig.json 里啟用 experimentalDecorators 編譯器選項:

命令行:

  1. tsc --target ES5 --experimentalDecorators 

tsconfig.json:

  1.   "compilerOptions": { 
  2.      "experimentalDecorators"true 
  3.    } 

了解完 TypeScript 裝飾器的分類,我們來開始分析 OvernightJS 框架中提供的裝飾器。

2.2 @Controller 裝飾器
在前面創建的簡單 Web 服務器中,我們通過以下方式來使用 @Controller 裝飾器:

  1. @Controller("api/users"
  2. export class UserController {} 

很明顯該裝飾器應用在 UserController 類上,它屬于類裝飾器。OvernightJS 的項目結構很簡單,我們可以很容易找到 @Controller 裝飾器的定義:

  1. // src/core/lib/decorators/class.ts 
  2. export function Controller(path: string): ClassDecorator { 
  3.   return <TFunction extends Function>(target: TFunction): void => { 
  4.     addBasePathToClassMetadata(target.prototype, "/" + path); 
  5.   }; 

通過觀察以上代碼可知,Controller 函數是一個裝飾器工廠,即調用該工廠方法之后會返回一個 ClassDecorator 對象。在 ClassDecorator 內部,會繼續調用 addBasePathToClassMetadata 方法,把基礎路徑添加到類的元數據中:

  1. // src/core/lib/decorators/class.ts 
  2. export function addBasePathToClassMetadata(target: Object, basePath: string): void { 
  3.   let metadata: IClassMetadata | undefined = Reflect.getOwnMetadata(classMetadataKey, target); 
  4.   if (!metadata) { 
  5.       metadata = {}; 
  6.   } 
  7.   metadata.basePath = basePath; 
  8.   Reflect.defineMetadata(classMetadataKey, metadata, target); 

addBasePathToClassMetadata 函數的實現很簡單,主要是利用 Reflect API 實現元數據的存取操作。在以上代碼中,會先獲取 target 對象上已保存的 metadata 對象,如果不存在的話,會創建一個空的對象,然后把參數 basePath 的值添加該對象的 basePath 屬性中,元數據設置完成后,在通過 Reflect.defineMetadata 方法進行元數據的保存。

下面我們用一張圖來說明一下 @Controller 裝飾器的處理流程:

在 OvernightJS 項目中,所使用的 Reflect API 是來自 reflect-metadata 這個第三方庫。該庫提供了很多 API 用于操作元數據,這里我們只簡單介紹幾個常用的 API:

  1. // define metadata on an object or property 
  2. Reflect.defineMetadata(metadataKey, metadataValue, target); 
  3. Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey); 
  4.  
  5. // check for presence of a metadata key on the prototype chain of an object or property 
  6. let result = Reflect.hasMetadata(metadataKey, target); 
  7. let result = Reflect.hasMetadata(metadataKey, target, propertyKey); 
  8.  
  9. // get metadata value of an own metadata key of an object or property 
  10. let result = Reflect.getOwnMetadata(metadataKey, target); 
  11. let result = Reflect.getOwnMetadata(metadataKey, target, propertyKey); 
  12.  
  13. // get metadata value of a metadata key on the prototype chain of an object or property 
  14. let result = Reflect.getMetadata(metadataKey, target); 
  15. let result = Reflect.getMetadata(metadataKey, target, propertyKey); 
  16.  
  17. // delete metadata from an object or property 
  18. let result = Reflect.deleteMetadata(metadataKey, target); 
  19. let result = Reflect.deleteMetadata(metadataKey, target, propertyKey); 

相信看到這里,可能有一些小伙伴會有疑問,通過 Reflect API 保存的元數據什么時候使用呢?這里我們先記住這個問題,后面我們再來分析它,接下來我們來開始分析 @Get 裝飾器。

2.3 @Get 裝飾器
在前面創建的簡單 Web 服務器中,我們通過以下方式來使用 @Get 裝飾器,該裝飾器用于配置 Get 請求:

  1. export class UserController { 
  2.   @Get(""
  3.   private getAll(req: Request, res: Response) { 
  4.     return res.status(200).json({ 
  5.       message: "成功獲取所有用戶"
  6.     }); 
  7.   } 

@Get 裝飾器應用在 UserController 類的 getAll 方法上,它屬于方法裝飾器。它的定義如下所示:

  1. // src/core/lib/decorators/method.ts 
  2. export function Get(path?: string | RegExp): MethodDecorator & PropertyDecorator { 
  3.   return helperForRoutes(HttpVerb.GET, path); 

與 Controller 函數一樣,Get 函數也是一個裝飾器工廠,調用該函數之后會返回 MethodDecorator & PropertyDecorator 的交叉類型。除了 Get 請求方法之外,常見的 HTTP 請求方法還有 Post、Delete、Put、Patch 和 Head 等。為了統一處理這些請求方法,OvernightJS 內部封裝了一個 helperForRoutes 函數,該函數的具體實現如下:

  1. // src/core/lib/decorators/method.ts 
  2. function helperForRoutes(httpVerb: HttpDecorator, path?: string | RegExp): MethodDecorator & PropertyDecorator { 
  3.   return (target: Object, propertyKey: string | symbol): void => { 
  4.       let newPath: string | RegExp; 
  5.       if (path === undefined) { 
  6.           newPath = ''
  7.       } else if (path instanceof RegExp) { 
  8.           newPath = addForwardSlashToFrontOfRegex(path); 
  9.       } else { // assert (path instanceof string) 
  10.           newPath = '/' + path; 
  11.       } 
  12.       addHttpVerbToMethodMetadata(target, propertyKey, httpVerb, newPath); 
  13.     }; 

觀察以上代碼可知,在 helperForRoutes 方法內部,會繼續調用 addHttpVerbToMethodMetadata 方法把請求方法和請求路徑這些元數據保存起來。

  1. // src/core/lib/decorators/method.ts 
  2. export function addHttpVerbToMethodMetadata(target: Object, metadataKey: any,  
  3.   httpDecorator: HttpDecorator, path: string | RegExp): void { 
  4.     let metadata: IMethodMetadata | undefined = Reflect.getOwnMetadata(metadataKey, target); 
  5.     if (!metadata) { 
  6.         metadata = {}; 
  7.     } 
  8.     if (!metadata.httpRoutes) { 
  9.         metadata.httpRoutes = []; 
  10.     } 
  11.     const newArr: IHttpRoute[] = [{ 
  12.       httpDecorator, 
  13.       path, 
  14.     }]; 
  15.     newArr.push(...metadata.httpRoutes); 
  16.     metadata.httpRoutes = newArr; 
  17.     Reflect.defineMetadata(metadataKey, metadata, target); 

在 addHttpVerbToMethodMetadata 方法中,會先獲取已保存的元數據,如果 metadata 對象不存在則會創建一個空的對象。然后會繼續判斷該對象上是否含有 httpRoutes 屬性,沒有的話會使用 [] 對象來作為該屬性的屬性值。而請求方法和請求路徑這些元數據會以對象的形式保存到數組中,最終在通過 Reflect.defineMetadata 方法進行元數據的保存。

同樣,我們用一張圖來說明一下 @Get 裝飾器的處理流程:

分析完 @Controller 和 @Get 裝飾器,我們已經知道元數據是如何進行保存的。下面我們來回答 “通過 Reflect API 保存的元數據什么時候使用呢?” 這個問題。

2.4 元數據的使用
要搞清楚通過 Reflect API 保存的元數據什么時候使用,我們就需要來回顧一下前面開發的 SampleServer 服務器:

  1. export class SampleServer extends Server { 
  2.   constructor() { 
  3.     super(process.env.NODE_ENV === "development"); 
  4.     this.setupControllers(); 
  5.   } 
  6.  
  7.   private setupControllers(): void { 
  8.     const userController = new UserController(); 
  9.     super.addControllers([userController]); 
  10.   } 
  11.  
  12.   public start(port: number): void { 
  13.     this.app.listen(port, () => { 
  14.       console.log(`⚡️[server]: Server is running at http://localhost:${PORT}`); 
  15.     }); 
  16.   } 
  17.  
  18. const sampleServer = new SampleServer(); 
  19. sampleServer.start(PORT); 

在以上代碼中 SampleServer 類繼承于 OvernightJS 內置的 Server 類,對應的 UML 類圖如下所示:

此外,在 SampleServer 類中我們定義了 setupControllers 和 start 方法,分別用于初始化控制器和啟動服務器。我們在自定義的控制器上使用了 @Controller 和 @Get 裝飾器,因此接下來我們的重點就是分析 setupControllers 方法。該方法的內部實現很簡單,就是手動創建控制器實例,然后調用父類的 addControllers 方法。

下面我們來分析 addControllers 方法,該方法位于 src/core/lib/Server.ts 文件中,具體實現如下:

  1. // src/core/lib/Server.ts 
  2. export class Server { 
  3.   public addControllers( 
  4.     controllers: Controller | Controller[], 
  5.     routerLib?: RouterLib, 
  6.     globalMiddleware?: RequestHandler, 
  7.   ): void { 
  8.        controllers = (controllers instanceof Array) ? controllers : [controllers]; 
  9.        // ① 支持動態設置路由庫 
  10.        const routerLibrary: RouterLib = routerLib || Router;  
  11.        controllers.forEach((controller: Controller) => { 
  12.          if (controller) { 
  13.              // ② 為每個控制器創建對應的路由對象 
  14.              const routerAndPath: IRouterAndPath | null = this.getRouter(routerLibrary, controller); 
  15.              // ③ 注冊路由 
  16.              if (routerAndPath) { 
  17.                   if (globalMiddleware) { 
  18.                       this.app.use(routerAndPath.basePath, globalMiddleware, routerAndPath.router); 
  19.                   } else { 
  20.                       this.app.use(routerAndPath.basePath, routerAndPath.router); 
  21.                   } 
  22.               } 
  23.             } 
  24.         }); 
  25.     } 

addControllers 方法的整個執行過程還是比較清晰,最核心的部分就是 getRouter 方法。在該方法內部就會處理通過裝飾器保存的元數據。其實 getRouter 方法內部還會處理其他裝飾器保存的元數據,簡單起見我們只考慮與 @Controller 和 @Get 裝飾器相關的處理邏輯。

  1. // src/core/lib/Server.ts 
  2. export class Server { 
  3.  private getRouter(routerLibrary: RouterLib, controller: Controller): IRouterAndPath | null { 
  4.         const prototype: any = Object.getPrototypeOf(controller); 
  5.         const classMetadata: IClassMetadata | undefined = Reflect.getOwnMetadata(classMetadataKey, prototype); 
  6.  
  7.         // 省略部分代碼 
  8.         const { basePath, options, ...}: IClassMetadata = classMetadata; 
  9.  
  10.         // ① 基于配置項創建Router對象 
  11.         const router: IRouter = routerLibrary(options); 
  12.  
  13.         // ② 為路由對象添加路徑和請求處理器 
  14.         let members: any = Object.getOwnPropertyNames(controller); 
  15.         members = members.concat(Object.getOwnPropertyNames(prototype)); 
  16.         members.forEach((member: any) => { 
  17.             // ③ 獲取方法中保存的元數據 
  18.             const methodMetadata: IMethodMetadata | undefined = Reflect.getOwnMetadata(member, prototype); 
  19.             if (methodMetadata) { 
  20.                 const { httpRoutes, ...}: IMethodMetadata = methodMetadata; 
  21.                 let callBack: (...args: any[]) => any = (...args: any[]): any => { 
  22.                     return controller[member](...args); 
  23.                 }; 
  24.                 // 省略部分代碼 
  25.                 if (httpRoutes) { // httpRoutes數組中包含了請求的方法和路徑 
  26.                     // ④ 處理控制器類中通過@Get、@Post、@Put或@Delete裝飾器保存的元數據 
  27.                     httpRoutes.forEach((route: IHttpRoute) => { 
  28.                         const { httpDecorator, path }: IHttpRoute = route; 
  29.                         // ⑤ 為router對象設置對應的路由信息 
  30.                         if (middlewares) { 
  31.                             router[httpDecorator](path, middlewares, callBack); 
  32.                         } else { 
  33.                             router[httpDecorator](path, callBack); 
  34.                         } 
  35.                     }); 
  36.                 } 
  37.             } 
  38.         }); 
  39.         return { basePath, router, }; 
  40.     } 

現在我們已經知道 OvernightJS 內部如何利用裝飾器來為控制器類配置路由信息,這里阿寶哥用一張圖來總結 OvernightJS 的工作流程:

在 OvernightJS 內部除了 @Controller、@Get、@Post、@Delete 等裝飾器之外,還提供了用于注冊中間件的 @Middleware 裝飾器及用于設置異常處理中間件的 @ErrorMiddleware 裝飾器。感興趣的小伙伴可以參考一下阿寶哥的學習思路,自行閱讀 OvernightJS 項目的源碼。

希望通過這篇文章,可以讓小伙伴們對裝飾器的應用場景有一些更深刻的理解。如果你還意猶未盡的話,可以閱讀阿寶哥之前寫的 了不起的 IoC 與 DI 這篇文章,該文章介紹了如何利用 TypeScript 裝飾器和 reflect-metadata 這個庫提供的 Reflect API 實現一個 IoC 容器。

三、參考資源

  • Github - overnight
  • expressjs.com

 

 

責任編輯:姜華 來源: 全棧修仙之路
相關推薦

2011-04-13 10:51:58

MATLAB

2021-07-13 07:52:03

SQL面試COUNT(*)

2025-04-15 00:00:00

2019-11-05 10:35:57

SpringBoot調優Java

2025-03-28 03:20:00

MySQL數據庫搜索

2024-06-12 12:28:23

2023-03-01 23:59:23

Java開發

2024-11-25 18:00:00

C#代碼編程

2021-01-04 15:11:57

開發 IDEA代碼

2024-11-27 09:46:34

2013-01-07 09:34:43

CodeLoveBAT

2011-02-25 08:39:11

QFabric數據中心Juniper

2025-09-02 01:35:00

JavaAIMIP

2025-08-04 02:22:00

2025-01-17 09:23:31

2019-03-25 08:05:35

Elasticsear優化集群

2016-01-19 17:03:59

數據中心網絡華為

2011-09-27 13:25:05

Web

2025-05-22 08:04:43

2022-09-02 08:21:24

idea插件
點贊
收藏

51CTO技術棧公眾號

99久久久国产精品免费调教网站 | 亚洲三级网址| 五月天视频一区| 国产日韩欧美二区| 国产这里有精品| 丁香婷婷成人| 欧美视频在线观看 亚洲欧| 精品婷婷色一区二区三区蜜桃| 国产黄色免费观看| 精品国产乱码久久久| 欧美色偷偷大香| 99re6这里有精品热视频| 欧美 日韩 中文字幕| 西西人体一区二区| xxxx性欧美| www国产视频| 日本综合久久| 一区二区理论电影在线观看| 国产一区二区三区四区五区在线| 欧美国产成人精品一区二区三区| 精品毛片免费观看| 日韩免费成人网| 国产精品无码专区av在线播放| 一区二区高清不卡| 久久成人av少妇免费| 午夜精品久久久久久久99黑人| 国产成人精品无码免费看夜聊软件| 97精品国产99久久久久久免费| 亚洲三级在线播放| 久久一区二区三区av| 国产乱码精品一区二区| 亚洲日本欧美| 自拍偷拍亚洲在线| av2014天堂网| 宅男噜噜噜66国产精品免费| 同产精品九九九| 国产精品99久久久久久大便| 色窝窝无码一区二区三区成人网站 | 伊人久久大香线蕉av超碰演员| 亚洲天堂成人在线| 五月天丁香社区| 四虎在线精品| 色吊一区二区三区| 91成人综合网| 黄色在线免费看| 国产日产精品一区| 国产精品日韩一区二区三区 | 国产欧美一区二区三区国产幕精品| 日韩视频免费观看| 最近中文字幕在线mv视频在线| 99a精品视频在线观看| 欧美色视频一区| 国产精品秘入口18禁麻豆免会员| 免费毛片在线看片免费丝瓜视频| 亚洲四区在线观看| 中文字幕欧美日韩一区二区三区 | 久久久久国产精品免费免费搜索| 久久精品五月婷婷| 三级在线观看| 久久久精品免费网站| 久久久99爱| 人人九九精品| 久久精品一级爱片| 日韩精品最新在线观看| av电影在线观看网址| 国产精品久久久久久户外露出| 亚洲激情一区二区三区| 五月天婷婷在线视频| 日韩码欧中文字| av磁力番号网| 国产粉嫩在线观看| 日韩欧美综合在线视频| 国产自偷自偷免费一区| 国产91欧美| 欧美一区二区三区免费观看视频 | 久久精品免费在线观看| 日韩精品国内| 国产区在线看| 亚洲超丰满肉感bbw| 免费黄色福利视频| 欧美123区| 欧美一卡二卡在线观看| 国产在线不卡av| 国产传媒欧美日韩成人精品大片| 中文字幕在线观看日韩| 国产尤物在线播放| 亚洲国产日本| 国产精品福利网站| 精品人妻少妇AV无码专区| 99精品欧美一区二区三区小说| 欧美高清性xxxxhd| 美女国产在线| 午夜精品久久久久久久| 久久久精品麻豆| 中文字幕一区二区三区中文字幕| 亚洲精品中文字| 999精品在线视频| 亚洲欧洲综合| 国产精品欧美一区二区| 黑人精品一区二区| 中文字幕免费在线观看视频一区| 狠狠精品干练久久久无码中文字幕 | 欧美另类99xxxxx| 91国产丝袜播放在线| 久久精品久久久精品美女| 国产欧美欧洲| 麻豆最新免费在线视频| 精品国产91久久久久久| 五月天视频在线观看| 精品三级av| 久久久精品一区| 亚洲欧美日韩激情| 成人小视频在线| 亚洲一二三区精品| 日本在线啊啊| 日韩女优av电影| av资源在线免费观看| 国产欧美综合一区二区三区| 91青草视频久久| 超碰免费在线观看| 日韩欧美aaa| 亚洲啪av永久无码精品放毛片 | 国产精品国三级国产av| 欧美成人免费全部网站| 亚洲精品福利资源站| 中文字幕资源站| 奇米影视在线99精品| 精品久久久久久亚洲| 在线观看中文| 这里只有精品电影| 99在线视频免费| 免费永久网站黄欧美| 国产精品久久久久久久久婷婷| 视频一区二区三区不卡| 欧美综合欧美视频| 一级片视频免费看| 久久成人国产| 久久久久综合一区二区三区| 高清电影在线免费观看| 欧美成人r级一区二区三区| 免费三级在线观看| 国产自产高清不卡| 特级黄色录像片| 精品视频一区二区三区在线观看| x99av成人免费| 亚洲视频一区在线播放| 欧美国产乱子伦| 亚洲色图久久久| gogogo高清在线观看一区二区| 日韩av电影中文字幕| 青青草超碰在线| 色婷婷av一区二区三区gif | 日韩美女视频19| 午夜免费一级片| 欧美wwwww| 成人免费午夜电影| 91在线中文| 欧美一区二区人人喊爽| avove在线播放| 国产精品1区二区.| 18禁裸男晨勃露j毛免费观看| 日韩欧美中文字幕一区二区三区 | 美女高潮视频在线看| 亚洲国产毛片完整版| 国产又色又爽又黄的| 久久久亚洲午夜电影| 国产精品成人久久电影| 五月综合久久| 国产精品国产亚洲伊人久久| av网在线观看| 欧美一区在线视频| 久久网免费视频| xfplay精品久久| 亚洲乱码国产一区三区| 99久久精品国产亚洲精品 | 亚洲一级片免费| 亚洲91久久| 国产精品日韩欧美一区二区三区| 亚洲小少妇裸体bbw| 在线视频欧美性高潮| 99精品免费观看| 亚洲成a人v欧美综合天堂| 一本色道综合久久欧美日韩精品 | 色哟哟在线观看视频| 伊人久久成人| 日韩欧美亚洲在线| 日韩精品一区二区三区中文在线 | www.成人网.com| 国产免费又粗又猛又爽| 国产精品v日韩精品v欧美精品网站 | 欧美高清一级大片| 日本一区高清| 91麻豆精品国产91久久久久久| 日韩xxx高潮hd| 日本一区二区三级电影在线观看 | 91精品国产自产精品男人的天堂| 2023亚洲男人天堂| 久操视频在线免费播放| 欧美精品一区二区三区蜜桃| 亚洲午夜在线播放| 亚欧色一区w666天堂| 国产三级在线观看完整版| 成人午夜伦理影院| 日本免费观看网站| 亚洲三级影院| 国产精品12p| 国产中文字幕一区二区三区 | 欧美成人福利在线观看| 99在线|亚洲一区二区| 超碰免费在线公开| 亚洲性视频大全| 97人人模人人爽视频一区二区| 性欧美freehd18| 2025国产精品视频| 在线看福利影| 久久久国产在线视频| 久久国产精品高清一区二区三区| 日韩三级av在线播放| 中文字幕av网站| 狠狠做深爱婷婷久久综合一区 | 国产日韩高清一区二区三区在线| 女同性恋一区二区| 日本一二区不卡| 欧美一区二区三区电影在线观看 | 一区在线电影| 久久99视频| 久久99国产精品99久久| 午夜视频在线观看精品中文| 国产一区私人高清影院| 忘忧草在线www成人影院| 91精品国产91久久久久久久久| 中中文字幕av在线| 久久久av一区| 色老头视频在线观看| 一本大道久久加勒比香蕉| 日夜干在线视频| 日韩国产精品一区| 手机看片1024国产| 精品国产一区久久| 亚洲第一页视频| 精品久久久久久久久久久久久久久 | 亚洲一二三四| 7777免费精品视频| 免费成人在线电影| 欧美孕妇与黑人孕交| 色戒汤唯在线观看| 91成品人片a无限观看| 美女视频在线免费| 欧美一区在线直播| 中国字幕a在线看韩国电影| 欧美一区二区三区免费视| 成人免费网站视频| 国产第一区电影| 欧美理论影院| 国产精品日韩专区| 亚洲青青一区| 91免费看网站| 久久久久久毛片免费看| 久久久福利视频| 欧美日韩国产免费观看视频| 亚洲美女搞黄| 一区二区三区在线| www.国产在线视频| 中文亚洲免费| 日本va中文字幕| 老汉av免费一区二区三区| 中文字幕剧情在线观看| 丁香桃色午夜亚洲一区二区三区| jjzzjjzz欧美69巨大| 91在线观看污| 美女av免费看| 一区二区久久久久| 亚洲成熟少妇视频在线观看| 欧美色图免费看| 黄频在线免费观看| 精品视频久久久| 在线免费观看黄色| 欧美精品videos另类日本| 亚洲欧美韩国| 成人黄色av网站| 成人线上播放| 视频一区视频二区视频三区视频四区国产 | 一区二区三区四区视频| 黄色av免费在线| 欧美一级bbbbb性bbbb喷潮片| 久久av影院| 国产一区二区高清不卡| 欧美三级情趣内衣| 毛片在线视频观看| 久久天堂精品| 少妇性l交大片7724com| 久久嫩草精品久久久精品一| 来吧亚洲综合网| 欧美日韩中文字幕日韩欧美| 国产精品一区二区人人爽| 亚洲激情视频在线| 国产精品刘玥久久一区| 69视频在线播放| 综合久久伊人| 欧美一区二区三区在线免费观看 | 欧美国产第一页| 全球最大av网站久久| 国产精品二区在线| 日韩欧美精品综合| 欧美成人xxxxx| 国产精品一区二区你懂的| 久久丫精品忘忧草西安产品| 夜夜爽夜夜爽精品视频| 伊人色综合久久久| 日韩精品一二三四区| 牛牛在线精品视频| 成人h视频在线观看播放| 午夜精品福利影院| www.avtt| 国产精品一二三区在线| 你懂得视频在线观看| 黑人欧美xxxx| 成人午夜免费在线观看| 精品国产一区二区在线| 久久久人成影片一区二区三区在哪下载| 国产成人看片| 欧美久久综合| а 天堂 在线| 国产精品国产三级国产aⅴ入口| 九九九在线观看| 亚洲国产天堂久久国产91 | 国产精品第七十二页| 欧美亚洲大陆| 欧美午夜性视频| 国产69精品一区二区亚洲孕妇| www.99re6| 欧美日韩一级视频| 福利成人在线观看| 国产精品黄色av| 精品在线播放| 日本在线视频www| 久久夜色精品国产噜噜av| 黑人一级大毛片| 亚洲国产天堂久久国产91| 欧美卡一卡二| 国产98在线|日韩| 国内精品美女在线观看| 特级特黄刘亦菲aaa级| 亚洲综合色噜噜狠狠| www香蕉视频| 欧美精品久久一区二区| 6080亚洲理论片在线观看| 97碰在线视频| 不卡视频免费播放| 九九热在线视频播放| 日韩精品视频观看| 第84页国产精品| 日韩精品成人一区二区在线观看| 日本午夜精品一区二区三区电影| 日本成人免费视频| 欧美性生活久久| 久久99精品久久久久久野外| 97久久夜色精品国产九色| 精品电影一区| 国产又爽又黄无码无遮挡在线观看| 欧美性猛交99久久久久99按摩| 蜜桃成人在线视频| 国产日韩在线观看av| 亚洲精品在线观看91| 在线播放第一页| 精品国产福利在线| yw193.com尤物在线| 成人性生交xxxxx网站| 狠色狠色综合久久| 国产成人无码一区二区在线观看| 在线影院国内精品| 黄黄的网站在线观看| 国内视频一区二区| 日韩国产高清在线| 538任你躁在线精品视频网站| 精品福利二区三区| 亚洲成人短视频| 艳母动漫在线免费观看| gogogo免费视频观看亚洲一| 一级特黄免费视频| 欧美猛男性生活免费| 亚洲最好看的视频| 亚洲午夜精品一区| 婷婷综合久久一区二区三区| se在线电影| 不卡一区二区三区视频| 久久电影一区| 波多野结衣亚洲色图| 亚洲欧美制服丝袜| 国产美女视频一区二区| 777久久久精品一区二区三区| 中文字幕一区在线| 亚洲 欧美 激情 另类| 成人午夜在线观看| 国产情侣久久| 波多野结衣亚洲色图| 在线观看精品国产视频| 国产精品国产| 最新国产黄色网址| 欧美日韩中文字幕综合视频|