為了在 Node.js 中 構建 API,我們將使用 Nest.js。它是一個相當靈活的框架,建立在 Express.js 的基礎上,可以讓你在短時間內制作出 Node.js 服務,因為它集成了很多好功能(如完全的類型化支持、依賴注入、模塊管理和更多)。

項目和工具

為了更快地開始工作,Nest.js 附帶了一個很好的 CLI 工具,可以為我們創建項目模板。我們開始用以下幾行代碼生成我們的項目:

npm i -g @nestjs/cli 
nest new project-name

更多的 Nest.js 和它的 CLI

讓我們測試一下,看看到目前為止是否一切正常:

npm run start:dev

添加數據持久層

我們將使用 TypeORM 來管理我們的數據庫架構。TypeORM 的優點是:它可以讓你通過代碼來描述數據實體模型,然后能夠應用和同步這些模型到表結構的數據庫。(這不僅適用于 PostgreSQL 數據庫,還適用于其他數據庫,可以在 TypeORM 文檔中找到支持哪些數據庫)

使用 docker 自動化設置本地 PostgreSQL 數據庫實例。

要在本地實現數據持久性,我們現在需要一個數據庫服務器和一個要連接的數據庫。一種方法是在本地機器上設置一個 PostgreSQL 數據庫服務器,但這樣做不是很好。因為這樣項目與我們的本地數據庫服務器會過于耦合。這意味著如果你和一個團隊一起做一個項目,只要切換機器就要在每臺機器上設置數據庫服務器,或者以某種方式編寫安裝指南等(當你團隊的開發同學有不同的操作系統時,事情變得更加棘手)。
那么我們如何克服這一點呢?讓這個步驟自動化!
我們使用預構建的 PostgreSQL docker 鏡像并將數據庫服務器作為 docker 進程運行。我們可以用幾行 shell 代碼編寫一個完整的設置來讓我們的服務器實例運行并準備一個空的數據庫準備連接。因為它是可復用的,并且設置代碼可以與項目代碼的其余部分一起在源代碼管理中進行管理,這使得團隊中其他開發人員的 “入門” 變得非常簡單。
下面是這個腳本的樣子:

#!/bin/bash
set -e

SERVER="my_database_server";
PW="mysecretpassword";
DB="my_database";

echo "echo stop & remove old docker [$SERVER] and starting new fresh instance of [$SERVER]"
(docker kill $SERVER || :) && \
(docker rm $SERVER || :) && \
docker run --name $SERVER -e POSTGRES_PASSWORD=$PW \
-e PGPASSWORD=$PW \
-p 5432:5432 \
-d postgres

# wait for pg to start
echo "sleep wait for pg-server [$SERVER] to start";
SLEEP 3;

# create the db
echo "CREATE DATABASE $DB ENCODING 'UTF-8';" | docker exec -i $SERVER psql -U postgres
echo "\l" | docker exec -i $SERVER psql -U postgres

讓我們將該命令添加到我們的 package.json 運行腳本中,以便我們可以輕松執行它。

"start:dev:db": "./src/scripts/start-db.sh"

現在我們有了一個可以運行的命令,它會設置數據庫服務器和一個普通的數據庫。
為了使過程更健壯,我們將為 docker 容器使用相同的名稱(腳本中的 $SERVER var),并添加一個額外的檢查:如果有同名的容器正在運行,那么將結束并刪除它以確保干凈狀態。

Nest.js 連接數據庫

就像所有事情一樣,已經有一個 NPM 模塊可以幫助您將 Nest.js 項目掛鉤到您的數據庫。讓我們使用預構建的 NestJS-to-TypeORM 模塊為我們的項目添加 TypeORM 支持。
您可以像這樣添加所需的模塊:

npm install --save @nestjs/typeorm typeorm pg

配置管理

我們可以在 Nest.js 中配置 TypeORM 連接到哪個數據庫服務器,方法是使用 TypeOrmModule。它有一個 forRoot 方法,我們可以傳入配置。我們知道配置在本地開發和生產環境中會有所不同。所以,這個過程在某種程度上必須是通用的,以便它可以在不同運行環境提供不同的配置。我們可以編寫以下配置服務。這個配置類的功能是在我們的 API Server main.ts 啟動之前運行。它可以從環境變量中讀取配置,然后在運行時以只讀方式提供值。為了使 dev 和 prod 靈活,我們將使用 dotenv 模塊。

npm install --save dotenv

有了這個模塊,我們可以在本地開發的項目根目錄中有一個 “.env” 文件來準備配置值,而在生產中,我們可以從生產服務器上的環境變量中讀取值。這是一種非常靈活的方法,還允許您使用一個文件輕松地與團隊中的其他開發人員共享配置。注意:我強烈建議 git 忽略此文件,因為你有可能會將生產環境的賬號密碼放入此文件中,所以你不應把配置文件提交到項目中而造成意外泄露。
這是您的 .env 文件的樣子:

POSTGRES_HOST=127.0.0.1 
POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=mysecretpassword
POSTGRES_DATABASE=my_database
PORT=3000
MODE=DEV
RUN_MIGRATIONS=true

因此,我們的 ConfigService 將作為單例服務運行,在啟動時加載配置值并將它們提供給其他模塊。我們將在服務中包含一個容錯模式。這意味著如果獲取一個不存在的值,它將拋出含義完整的錯誤。這使您的設置更加健壯,因為您將在構建 / 啟動時檢測配置錯誤,而不是在運行時生命周期。這樣您將能夠在部署 / 啟動服務器時盡早地檢測到這一點,而不是在消費者使用您的 api 時才發現問題。
這是您的 ConfigService 的外觀以及我們將其添加到 Nest.js 應用程序模塊的方式:

// app.module.ts

import { Module } from'@nestjs/common';
import { TypeOrmModule } from'@nestjs/typeorm';
import { AppController } from'./app.controller';
import { AppService } from'./app.service';
import { configService } from'./config/config.service';

@Module({
imports: [
TypeOrmModule.forRoot(configService.getTypeOrmConfig())
],
controllers: [AppController],
providers: [AppService],
})
exportclass AppModule { }
// src/config/config.service.ts

import { TypeOrmModuleOptions } from'@nestjs/typeorm';

require('dotenv').config();

class ConfigService {

constructor(private env: { [k: string]: string | undefined }) { }

private getValue(key: string, throwOnMissing = true): string {
const value = this.env[key];
if (!value && throwOnMissing) {
thrownewError(config error - missing env.${key}); } return value; } publicensureValues(keys: string[]) { keys.forEach(k =>this.getValue(k, true)); returnthis; } publicgetPort() { returnthis.getValue('PORT', true); } publicisProduction() { const mode = this.getValue('MODE', false); return mode != 'DEV'; } public getTypeOrmConfig(): TypeOrmModuleOptions { return { type: 'postgres', host: this.getValue('POSTGRES_HOST'), port: parseInt(this.getValue('POSTGRES_PORT')), username: this.getValue('POSTGRES_USER'), password: this.getValue('POSTGRES_PASSWORD'), database: this.getValue('POSTGRES_DATABASE'), entities: ['**/*.entity{.ts,.js}'], migrationsTableName: 'migration', migrations: ['src/migration/*.ts'], cli: { migrationsDir: 'src/migration', }, ssl: this.isProduction(), }; } } const configService = new ConfigService(process.env) .ensureValues([ 'POSTGRES_HOST', 'POSTGRES_PORT', 'POSTGRES_USER', 'POSTGRES_PASSWORD', 'POSTGRES_DATABASE' ]); export { configService };

開發重啟

npm i --save-dev nodemon ts-node

然后在 root 中添加一個帶有調試和 ts-node 支持的 nodemon.json 文件

{ 
"watch": ["src"],
"ext": "ts",
"ignore": ["src/**/*.spec.ts"],
"exec": "node --inspect=127.0\. 0.1:9223 -r ts-node/register -- src/main.ts",
"env": {}
}

最后我們將 package.json 中的 start:dev 腳本更改為:

"start:dev": "nodemon --config nodemon.json",

這樣可以通過 npm run start:dev 來啟動我們的 API-server,在啟動時它應該從 ConfigService 中獲取 .env 對應環境的 values,然后將 typeORM 連接到我們的數據庫,而且它不綁定在我的機器上。

定義和加載數據模型實體

TypeORM 支持自動加載數據模型實體。您可以簡單地將它們全部放在一個文件夾中,并在您的配置中使用一種模式加載它們 —— 我們將我們的放在 model/.entity.ts 中。(見實體的 TypeOrmModuleOptions 中的 ConfigService)

TypeORM 的另一個特性是這些實體模型支持繼承。
例如,如果您希望每個實體都擁有某些數據字段。
例如:自動生成的 uuid id 字段 和 createDateTime 字段,lastChangedDateTime 字段。
注意:這些基類應該是 abstract。
因此,在 TypeORM 中定義數據模型實體將如下所示:

// base.entity.ts

import { PrimaryGeneratedColumn, Column, UpdateDateColumn, CreateDateColumn } from'typeorm';

exportabstractclass BaseEntity {
@PrimaryGeneratedColumn('uuid')
id: string;

@Column({ type: 'boolean', default: true })
isActive: boolean;

@Column({ type: 'boolean', default: false })
isArchived: boolean;

@CreateDateColumn({ type: 'timestamptz', default: () =>'CURRENT_TIMESTAMP' })
createDateTime: Date;

@Column({ type: 'varchar', length: 300 })
createdBy: string;

@UpdateDateColumn({ type: 'timestamptz', default: () =>'CURRENT_TIMESTAMP' })
lastChangedDateTime: Date;

@Column({ type: 'varchar', length: 300 })
lastChangedBy: string;

@Column({ type: 'varchar', length: 300, nullable: true })
internalComment: string | null;
}
// item.entity.ts

import { Entity, Column } from'typeorm';
import { BaseEntity } from'./base.entity';

@Entity({ name: 'item' })
exportclass Item extends BaseEntity {

@Column({ type: 'varchar', length: 300 })
name: string;

@Column({ type: 'varchar', length: 300 })
description: string;
}

在 typeORM 文檔中查找更多支持的數據注釋。
讓我們啟動我們的 API,看看它是否有效。

npm run start:dev:db 
npm run start:dev

實際上我們的數據庫并沒有立即反映我們的數據模型,TypeORM 能夠將您的數據模型同步到數據庫中的表中。數據模型自動同步很好,但也很危險。為什么?在前期開發中,您可能沒有把所有數據實體都整理清楚。因此,您在代碼中更改了實體類, typeORM 會為你自動同步字段, 但是,一旦您的數據庫中有實際數據,后期打算修改字段類型或其他操作時,TypeORM 將通過刪除并重新創建數據庫表來更改數據庫,這意味著你極有可能丟失了表內的數據。當然在生產環境中你應該避免這種意想不到情況發生。
這就是為什么我更喜歡從一開始就直接在代碼中處理數據庫遷移。
這也將幫助您和您的團隊更好地跟蹤和理解數據結構的變化,并迫使您更積極地思考這一點:怎樣做可以幫助您避免生產環境中的破壞性更改和數據丟失。
幸運的是 TypeORM 提供了一個解決方案和 CLI 命令,它為你處理生成 SQL 命令的任務。然后,您可以輕松驗證和測試這些,而無需在后臺使用任何黑魔法。
以下是如何設置 typeORM CLI 的最佳實踐。

1.typeORM CLI 的設置

我們已經在 ConfigService 中添加了所有必要的配置,但是 typeORM CLI 與 ormconfig.json 是同時生效的,所以我們希望與正式環境的 CLI 區分開來。添加一個腳本來編寫配置 json 文件并將其添加到我們的.gitignore -list:

import fs = require('fs');
fs.writeFileSync('ormconfig.json', JSON.stringify(configService.getTypeOrmConfig(), null, 2)
);

添加一個 npm 腳本任務來運行它以及 typeorm:migration:generate 和 typeorm:migration:run 的命令。
像這樣 ormconfig 將在運行 typeORM CLI 命令之前生成。

"pretypeorm": "(rm ormconfig.json || :) && ts-node -r tsconfig-paths/register src/scripts/write-type-orm-config.ts",
"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js",
"typeorm:migration:generate": "npm run typeorm -- migration:generate -n",
"typeorm:migration:run": "npm run typeorm -- migration:run"

2. 創建遷移

現在我們可以運行這個命令來創建一個初始化遷移:

npm run typeorm:migration:generate -- my_init

這會將 typeORM 連接到您的數據庫并生成一個數據庫遷移腳本 my_init.ts(在 typescript 中)并將其放入您項目的遷移文件夾中。
注意:您應該將這些遷移腳本提交到您的源代碼管理中,并將這些文件視為只讀。
如果你想改變一些東西,想法是使用 CLI 命令在頂部添加另一個遷移。

3. 運行遷移

npm run typeorm:migration:run

現在我們擁有了創建和運行遷移所需的所有工具,而無需運行 API 服務器項目,它在開發時為我們提供了很大的靈活性,我們可以隨時重新運行、重新創建和添加它們。然而,在生產或階段環境中,您實際上經常希望在部署之后 / 之后啟動 API 服務器之前自動運行遷移腳本。
為此,您只需添加一個 start.sh 腳本即可。
您還可以添加一個環境變量 RUN_MIGRATIONS=<0|1> 來控制遷移是否應該自動運行。

#!/bin/bash
設置 -e
設置 -x
如果 [ "$RUN_MIGRATIONS" ]; 然后
回顯“正在運行的遷移”;
npm run typeorm:migration:run
fi
回聲“啟動服務器”;
npm run start:prod

調試和數據庫工具

我們通過 API 完成同步數據庫字段工作 – 但我們的數據庫實際上反映了我們的數據模型嗎?
可以通過對 DB 運行一些 CLI 腳本查詢或使用 UI 數據庫管理工具進行快速調試來檢查這一點。
使用 PostgreSQL 數據庫時,我使用 pgAdmin
這是一個非常強大的工具,有一個漂亮的用戶界面。但是,我建議您使用以下工作流程:

我們現在可以看到表在數據庫中創建。1. 我們在項目中定義的項目表。2. 一個遷移表,在這個表中 typeORM 跟蹤已經在這個數據庫上執行了哪個遷移。(注意:您也應該將此表視為只讀,否則 typeORM CLI 會混淆)

添加一些業務邏輯

現在讓我們添加一些業務邏輯。
為了演示,我將添加一個簡單的 endpoint,它將返回表中的數據。
我們使用 Nest.js CLI 添加一個項目控制器和一個項目服務。

nest -- generate controller item
nest -- generate service item

這將為我們生成一些模板,然后我們添加:

// item.service.ts 

import { Injectable } from'@nestjs/common';
import { InjectRepository } from'@nestjs/typeorm';
import { Item } from'../model/item.entity';
import { Repository } from'typeorm';

@Injectable()
exportclass ItemService {
constructor(@InjectRepository(Item) private readonly repo: Repository<Item>) { }

publicasyncgetAll() {
returnawaitthis.repo.find();
}
}
// item.controller.ts

import { Controller, Get } from'@nestjs/common';
import { ItemService } from'./item.service';

@Controller('item')
exportclass ItemController {
constructor(private serv: ItemService) { }

@Get()
publicasyncgetAll() {
returnawaitthis.serv.getAll();
}
}

 然后通過 ItemModule 連接在一起,然后在 AppModule 中導入。

// item.module.ts

import { Module } from'@nestjs/common';
import { TypeOrmModule } from'@nestjs/typeorm';
import { ItemService } from'./item.service';
import { ItemController } from'./item.controller';
import { Item } from'../model/item.entity';

@Module({
imports: [TypeOrmModule.forFeature([Item])],
providers: [ItemService],
controllers: [ItemController],
exports: []
})
exportclass ItemModule { }

 啟動 API 后,curl 試試:

curl localhost:3000/item | jq
[] # << indicating no items in the DB - cool :)

不要暴露你的實體 —— 添加 DTO 和響應

不要通過您的 API 向消費者公開您在持久性上的實際數據模型。
當你用一個數據傳輸對象包裝每個數據實體時,你必須對它做序列化和反序列化。

在內部數據模型(API 到數據庫)和外部模型(API 消費者到 API)之間應該是有區別的。從長遠來看,這將幫助您解耦,令維護變得更容易。

// item.dto.ts

import { ApiModelProperty } from'@nestjs/swagger';
import { IsString, IsUUID, } from'class-validator';
import { Item } from'../model/item.entity';
import { User } from'../user.decorator';

exportclass ItemDTO implements Readonly<ItemDTO> {
@ApiModelProperty({ required: true })
@IsUUID()
id: string;

@ApiModelProperty({ required: true })
@IsString()
name: string;

@ApiModelProperty({ required: true })
@IsString()
description: string;

publicstaticfrom(dto: Partial<ItemDTO>) {
const it = new ItemDTO();
it.id = dto.id;
it.name = dto.name;
it.description = dto.description;
return it;
}

publicstaticfromEntity(entity: Item) {
returnthis.from({
id: entity.id,
name: entity.name,
description: entity.description
});
}

publictoEntity(user: User = null) {
const it = new Item();
it.id = this.id;
it.name = this.name;
it.description = this.description;
it.createDateTime = newDate();
it.createdBy = user ? user.id : null;
it.lastChangedBy = user ? user.id : null;
return it;
}
}

 現在我們可以像這樣簡單地使用 DTO:

// item.controller.ts

@Get()
publicasync getAll(): Promise<ItemDTO[]> {
returnawaitthis.serv.getAll()
}

@Post()
publicasync post(@User() user: User, @Body() dto: ItemDTO): Promise<ItemDTO> {
returnthis.serv.create(dto, user);
}
// item.service.ts

publicasync getAll(): Promise<ItemDTO[]> {
returnawaitthis.repo.find()
.then(items => items.map(e => ItemDTO.fromEntity(e)));
}

publicasync create(dto: ItemDTO, user: User): Promise<ItemDTO> {
returnthis.repo.save(dto.toEntity(user))
.then(e => ItemDTO.fromEntity(e));
}

設置 OpenAPI(Swagger)
DTO 方法還使您能夠從它們生成 API 文檔(openAPI aka swagger docs)。您只需安裝:

npm install --save @nestjs/swagger swagger-ui-express

并在 main.ts 中添加這幾行

// main.ts

asyncfunction bootstrap() {
const app = await NestFactory.create(AppModule);

if (!configService.isProduction()) {

constdocument = SwaggerModule.createDocument(app, new DocumentBuilder()
.setTitle('Item API')
.setDescription('My Item API')
.build());

SwaggerModule.setup('docs', app, document);

}

await app.listen(3000);
}

本文章轉載微信公眾號@騰訊IMWeb前端團隊

熱門推薦
一個賬號試用1000+ API
助力AI無縫鏈接物理世界 · 無需多次注冊
3000+提示詞助力AI大模型
和專業工程師共享工作效率翻倍的秘密
返回頂部
上一篇
Protobuf 二進制解碼與 Buf Reflection API 技術解析
下一篇
利用 AWS Spot 實例與 ECS 自動擴展,將生產環境服務器成本降低三分之二
国内精品久久久久影院日本,日本中文字幕视频,99久久精品99999久久,又粗又大又黄又硬又爽毛片
99精品久久只有精品| 国产高清亚洲一区| 亚洲午夜电影在线观看| 99re热这里只有精品免费视频| 中文字幕一区二区三区在线播放 | 亚洲福利视频一区二区| 欧美一区二区三区视频免费播放| 国产一区 二区 三区一级| 亚洲人成在线播放网站岛国| 亚洲精品在线观看网站| 4438x成人网最大色成网站| 国产自产视频一区二区三区| 天天影视涩香欲综合网| 中文字幕一区三区| 国产偷国产偷精品高清尤物| 制服丝袜av成人在线看| 91亚洲永久精品| 成人激情黄色小说| 国产不卡在线一区| 国产精品99久久久久久似苏梦涵| 久久99国产精品免费网站| 视频一区二区三区中文字幕| 日韩av一区二区三区四区| 亚洲国产日日夜夜| 六月丁香婷婷久久| 国产呦萝稀缺另类资源| 国产91丝袜在线观看| 国产麻豆精品在线| 99国产精品99久久久久久| 一本一本大道香蕉久在线精品| 99久久精品国产麻豆演员表| 99国产精品国产精品久久| 91精品国产入口| 国产精品蜜臀av| 久久久久久日产精品| 亚洲欧洲精品成人久久奇米网| 亚洲精品视频在线观看网站| 日韩在线一二三区| 成人免费毛片app| 69p69国产精品| 在线成人小视频| 中文字幕在线一区免费| 日本人妖一区二区| 欧美色欧美亚洲另类二区| 国产色91在线| 另类综合日韩欧美亚洲| 欧美三区在线观看| 一区二区三区日韩精品视频| 成人免费视频caoporn| 26uuu亚洲综合色| 九九九精品视频| 欧美精品一区二区三区在线| 午夜欧美在线一二页| 日韩一区二区视频| 午夜激情久久久| 91精品国产91综合久久蜜臀| 一区二区三区在线视频观看58| 国产高清精品网站| 欧美国产视频在线| 成av人片一区二区| 国产精品剧情在线亚洲| www.久久精品| 免费在线成人网| 久久蜜桃香蕉精品一区二区三区| 精品亚洲国内自在自线福利| 色婷婷av一区二区三区gif| 国产色婷婷亚洲99精品小说| 国产一区二区三区综合| 中文字幕佐山爱一区二区免费| 日本午夜精品一区二区三区电影| 日韩亚洲欧美一区| 一本到高清视频免费精品| 国产精品麻豆视频| 欧美日韩国产区一| kk眼镜猥琐国模调教系列一区二区| 亚洲女人****多毛耸耸8| 26uuu精品一区二区三区四区在线| 成人性生交大片免费看在线播放 | 日韩精品成人一区二区三区| 日韩视频免费观看高清完整版在线观看 | 中文字幕佐山爱一区二区免费| 欧美一区二区啪啪| 国产很黄免费观看久久| 久久草av在线| 日韩国产成人精品| 日韩精品亚洲一区| 美女在线视频一区| 免费欧美日韩国产三级电影| 蜜臀久久久久久久| 麻豆91免费观看| 精品一区二区三区在线播放视频| 91麻豆精品国产自产在线观看一区| 欧美男女性生活在线直播观看| 91尤物视频在线观看| 99久久婷婷国产| 精品视频免费看| 国产精品一区二区三区四区| 亚洲色图视频网| 国产精品乱码一区二区三区软件| 国产欧美一区二区精品性色| 欧洲色大大久久| 色综合一个色综合亚洲| av成人免费在线观看| 4438亚洲最大| 久久老女人爱爱| 亚洲图片激情小说| 日本大胆欧美人术艺术动态| 毛片不卡一区二区| 成人99免费视频| 在线视频一区二区三区| 懂色av中文一区二区三区| 99久久99久久精品免费观看 | 国产盗摄女厕一区二区三区| 国产九九视频一区二区三区| 91免费视频网| 在线一区二区三区四区| 久久久久久久久久久久电影| 一区二区三区国产精品| 国产精品夜夜爽| 欧美成人综合网站| 一区二区在线观看视频| 国产成人精品免费一区二区| 色综合久久综合| 国产精品久久久久久久久免费桃花| 亚洲成av人片在线观看| 国产欧美中文在线| 极品少妇一区二区三区精品视频| 在线观看网站黄不卡| 亚洲乱码国产乱码精品精98午夜| 国产福利一区在线观看| 日韩三级中文字幕| 激情综合一区二区三区| 久久网站热最新地址| 韩日av一区二区| 国产精品美女久久久久久久久| 国产91综合一区在线观看| 日韩一区二区三区观看| 日本成人在线网站| 亚洲欧美另类在线| 欧美亚洲综合一区| 中文字幕在线观看不卡| 精品一区二区在线视频| av成人动漫在线观看| 亚洲天堂av老司机| 精品国产99国产精品| 国产·精品毛片| 色婷婷综合视频在线观看| 日韩激情视频在线观看| 国产日韩欧美精品电影三级在线| 日本高清免费不卡视频| 亚洲图片欧美色图| 国产精品亲子乱子伦xxxx裸| 在线成人av影院| 成人18视频日本| 蜜桃一区二区三区在线| 午夜视频在线观看一区二区| 国产偷国产偷精品高清尤物| 91精品国产综合久久久久久久久久| 成人三级伦理片| 粉嫩嫩av羞羞动漫久久久| 极品少妇xxxx精品少妇| 天天影视网天天综合色在线播放| 欧美午夜视频网站| 欧美日韩一级片网站| 99re亚洲国产精品| 成人一区在线看| 波多野结衣一区二区三区| 99免费精品视频| 99国产精品久久| 日本道精品一区二区三区| 国产一区二区不卡在线 | 男女激情视频一区| 久久精品国产亚洲一区二区三区 | 国产精品538一区二区在线| 成人h精品动漫一区二区三区| 成人激情动漫在线观看| 色丁香久综合在线久综合在线观看| 日本高清成人免费播放| 欧美日韩视频第一区| 国产色综合一区| 亚洲国产aⅴ天堂久久| 国产a久久麻豆| 日韩精品一区二区三区中文不卡| 色综合天天综合网天天狠天天| 欧美日本国产视频| 久久婷婷综合激情| 日韩综合一区二区| 成人福利在线看| 久久综合久久综合亚洲| 亚洲精品伦理在线| 狂野欧美性猛交blacked| 欧美性猛交一区二区三区精品| 久久日韩粉嫩一区二区三区| 天天影视网天天综合色在线播放| 亚洲成av人片| 99久久精品国产观看| 日本一区二区高清| 粉嫩av一区二区三区| 日韩一区二区免费视频| 国产一区二区视频在线|