精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

国产毛片精品| а√在线中文在线新版| 美腿丝袜在线亚洲一区| 日韩性xxxx爱| 日本黄色大片在线观看| 中文字幕在线官网| 亚洲视频狠狠干| 精品视频第一区| 中文字幕有码视频| 国模一区二区三区| 亚洲色图15p| www.色.com| 在线观看欧美日韩电影| 国产精品久久久久一区二区三区 | www.涩涩爱| 一区三区自拍| 欧美色图在线观看| 日韩av综合在线观看| 麻豆免费在线视频| 91亚洲精品久久久蜜桃网站| 成人免费福利视频| 无码人妻丰满熟妇精品| 国产精品二区影院| 亚洲最大中文字幕| 熟妇人妻久久中文字幕| 精品国产一级| 欧美日韩中文精品| www黄色日本| 性欧美1819sex性高清大胸| 国产女主播视频一区二区| 国产欧美一区二区三区另类精品 | 国产欧美精品一区二区三区| 一级黄色片网站| 久久精品日产第一区二区| 欧美激情按摩在线| 久久久久亚洲av无码专区体验| 精品美女视频| 亚洲美女福利视频网站| 国产精品一区二区人妻喷水| 精品成人18| 欧美日韩中文精品| 日日噜噜噜噜久久久精品毛片| mm视频在线视频| 亚洲男人都懂的| 一区二区在线观看网站| 成人性爱视频在线观看| 国产视频一区二区三区在线观看| 激情五月综合色婷婷一区二区| 国产超碰人人模人人爽人人添| 免费看黄色91| 国产精品视频免费在线观看| 欧美 亚洲 另类 激情 另类| 日本最新不卡在线| 国产精品69久久| 中国女人一级一次看片| 日韩和的一区二区| 国产精品免费网站| 中文字幕av在线免费观看| 蜜桃一区二区三区四区| 国产精品主播视频| 国产乱人乱偷精品视频| 国产综合久久久久久鬼色 | 久热在线中文字幕色999舞| 国产福利视频网站| 综合日韩在线| 高清欧美性猛交xxxx| 99视频在线看| 六月天综合网| 国产精品一区二区三| 一级片在线观看视频| 看电视剧不卡顿的网站| 成人天堂噜噜噜| 精品欧美在线观看| 99国产欧美久久久精品| 青青草原成人| 在线播放毛片| 一区二区三区日韩在线观看| 国产玉足脚交久久欧美| 在线手机中文字幕| 欧美性大战xxxxx久久久| 中文字幕国产免费| 午夜视频在线观看精品中文| 亚洲黄色有码视频| 九九九视频在线观看| 99久久亚洲精品蜜臀| 欧美激情乱人伦| 欧美日韩乱国产| 美国三级日本三级久久99| 97人人模人人爽人人喊38tv| 香蕉国产在线视频| 国产精品国产自产拍在线| 黄色污污在线观看| 色多多在线观看| 欧美军同video69gay| 久久黄色一级视频| 免费看av成人| 欧美大码xxxx| 天码人妻一区二区三区在线看| 免费久久99精品国产| 国产精品swag| 成人免费在线视频网| 亚洲一区二区中文在线| av在线无限看| 成人免费直播在线| 综合久久五月天| 精品91久久久| 国产在线视频不卡二| 久久亚洲免费| 欧美videos另类精品| 欧美午夜影院一区| 波多野结衣影院| 天天射—综合中文网| 日韩av手机在线| 亚洲精品国产suv一区| 国产精品视频一二三区| 欧美精品久久久久久久久久久| 成人午夜一级| 日韩乱码在线视频| 免费无码毛片一区二区app| 免费美女久久99| 欧美一区二区视频在线| bl视频在线免费观看| 在线播放91灌醉迷j高跟美女 | 亚洲欧洲日产国码无码久久99| 亚洲影视资源| 国产亚洲精品久久久久动| 国产乡下妇女做爰毛片| 国产综合久久久久影院| 亚洲精品日韩在线观看| 日本综合字幕| 亚洲国产欧美一区二区丝袜黑人| 婷婷色中文字幕| 久久成人18免费观看| 日本在线播放不卡| 成人美女视频| 日韩精品在线免费观看视频| 日本中文字幕免费观看| 成人午夜免费电影| 8x8ⅹ国产精品一区二区二区| 日本久久一区| 综合欧美国产视频二区| 国产偷人爽久久久久久老妇app| www国产精品av| 黄色网页免费在线观看| 澳门久久精品| 午夜精品福利在线观看| 亚洲精品视频91| 一区二区三区免费看视频| 久久婷婷中文字幕| 婷婷另类小说| 91在线观看免费高清完整版在线观看| 伊人在线视频| 欧美一区二区在线不卡| 日韩精品一区二区亚洲av性色| 久久电影国产免费久久电影 | 中文字幕在线视频播放| 激情婷婷欧美| 好吊色欧美一区二区三区视频| 成人一级福利| 亚洲精品视频中文字幕| www.com亚洲| 欧美激情在线一区二区| 亚洲综合欧美激情| 91精品国产91久久综合| 91超碰在线免费观看| 少女频道在线观看高清| 亚洲国产成人精品一区二区 | 黄av在线播放| 欧美成人a在线| 日韩av一二三区| 国产午夜精品在线观看| 国产三级国产精品国产专区50| 国产精品国内免费一区二区三区| 成人欧美一区二区三区在线湿哒哒 | 免费久久精品视频| 三级在线免费观看| 另类春色校园亚洲| 国产成人综合亚洲| 欧美三级电影一区二区三区| 日韩欧美一区电影| 天堂а√在线中文在线新版| 中文字幕乱码亚洲精品一区| 三日本三级少妇三级99| 国产精品日韩| 一区在线电影| eeuss鲁片一区二区三区| 26uuu亚洲国产精品| av黄色在线观看| 日韩视频永久免费| 日韩一区二区视频在线| 一色桃子久久精品亚洲| 少妇被狂c下部羞羞漫画| 日本vs亚洲vs韩国一区三区二区| 性做爰过程免费播放| 亚洲va久久| 亚洲a∨日韩av高清在线观看| 超碰在线99| 久久中文久久字幕| 黄色在线播放| 日韩天堂在线观看| 亚洲图片欧美日韩| 亚洲午夜电影网| 蜜桃av免费在线观看| av不卡免费电影| 日本中文字幕影院| 久久黄色网页| 日本a视频在线观看| 99久久www免费| 蜜桃av久久久亚洲精品| 日韩一区二区三区精品视频第3页| 热久久这里只有精品| 亚洲h片在线看| 综合网中文字幕| 污污的视频网站在线观看| 91麻豆精品国产91久久久久久| 亚洲国产av一区二区三区| 亚洲黄网站在线观看| 99久久精品久久亚洲精品| 久久久久久久久久电影| 国产chinese中国hdxxxx| 国产在线视频精品一区| 老司机午夜性大片| 久久久国产精品一区二区中文| 国产精品久久久久久久久电影网| 日韩欧美综合| 日韩福利在线| 综合亚洲自拍| 精品一区二区久久久久久久网站| 日韩一区二区三区高清在线观看| 国产日产欧美a一级在线| 欧美一区 二区 三区| 9.1国产丝袜在线观看| 草美女在线观看| 欧美成人午夜影院| 麻豆91在线| 日韩视频免费在线观看| 成人在线免费公开观看视频| 亚洲色图13p| 中文无码日韩欧| 久久久91精品| 在线视频二区| 夜夜嗨av色综合久久久综合网| 天堂91在线| 亚洲精品久久久久国产| 天天操天天操天天操| 欧美精品一区二区高清在线观看| 亚洲爱爱综合网| 日韩欧美一级二级| www.色播.com| 日韩欧美国产综合在线一区二区三区| 国产三级自拍视频| 日韩欧美中文字幕公布| 精品久久久久成人码免费动漫| 日韩视频一区二区三区| 国产wwwxxx| 亚洲成人免费在线视频| 四虎在线视频免费观看| 亚洲精品第一页| 久久米奇亚洲| 中文字幕欧美日韩在线| 一本一道波多野毛片中文在线 | 国产一区二区影视| 一区二区av在线| 男人天堂手机在线| 美女福利精品视频| av在线加勒比| 日本国产精品视频| 激情久久一区二区| 成人网在线免费观看| 都市激情亚洲| 欧美高清一区二区| 久久一区二区三区喷水| 亚洲小视频在线播放| 亚洲高清不卡| 乱子伦视频在线看| 久久草av在线| 18禁一区二区三区| 久久久99免费| 欧美爱爱免费视频| 婷婷综合另类小说色区| 国产裸体美女永久免费无遮挡| 538在线一区二区精品国产| 亚洲第一成人av| 亚洲欧美日韩精品久久奇米色影视 | 国产一区二区视频在线| 国产大尺度视频| 久久精品一区二区三区不卡 | 天天干,夜夜爽| 国产亚洲精品久久久久久牛牛| 毛片在线播放a| 91精品国产91久久| av在线播放一区二区| 国内视频一区二区| 欧美高清视频在线观看mv| 日韩网站在线免费观看| 日日夜夜一区二区| 无码人妻一区二区三区精品视频| 26uuu亚洲婷婷狠狠天堂| 黄视频网站免费看| 色婷婷av一区二区三区gif| 国产夫妻性生活视频| 亚洲视频在线观看| 青青草视频在线免费直播| 日韩av免费看| 成人av综合网| 亚洲精品第一区二区三区| 99精品视频网| 美女又黄又免费的视频| 国产性色一区二区| 奇米影视第四色777| 欧美视频中文字幕| 天天操天天操天天干| 精品中文字幕乱| 91亚洲精品| 蜜桃麻豆91| 激情视频一区二区三区| 手机在线免费毛片| 国产精品色呦呦| 欧美 日韩 精品| 精品国产一区久久| 黄色网页在线播放| 国产精品极品美女在线观看免费| 99精品在免费线中文字幕网站一区| 亚洲福利av| 国产毛片一区| 99久久免费看精品国产一区| 亚洲色图制服丝袜| 在线播放亚洲精品| 国产亚洲欧洲高清| 欧洲精品一区二区三区| 久久精彩视频| 国产精品毛片一区二区三区| 99热这里只有精品2| 亚洲欧美偷拍卡通变态| 国产精品久久久久精| 日韩一级黄色av| 久久夜夜久久| 亚洲 国产 日韩 综合一区| 美女精品在线| 免费黄色在线视频| 色婷婷综合中文久久一本| 天堂av一区二区三区| 久久免费福利视频| 国产精品99久久免费观看| 欧美性潮喷xxxxx免费视频看| 国产寡妇亲子伦一区二区| 免费中文字幕在线| 日韩欧美不卡在线观看视频| 婷婷在线播放| 国产不卡一区二区在线观看 | 国产精品三级久久久久三级| 天堂网一区二区| 在线观看国产精品91| 成人在线观看免费播放| 亚洲精品永久www嫩草| 久久99久久精品| 免费在线观看一级片| 欧美a一区二区| 日韩www视频| 午夜私人影院久久久久| 男人天堂av网| 97国产精品视频| 岛国精品一区| 精品无码国模私拍视频| 91论坛在线播放| 波多野结衣人妻| 日韩在线视频播放| 精品一区二区三区免费看| 国产一区二区三区在线免费| 成人99免费视频| 91美女免费看| 中文字幕亚洲一区二区三区五十路 | 日韩精品手机在线| 亚洲欧美日韩高清| 亚洲国产aⅴ精品一区二区三区| wwwjizzjizzcom| 91麻豆swag| 国产又粗又猛又爽又黄的视频一| 久久国产精品久久国产精品| 动漫av一区| 一区二区三区视频在线观看免费| 亚洲视频一区二区免费在线观看| 精品人妻一区二区三区含羞草| 97在线观看免费高清| 成人vr资源| 亚洲欧洲国产视频| 色噜噜狠狠色综合中国| 国产超级va在线视频| 国产一区视频观看| 秋霞电影网一区二区| 成人免费毛片东京热| 亚洲欧美一区二区三区情侣bbw | 激情视频在线观看| 国产女主播一区二区| 久久国产生活片100| 国产 欧美 日韩 在线| 久久精品国产亚洲7777| 精品无人区一区二区| 污污网站在线观看视频| 欧美性xxxxxxxxx|