久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

asp编程工具_使用ASP.NET Core构建RESTful API的技术指南

發(fā)布時間:2024/9/3 asp.net 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 asp编程工具_使用ASP.NET Core构建RESTful API的技术指南 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

譯者薦語:利用周末的時間,本人拜讀了長沙.NET技術社區(qū)翻譯的技術文章《微軟RESTFul API指南》,打算按照步驟寫一個完整的教程,后來無意中看到了這篇文章,與我要寫的主題有不少相似之處,特意翻譯下來。

原文來自互聯(lián)網(wǎng),由長沙DotNET技術社區(qū)【鄒溪源】翻譯。如譯文侵犯您的版權,請聯(lián)系小編,小編將在24小時內(nèi)刪除。

前方高能。本文共有兩萬字。

一步一步的構建整潔、可維護的RESTful APIs

總覽

RESTful不是一個新名詞。它是一種架構風格,這種架構風格使用Web服務從客戶端應用程序接收數(shù)據(jù)和向客戶端應用程序發(fā)送數(shù)據(jù)。其目標是集中不同客戶端應用程序將使用的數(shù)據(jù)。

選擇正確的工具來編寫RESTful服務至關重要,因為我們需要關注可伸縮性,維護,文檔以及所有其他相關方面。在ASP.NET?Core為我們提供了一個功能強大、易于使用的API,使用這些API將很好的實現(xiàn)這個目標。

在本文中,我將向您展示如何使用ASP.NET Core框架為“幾乎”現(xiàn)實世界的場景編寫結構良好的RESTful API。我將詳細介紹常見的模式和策略以簡化開發(fā)過程。

我還將向您展示如何集成通用框架和庫,例如Entity Framework Core和AutoMapper,以提供必要的功能。

先決條件

我希望您了解面向對象的編程概念。

即使我將介紹C#編程語言的許多細節(jié),我還是建議您具有該主題的基本知識。

我還假設您知道什么是REST,HTTP協(xié)議如何工作,什么是API端點以及什么是JSON。這是關于此主題的出色的入門教程。最后,您需要了解關系數(shù)據(jù)庫的工作原理。

要與我一起編碼,您將必須安裝.NET Core 2.2以及Postman(我將用來測試API的工具)。我建議您使用諸如Visual Studio Code之類的代碼編輯器來開發(fā)API。選擇您喜歡的代碼編輯器。如果選擇Visual Studio Code作為您的代碼編輯器,建議您安裝C#擴展以更好地突出顯示代碼。

您可以在本文末尾找到該API的Github的鏈接,以檢查最終結果。

范圍

讓我們?yōu)橐患页芯帉懸粋€虛構的Web API。假設我們必須實現(xiàn)以下范圍:

  • 創(chuàng)建一個RESTful服務,該服務允許客戶端應用程序管理超市的產(chǎn)品目錄。它需要公開端點以創(chuàng)建,讀取,編輯和刪除產(chǎn)品類別,例如乳制品和化妝品,還需要管理這些類別的產(chǎn)品。

  • 對于類別,我們需要存儲其名稱。對于產(chǎn)品,我們需要存儲其名稱,度量單位(例如,按重量測量的產(chǎn)品為KG),包裝中的數(shù)量(例如,如果一包餅干是10,則為10)及其各自的類別。

為了簡化示例,我將不處理庫存產(chǎn)品,產(chǎn)品運輸,安全性和任何其他功能。這個范圍足以向您展示ASP.NET Core的工作方式。

要開發(fā)此服務,我們基本上需要兩個API 端點(譯者注:指控制器):一個用于管理類別,一個用于管理產(chǎn)品。在JSON通訊方面,我們可以認為響應如下:

123456789API endpoint:?/api/categoriesJSON Response (for GET requests):{ [ { "id": 1, "name": "Fruits and Vegetables" }, { "id": 2, "name": "Breads" }, … // Other categories ]}
1234567891011121314151617API endpoint:?/api/productsJSON Response (for GET requests):{ [ { "id": 1, "name": "Sugar", "quantityInPackage": 1, "unitOfMeasurement": "KG" "category": { "id": 3, "name": "Sugar" } }, … // Other products ]}

讓我們開始編寫應用程序。

第1步-創(chuàng)建API

首先,我們必須為Web服務創(chuàng)建文件夾結構,然后我們必須使用.NET CLI工具來構建基本的Web API。打開終端或命令提示符(取決于您使用的操作系統(tǒng)),并依次鍵入以下命令:

12345mkdir src/Supermarket.APIcd src/Supermarket.APIdotnet new webapi

前兩個命令只是為API創(chuàng)建一個新目錄,然后將當前位置更改為新文件夾。最后一個遵循Web API模板生成一個新項目,這是我們正在開發(fā)的應用程序。您可以閱讀有關這些命令和其他項目模板的更多信息,并可以通過檢查此鏈接來生成其他項目模板。
現(xiàn)在,新目錄將具有以下結構:

項目結構

結構概述

ASP.NET Core應用程序由在類中配置的一組中間件(應用程序流水線中的小塊應用程序,用于處理請求和響應)組成Startup。如果您以前已經(jīng)使用過Express.js之類的框架,那么這個概念對您來說并不是什么新鮮事物。

123456789101112131415161718192021222324252627282930313233343536public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseMvc(); } }

當應用程序啟動時,將調(diào)用類中的Main** **方法Program。它使用啟動配置創(chuàng)建默認的Web主機,通過HTTP通過特定端口(默認情況下,HTTP為5000,HTTPS為5001)公開應用程序。

123456789101112131415namespace Supermarket.API { public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup(); } }

看一下文件夾中的ValuesController類Controllers。它公開了API通過路由接收請求時將調(diào)用的方法/api/values。

12345678910111213141516171819202122232425262728293031323334353637383940[Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { // GET api/values [HttpGet] public ActionResult> Get() { return new string[] { "value1", "value2" }; } // GET api/values/5 [HttpGet("{id}")] public ActionResult Get(int id) { return "value"; } // POST api/values [HttpPost] public void Post([FromBody] string value) { } // PUT api/values/5 [HttpPut("{id}")] public void Put(int id, [FromBody] string value) { } // DELETE api/values/5 [HttpDelete("{id}")] public void Delete(int id) { } }

如果您不了解此代碼的某些部分,請不要擔心。在開發(fā)必要的API端點時,我將詳細介紹每一個。現(xiàn)在,只需刪除此類,因為我們不會使用它。

第2步-創(chuàng)建領域模型

我將應用一些設計概念,以使應用程序簡單易維護。

編寫可以由您自己理解和維護的代碼并不難,但是您必須牢記您將成為團隊的一部分。如果您不注意如何編寫代碼,那么結果將是一個龐然大物,這將使您和您的團隊成員頭痛不已。聽起來很極端吧?但是相信我,這就是事實。

衡量好代碼的標準是WTF的頻率。原圖來自smitty42,發(fā)表于filckr。該圖遵循CC-BY-2.0。

在Supermarket.API目錄中,創(chuàng)建一個名為的新文件夾Domain。在新的領域文件夾中,創(chuàng)建另一個名為的文件夾Models。我們必須添加到此文件夾的第一個模型是Category。最初,它將是一個簡單的Plain Old CLR Object(POCO)類。這意味著該類將僅具有描述其基本信息的屬性。

123456789101112using System.Collections.Generic; namespace Supermarket.API.Domain.Models { public class Category { public int Id { get; set; } public string Name { get; set; } public IList Products { get; set; } = new List(); } }

該類具有一個Id**?屬性(用于標識類別)和一個Name屬性。以及一個Products?屬性。最后一個屬性將由Entity Framework Core使用**,大多數(shù)ASP.NET Core應用程序使用ORM將數(shù)據(jù)持久化到數(shù)據(jù)庫中,以映射類別和產(chǎn)品之間的關系。由于類別具有許多相關產(chǎn)品,因此在面向對象的編程方面也具有合理的思維能力。
我們還必須創(chuàng)建產(chǎn)品模型。在同一文件夾中,添加一個新Product類。

1234567891011121314namespace Supermarket.API.Domain.Models { public class Product { public int Id { get; set; } public string Name { get; set; } public short QuantityInPackage { get; set; } public EUnitOfMeasurement UnitOfMeasurement { get; set; } public int CategoryId { get; set; } public Category Category { get; set; } } }

該產(chǎn)品還具有ID和名稱的屬性。屬性QuantityInPackage,它告訴我們一包中有多少個產(chǎn)品單位(請記住應用范圍的餅干示例)和一個UnitOfMeasurement**?屬性,這是表示一個枚舉類型,它表示可能的度量單位的枚舉。最后兩個屬性,CategoryId?**和Category將由ORM用于映射的產(chǎn)品和類別之間的關系。它表明一種產(chǎn)品只有一個類別。

讓我們定義領域模型的最后一部分,EUnitOfMeasurement** **枚舉。

按照慣例,枚舉不需要在名稱前以“ E”開頭,但是在某些庫和框架中,您會發(fā)現(xiàn)此前綴是將枚舉與接口和類區(qū)分開的一種方式。

123456789101112131415161718192021222324252627using System.ComponentModel; namespace Supermarket.API.Domain.Models { public enum EUnitOfMeasurement : byte { [Description("UN")] Unity = 1, [Description("MG")] Milligram = 2, [Description("G")] Gram = 3, [Description("KG")] Kilogram = 4, [Description("L")] Liter = 5 } }

該代碼非常簡單。在這里,我們僅定義了幾種度量單位的可能性,但是,在實際的超市系統(tǒng)中,您可能具有許多其他度量單位,并且可能還有一個單獨的模型。
注意,【Description】特性應用于所有枚舉可能性。特性是一種在C#語言的類,接口,屬性和其他組件上定義元數(shù)據(jù)的方法。在這種情況下,我們將使用它來簡化產(chǎn)品API端點的響應,但是您現(xiàn)在不必關心它。我們待會再回到這里。

我們的基本模型已準備就緒,可以使用。現(xiàn)在,我們可以開始編寫將管理所有類別的API端點。

第3步-類別API

在Controllers文件夾中,添加一個名為的新類CategoriesController。

按照慣例,該文件夾中所有后綴為“ Controller”的類都將成為我們應用程序的控制器。這意味著他們將處理請求和響應。您必須從命名空間【Microsoft.AspNetCore.Mvc】繼承Controller。

命名空間由一組相關的類,接口,枚舉和結構組成。您可以將其視為類似于Java語言模塊或Java?程序包的東西。

新的控制器應通過路由/api/categories做出響應。我們通過Route** **在類名稱上方添加屬性,指定占位符來實現(xiàn)此目的,該占位符表示路由應按照慣例使用不帶控制器后綴的類名稱。

12345678910using Microsoft.AspNetCore.Mvc; namespace Supermarket.API.Controllers { [Route("/api/[controller]")] public class CategoriesController : Controller { } }

讓我們開始處理GET請求。首先,當有人/api/categories通過GET動詞請求數(shù)據(jù)時,API需要返回所有類別。為此,我們可以創(chuàng)建類別服務。
從概念上講,服務基本上是定義用于處理某些業(yè)務邏輯的方法的類或接口。創(chuàng)建用于處理業(yè)務邏輯的服務是許多不同編程語言的一種常見做法,例如身份驗證和授權,付款,復雜的數(shù)據(jù)流,緩存和需要其他服務或模型之間進行某些交互的任務。

使用服務,我們可以將請求和響應處理與完成任務所需的真實邏輯隔離開來。

該服務,我們要創(chuàng)建將首先定義一個單獨的行為,或方法:一個list方法。我們希望該方法返回數(shù)據(jù)庫中所有現(xiàn)有的類別。

為簡單起見,在這篇博客中,我們將不處理數(shù)據(jù)分頁或過濾,(譯者注:基于RESTFul規(guī)范,提供了一套完整的分頁和過濾的規(guī)則)。將來,我將寫一篇文章,展示如何輕松處理這些功能。

為了定義C#(以及其他面向對象的語言,例如Java)中某事物的預期行為,我們定義一個interface。一個接口告訴某些事情應該如何工作,但是沒有實現(xiàn)行為的真實邏輯。邏輯在實現(xiàn)接口的類中實現(xiàn)。如果您不清楚此概念,請不要擔心。一段時間后您將了解它。

在Domain文件夾中,創(chuàng)建一個名為的新目錄Services。在此添加一個名為ICategoryService的接口。按照慣例,所有接口都應以C#中的大寫字母“ I”開頭。定義接口代碼,如下所示:

123456789101112using System.Collections.Generic; using System.Threading.Tasks; using Supermarket.API.Domain.Models; namespace Supermarket.API.Domain.Services { public interface ICategoryService { Task> ListAsync(); } }

該ListAsync方法的實現(xiàn)必須異步返回類別的可枚舉對象。
Task封裝返回的類表示異步。由于必須等待數(shù)據(jù)庫完成操作才能返回數(shù)據(jù),因此我們需要考慮執(zhí)行此過程可能需要一段時間,因此我們需要使用異步方法。另請注意“Async”后綴。這是一個約定,告訴我們的方法應異步執(zhí)行。

我們有很多約定,對嗎?我個人喜歡它,因為它使應用程序易于閱讀,即使你在一家使用.NET技術的公司是新人。

“-好的,我們定義了此接口,但是它什么也沒做。有什么用?”

如果您來自Javascript或其他非強類型語言,則此概念可能看起來很奇怪。

接口使我們能夠從實際實現(xiàn)中抽象出所需的行為。使用稱為依賴注入的機制,我們可以實現(xiàn)這些接口并將它們與其他組件隔離。

基本上,當您使用依賴項注入時,您可以使用接口定義一些行為。然后,創(chuàng)建一個實現(xiàn)該接口的類。最后,將引用從接口綁定到您創(chuàng)建的類。

”-聽起來確實令人困惑。我們不能簡單地創(chuàng)建一個為我們做這些事情的類嗎?”

讓我們繼續(xù)實現(xiàn)我們的API,您將了解為什么使用這種方法。

更改CategoriesController代碼,如下所示:

12345678910111213141516171819202122232425262728using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Supermarket.API.Domain.Models; using Supermarket.API.Domain.Services; namespace Supermarket.API.Controllers { [Route("/api/[controller]")] public class CategoriesController : Controller { private readonly ICategoryService _categoryService; public CategoriesController(ICategoryService categoryService) { _categoryService = categoryService; } [HttpGet] public async Task> GetAllAsync() { var categories = await _categoryService.ListAsync(); return categories; } } }

我已經(jīng)為控制器定義了一個構造函數(shù)(當創(chuàng)建一個類的新實例時會調(diào)用一個構造函數(shù)),并且它接收的實例ICategoryService。這意味著實例可以是任何實現(xiàn)服務接口的實例。我將此實例存儲在一個私有的只讀字段中_categoryService。我們將使用此字段訪問類別服務實現(xiàn)的方法。
順便說一下,下劃線前綴是表示字段的另一個通用約定。特別地,.NET的官方命名約定指南不建議使用此約定,但是這是一種非常普遍的做法,可以避免使用“ this”關鍵字來區(qū)分類字段和局部變量。我個人認為閱讀起來要干凈得多,并且許多框架和庫都使用此約定。

在構造函數(shù)下,我定義了用于處理請求的方法/api/categories。該HttpGet** **屬性告訴ASP.NET Core管道使用該屬性來處理GET請求(可以省略此屬性,但是最好編寫它以便于閱讀)。

該方法使用我們的CategoryService實例列出所有類別,然后將類別返回給客戶端。框架管道將數(shù)據(jù)序列化為JSON對象。IEnumerable類型告訴框架,我們想要返回一個類別的枚舉,而Task類型(使用async關鍵字修飾)告訴管道,這個方法應該異步執(zhí)行。最后,當我們定義一個異步方法時,我們必須使用await關鍵字來處理需要一些時間的任務。

好的,我們定義了API的初始結構。現(xiàn)在,有必要真正實現(xiàn)類別服務。

步驟4-實現(xiàn)類別服務

在API的根文件夾(即Supermarket.API文件夾)中,創(chuàng)建一個名為的新文件夾Services。在這里,我們將放置所有服務實現(xiàn)。在新文件夾中,添加一個名為CategoryService的新類。更改代碼,如下所示:

123456789101112131415using System.Collections.Generic; using System.Threading.Tasks; using Supermarket.API.Domain.Models; using Supermarket.API.Domain.Services; namespace Supermarket.API.Services { public class CategoryService : ICategoryService { public async Task> ListAsync() { } } }

以上只是接口實現(xiàn)的基本代碼,我們暫時仍不處理任何邏輯。讓我們考慮一下列表方法應該如何實現(xiàn)。
我們需要訪問數(shù)據(jù)庫并返回所有類別,然后我們需要將此數(shù)據(jù)返回給客戶端。

服務類不是應該處理數(shù)據(jù)訪問的類。我們將使用一種稱為“倉儲模式”的設計模式,定義倉儲類,用于管理數(shù)據(jù)庫中的數(shù)據(jù)。

在使用倉儲模式時,我們定義了repository 類,該類基本上封裝了處理數(shù)據(jù)訪問的所有邏輯。這些倉儲類使方法可以列出,創(chuàng)建,編輯和刪除給定模型的對象,與操作集合的方式相同。在內(nèi)部,這些方法與數(shù)據(jù)庫對話以執(zhí)行CRUD操作,從而將數(shù)據(jù)庫訪問與應用程序的其余部分隔離開。

我們的服務需要調(diào)用類別倉儲,以獲取列表對象。

從概念上講,服務可以與一個或多個倉儲或其他服務“對話”以執(zhí)行操作。

創(chuàng)建用于處理數(shù)據(jù)訪問邏輯的新定義似乎是多余的,但是您將在一段時間內(nèi)看到將這種邏輯與服務類隔離是非常有利的。

讓我們創(chuàng)建一個倉儲,該倉儲負責與數(shù)據(jù)庫通信,作為持久化保存類別的一種方式。

步驟5-類別倉儲和持久層

在該Domain文件夾內(nèi),創(chuàng)建一個名為的新目錄Repositories。然后,添加一個名為的新接口ICategoryRespository。定義接口如下:

12345678910using System.Collections.Generic; using System.Threading.Tasks; using Supermarket.API.Domain.Models; namespace Supermarket.API.Domain.Repositories { public interface ICategoryRepository { Task> ListAsync(); } }

初始代碼基本上與服務接口的代碼相同。
定義了接口之后,我們可以返回服務類并使用的實例ICategoryRepository返回數(shù)據(jù)來完成實現(xiàn)list方法。

1234567891011121314151617181920212223242526using System.Collections.Generic; using System.Threading.Tasks; using Supermarket.API.Domain.Models; using Supermarket.API.Domain.Repositories; using Supermarket.API.Domain.Services; namespace Supermarket.API.Services { public class CategoryService : ICategoryService { private readonly ICategoryRepository _categoryRepository; public CategoryService(ICategoryRepository categoryRepository) { this._categoryRepository = categoryRepository; } public async Task> ListAsync() { return await _categoryRepository.ListAsync(); } } }

現(xiàn)在,我們必須實現(xiàn)類別倉儲的真實邏輯。在這樣做之前,我們必須考慮如何訪問數(shù)據(jù)庫。順便說一句,我們?nèi)匀粵]有數(shù)據(jù)庫!

我們將使用Entity Framework Core(為簡單起見,我將其稱為EF Core)作為我們的數(shù)據(jù)庫ORM。該框架是ASP.NET Core的默認ORM,并公開了一個友好的API,該API使我們能夠將應用程序的類映射到數(shù)據(jù)庫表。

EF Core還允許我們先設計應用程序,然后根據(jù)我們在代碼中定義的內(nèi)容生成數(shù)據(jù)庫。此技術稱為Code First。我們將使用Code First方法來生成數(shù)據(jù)庫(實際上,在此示例中,我將使用內(nèi)存數(shù)據(jù)庫,但是您可以輕松地將其更改為像SQL Server或MySQL服務器這樣的實例數(shù)據(jù)庫)。

在API的根文件夾中,創(chuàng)建一個名為的新目錄Persistence。此目錄將包含我們訪問數(shù)據(jù)庫所需的所有內(nèi)容,例如倉儲實現(xiàn)。

在新文件夾中,創(chuàng)建一個名為的新目錄Contexts,然后添加一個名為的新類AppDbContext。此類必須繼承DbContext,EF Core通過DBContext用來將您的模型映射到數(shù)據(jù)庫表的類。通過以下方式更改代碼:

123456789101112using Microsoft.EntityFrameworkCore; namespace Supermarket.API.Domain.Persistence.Contexts { public class AppDbContext : DbContext { public AppDbContext(DbContextOptions options) : base(options) { } } }

我們添加到此類的構造函數(shù)負責通過依賴注入將數(shù)據(jù)庫配置傳遞給基類。稍后您將看到其工作原理。
現(xiàn)在,我們必須創(chuàng)建兩個DbSet屬性。這些屬性是將模型映射到數(shù)據(jù)庫表的集合(唯一對象的集合)。

另外,我們必須將模型的屬性映射到相應的列,指定哪些屬性是主鍵,哪些是外鍵,列類型等。我們可以使用稱為Fluent API的功能來覆蓋OnModelCreating方法,以指定數(shù)據(jù)庫映射。更改AppDbContext類,如下所示:

該代碼是如此直觀。

123456789101112131415161718192021222324252627282930313233343536373839404142using Microsoft.EntityFrameworkCore; using Supermarket.API.Domain.Models; namespace Supermarket.API.Persistence.Contexts { public class AppDbContext : DbContext { public DbSet Categories { get; set; } public DbSet Products { get; set; } public AppDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.Entity().ToTable("Categories"); builder.Entity().HasKey(p => p.Id); builder.Entity().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd(); builder.Entity().Property(p => p.Name).IsRequired().HasMaxLength(30); builder.Entity().HasMany(p => p.Products).WithOne(p => p.Category).HasForeignKey(p => p.CategoryId); builder.Entity().HasData ( new Category { Id = 100, Name = "Fruits and Vegetables" }, // Id set manually due to in-memory provider new Category { Id = 101, Name = "Dairy" } ); builder.Entity().ToTable("Products"); builder.Entity().HasKey(p => p.Id); builder.Entity().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd(); builder.Entity().Property(p => p.Name).IsRequired().HasMaxLength(50); builder.Entity().Property(p => p.QuantityInPackage).IsRequired(); builder.Entity().Property(p => p.UnitOfMeasurement).IsRequired(); } } }

我們指定我們的模型應映射到哪些表。此外,我們設置了主鍵,使用該方法HasKey,該表的列,使用Property方法,和一些限制,例如IsRequired,HasMaxLength,和ValueGeneratedOnAdd,這些都是使用FluentApi的方式基于Lamada 表達式語法實現(xiàn)的(鏈式語法)。
看一下下面的代碼:

1234builder.Entity() .HasMany(p => p.Products) .WithOne(p => p.Category) .HasForeignKey(p => p.CategoryId);

在這里,我們指定表之間的關系。我們說一個類別有很多產(chǎn)品,我們設置了將映射此關系的屬性(Products,來自Category類,和Category,來自Product類)。我們還設置了外鍵(CategoryId)。
如果您想學習如何使用EF Core配置一對一和多對多關系,以及如何完整的使用它,請看一下本教程。

還有一種用于通過HasData方法配置種子數(shù)據(jù)的方法:

12345builder.Entity().HasData( new Category { Id = 100, Name = "Fruits and Vegetables" }, new Category { Id = 101, Name = "Dairy" });

默認情況下,在這里我們僅添加兩個示例類別。這對我們完成后進行API的測試來說是非常有必要的。

注意:我們在Id這里手動設置屬性,因為內(nèi)存提供程序的工作機制需要。我將標識符設置為大數(shù)字,以避免自動生成的標識符和種子數(shù)據(jù)之間發(fā)生沖突。

真正的關系數(shù)據(jù)庫提供程序中不存在此限制,因此,例如,如果要使用SQL Server等數(shù)據(jù)庫,則不必指定這些標識符。如果您想了解此行為,請檢查此Github問題。

在實現(xiàn)數(shù)據(jù)庫上下文類之后,我們可以實現(xiàn)類別倉儲。添加一個名為新的文件夾Repositories里面Persistence的文件夾,然后添加一個名為新類BaseRepository。

12345678910111213141516using Supermarket.API.Persistence.Contexts; namespace Supermarket.API.Persistence.Repositories { public abstract class BaseRepository { protected readonly AppDbContext _context; public BaseRepository(AppDbContext context) { _context = context; } } }

此類只是我們所有倉儲都將繼承的抽象類。抽象類是沒有直接實例的類。您必須創(chuàng)建直接類來創(chuàng)建實例。
在BaseRepository接受我們的實例,AppDbContext通過依賴注入暴露了一個受保護的屬性稱為(只能是由子類訪問一個屬性)_context,即可以訪問我們需要處理數(shù)據(jù)庫操作的所有方法。

在相同文件夾中添加一個新類CategoryRepository。現(xiàn)在,我們將真正實現(xiàn)倉儲邏輯:

1234567891011121314151617181920212223using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Supermarket.API.Domain.Models; using Supermarket.API.Domain.Repositories; using Supermarket.API.Persistence.Contexts; namespace Supermarket.API.Persistence.Repositories { public class CategoryRepository : BaseRepository, ICategoryRepository { public CategoryRepository(AppDbContext context) : base(context) { } public async Task> ListAsync() { return await _context.Categories.ToListAsync(); } } }

倉儲繼承BaseRepository和實現(xiàn)ICategoryRepository。
注意實現(xiàn)list方法是很簡單的。我們使用Categories數(shù)據(jù)庫集訪問類別表,然后調(diào)用擴展方法ToListAsync,該方法負責將查詢結果轉換為類別的集合。

EF Core?將我們的方法調(diào)用轉換為SQL查詢,這是最有效的方法。這種方式僅當您調(diào)用將數(shù)據(jù)轉換為集合的方法或使用方法獲取特定數(shù)據(jù)時才執(zhí)行查詢。

現(xiàn)在,我們有了類別控制器,服務和倉儲庫的代碼實現(xiàn)。

我們將關注點分離開來,創(chuàng)建了只執(zhí)行應做的事情的類。

測試應用程序之前的最后一步是使用ASP.NET Core依賴項注入機制將我們的接口綁定到相應的類。

第6步-配置依賴注入

現(xiàn)在是時候讓您最終了解此概念的工作原理了。

在應用程序的根文件夾中,打開Startup類。此類負責在應用程序啟動時配置各種配置。

該ConfigureServices和Configure方法通過框架管道在運行時調(diào)用來配置應用程序應該如何工作,必須使用哪些組件。

打開ConfigureServices方法。在這里,我們只有一行配置應用程序以使用MVC管道,這基本上意味著該應用程序將使用控制器類來處理請求和響應(在這段代碼背后發(fā)生了很多事情,但目前您僅需要知道這些)。

我們可以使用ConfigureServices訪問services參數(shù)的方法來配置我們的依賴項綁定。清理類代碼,刪除所有注釋并按如下所示更改代碼:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Supermarket.API.Domain.Repositories; using Supermarket.API.Domain.Services; using Supermarket.API.Persistence.Contexts; using Supermarket.API.Persistence.Repositories; using Supermarket.API.Services; namespace Supermarket.API { public class Startup { public IConfiguration Configuration { get; } public Startup(IConfiguration configuration) { Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddDbContext(options => { options.UseInMemoryDatabase("supermarket-api-in-memory"); }); services.AddScoped(); services.AddScoped(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseMvc(); } } }

看一下這段代碼:

12345services.AddDbContext(options => { options.UseInMemoryDatabase("supermarket-api-in-memory"); });

在這里,我們配置數(shù)據(jù)庫上下文。我們告訴ASP.NET Core將其AppDbContext與內(nèi)存數(shù)據(jù)庫實現(xiàn)一起使用,該實現(xiàn)由作為參數(shù)傳遞給我們方法的字符串標識。通常,在編寫集成測試時才會使用內(nèi)存數(shù)據(jù)庫,但是為了簡單起見,我在這里使用了內(nèi)存數(shù)據(jù)庫。這樣,我們無需連接到真實的數(shù)據(jù)庫即可測試應用程序。
這些代碼行在內(nèi)部配置我們的數(shù)據(jù)庫上下文,以便使用確定作用域的生存周期進行依賴注入。

scoped生存周期告訴ASP.NET Core管道,每當它需要解析接收AppDbContext作為構造函數(shù)參數(shù)的實例的類時,都應使用該類的相同實例。如果內(nèi)存中沒有實例,則管道將創(chuàng)建一個新實例,并在給定請求期間在需要它的所有類中重用它。這樣,您無需在需要使用時手動創(chuàng)建類實例。

如果你想了解其他有關生命周期的知識,可以閱讀官方文檔。

依賴注入技術為我們提供了許多優(yōu)勢,例如:

  • 代碼可重用性;

  • 更高的生產(chǎn)力,因為當我們不得不更改實現(xiàn)時,我們無需費心去更改您使用該功能的一百個地方;

  • 您可以輕松地測試應用程序,因為我們可以使用mock(類的偽實現(xiàn))隔離必須測試的內(nèi)容,而我們必須將接口作為構造函數(shù)參數(shù)進行傳遞。

  • 當一個類需要通過構造函數(shù)接收更多的依賴關系時,您不必手動更改正在創(chuàng)建實例的所有位置(太贊了!)。

配置數(shù)據(jù)庫上下文之后,我們還將我們的服務和倉儲綁定到相應的類。

123services.AddScoped();services.AddScoped();

在這里,我們還使用了scoped生存周期,因為這些類在內(nèi)部必須使用數(shù)據(jù)庫上下文類。在這種情況下,指定相同的范圍是有意義的。
現(xiàn)在我們配置了依賴綁定,我們必須在Program類上進行一些小的更改,以便數(shù)據(jù)庫正確地初始化種子數(shù)據(jù)。此步驟僅在使用內(nèi)存數(shù)據(jù)庫提供程序時才需要執(zhí)行(請參閱此Github問題以了解原因)。

123456789101112131415161718192021222324252627282930313233343536373839using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Supermarket.API.Persistence.Contexts; namespace Supermarket.API { public class Program { public static void Main(string[] args) { var host = BuildWebHost(args); using(var scope = host.Services.CreateScope()) using(var context = scope.ServiceProvider.GetService()) { context.Database.EnsureCreated(); } host.Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() .Build(); } }

由于我們使用的是內(nèi)存提供程序,因此有必要更改Main方法 添加“ context.Database.EnsureCreated();”代碼以確保在應用程序啟動時將“創(chuàng)建”數(shù)據(jù)庫。沒有此更改,將不會創(chuàng)建我們想要的初始化種子數(shù)據(jù)。
實現(xiàn)了所有基本功能后,就該測試我們的API端點了。

第7步-測試類別

在API根文件夾中打開終端或命令提示符,然后鍵入以下命令:

1dotnet run

上面的命令啟動應用程序。控制臺將顯示類似于以下內(nèi)容的輸出:

123456789101112131415161718192021info: Microsoft.EntityFrameworkCore.Infrastructure[10403]Entity Framework Core 2.2.0-rtm-35687 initialized ‘AppDbContext’ using provider ‘Microsoft.EntityFrameworkCore.InMemory’ with options: StoreName=supermarket-api-in-memoryinfo: Microsoft.EntityFrameworkCore.Update[30100]Saved 2 entities to in-memory store.info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]User profile is available. Using ‘C:\Users\evgomes\AppData\Local\ASP.NET\DataProtection-Keys’ as key repository and Windows DPAPI to encrypt keys at rest.Hosting environment: DevelopmentContent root path: C:\Users\evgomes\Desktop\Tutorials\src\Supermarket.APINow listening on: https://localhost:5001Now listening on: http://localhost:5000Application started. Press Ctrl+C to shut down.

您可以看到調(diào)用了EF Core來初始化數(shù)據(jù)庫。最后幾行顯示應用程序在哪個端口上運行。
打開瀏覽器,然后導航到?http://localhost:5000/api/categories?(或控制臺輸出上顯示的URL)。如果您發(fā)現(xiàn)由于HTTPS導致的安全錯誤,則只需為應用程序添加一個例外。

瀏覽器將顯示以下JSON數(shù)據(jù)作為輸出:

123456789101112[ { "id": 100, "name": "Fruits and Vegetables", "products": [] }, { "id": 101, "name": "Dairy", "products": [] }]

在這里,我們看到配置數(shù)據(jù)庫上下文時添加到數(shù)據(jù)庫的數(shù)據(jù)。此輸出確認我們的代碼正在運行。
您使用很少的代碼行創(chuàng)建了GET API端點,并且由于當前API項目的架構模式,您的代碼結構確實很容易更改。

現(xiàn)在,該向您展示在由于業(yè)務需要而不得不對其進行更改時,更改此代碼有多么容易。

步驟8-創(chuàng)建類別資源

如果您還記得API端點的規(guī)范,您會注意到我們的實際JSON響應還有一個額外的屬性:products數(shù)組。看一下所需響應的示例:

1234567{ [ { "id": 1, "name": "Fruits and Vegetables" }, { "id": 2, "name": "Breads" }, … // Other categories ]}

產(chǎn)品數(shù)組出現(xiàn)在我們當前的JSON響應中,因為我們的Category模型具有Products,EF Core需要的屬性,以正確映射給定類別的產(chǎn)品。
我們不希望在響應中使用此屬性,但是不能更改模型類以排除此屬性。當我們嘗試管理類別數(shù)據(jù)時,這將導致EF Core引發(fā)錯誤,并且也將破壞我們的領域模型設計,因為沒有產(chǎn)品的產(chǎn)品類別沒有意義。

要返回僅包含超級市場類別的標識符和名稱的JSON數(shù)據(jù),我們必須創(chuàng)建一個資源類。

資源類是一種包含將客戶端應用程序和API端點之間進行交換的類型,通常以JSON數(shù)據(jù)的形式出現(xiàn),以表示一些特定信息的類。

來自API端點的所有響應都必須返回資源。

將真實模型表示形式作為響應返回是一種不好的做法,因為它可能包含客戶端應用程序不需要或沒有其權限的信息(例如,用戶模型可以返回用戶密碼的信息) ,這將是一個很大的安全問題)。

我們需要一種資源來僅代表我們的類別,而沒有產(chǎn)品。

現(xiàn)在您知道什么是資源,讓我們實現(xiàn)它。首先,在命令行中按Ctrl + C停止正在運行的應用程序。在應用程序的根文件夾中,創(chuàng)建一個名為Resources的新文件夾。在其中添加一個名為的新類CategoryResource。

12345678namespace Supermarket.API.Resources { public class CategoryResource { public int Id { get; set; } public string Name { get; set; } } }

我們必須將類別服務提供的類別模型集合映射到類別資源集合。
我們將使用一個名為AutoMapper的庫來處理對象之間的映射。AutoMapper是.NET世界中非常流行的庫,并且在許多商業(yè)和開源項目中使用。

在命令行中輸入以下命令,以將AutoMapper添加到我們的應用程序中:

123dotnet add package AutoMapperdotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection

要使用AutoMapper,我們必須做兩件事:

  • 注冊它以進行依賴注入;

  • 創(chuàng)建一個類,該類將告訴AutoMapper如何處理類映射。

首先,打開Startup課程。在該ConfigureServices方法的最后一行之后,添加以下代碼:

1services.AddAutoMapper();

此行處理AutoMapper的所有必需配置,例如注冊它以進行依賴項注入以及在啟動過程中掃描應用程序以配置映射配置文件。
現(xiàn)在,在根目錄中,添加一個名為的新文件夾Mapping,然后添加一個名為的類ModelToResourceProfile。通過以下方式更改代碼:

123456789101112131415using AutoMapper; using Supermarket.API.Domain.Models; using Supermarket.API.Resources; namespace Supermarket.API.Mapping { public class ModelToResourceProfile : Profile { public ModelToResourceProfile() { CreateMap(); } } }

該類繼承Profile了AutoMapper用于檢查我們的映射如何工作的類類型。在構造函數(shù)上,我們在Category模型類和CategoryResource類之間創(chuàng)建一個映射。由于類的屬性具有相同的名稱和類型,因此我們不必為其使用任何特殊的配置。
最后一步包括更改類別控制器以使用AutoMapper處理我們的對象映射。

1234567891011121314151617181920212223242526272829303132333435using System.Collections.Generic; using System.Threading.Tasks; using AutoMapper; using Microsoft.AspNetCore.Mvc; using Supermarket.API.Domain.Models; using Supermarket.API.Domain.Services; using Supermarket.API.Resources; namespace Supermarket.API.Controllers { [Route("/api/[controller]")] public class CategoriesController : Controller { private readonly ICategoryService _categoryService; private readonly IMapper _mapper; public CategoriesController(ICategoryService categoryService, IMapper mapper) { _categoryService = categoryService; _mapper = mapper; } [HttpGet] public async Task> GetAllAsync() { var categories = await _categoryService.ListAsync(); var resources = _mapper.Map, IEnumerable>(categories); return resources; } } }

我更改了構造函數(shù)以接收IMapper實現(xiàn)的實例。您可以使用這些接口方法來使用AutoMapper映射方法。
我還更改了GetAllAsync使用Map方法將類別枚舉映射到資源枚舉的方法。此方法接收我們要映射的類或集合的實例,并通過通用類型定義定義必須映射到什么類型的類或集合。

注意,我們只需將新的依賴項(IMapper)注入構造函數(shù),就可以輕松地更改實現(xiàn),而不必修改服務類或倉儲。

依賴注入使您的應用程序可維護且易于更改,因為您不必中斷所有代碼實現(xiàn)即可添加或刪除功能。

您可能意識到,不僅控制器類,而且所有接收依賴項的類(包括依賴項本身)都會根據(jù)綁定配置自動解析為接收正確的類。

依賴注入如此的Amazing,不是嗎?

現(xiàn)在,使用dotnet run命令再次啟動API,然后轉到http://localhost:5000/api/categories以查看新的JSON響應。

這是您應該看到的響應數(shù)據(jù)

我們已經(jīng)有了GET端點。現(xiàn)在,讓我們?yōu)镻OST(創(chuàng)建)類別創(chuàng)建一個新端點。

第9步-創(chuàng)建新類別

在處理資源創(chuàng)建時,我們必須關心很多事情,例如:

  • 數(shù)據(jù)驗證和數(shù)據(jù)完整性;

  • 授權創(chuàng)建資源;

  • 錯誤處理;

  • 正在記錄。

在本教程中,我不會顯示如何處理身份驗證和授權,但是您可以閱讀JSON Web令牌身份驗證教程,了解如何輕松實現(xiàn)這些功能。

另外,有一個非常流行的框架稱為ASP.NET Identity,該框架提供了有關安全性和用戶注冊的內(nèi)置解決方案,您可以在應用程序中使用它們。它包括與EF Core配合使用的提供程序,例如IdentityDbContext可以使用的內(nèi)置程序。您可以在此處了解更多信息。

讓我們編寫一個HTTP POST端點,該端點將涵蓋其他場景(日志記錄除外,它可以根據(jù)不同的范圍和工具進行更改)。

在創(chuàng)建新端點之前,我們需要一個新資源。此資源會將客戶端應用程序發(fā)送到此端點的數(shù)據(jù)(在本例中為類別名稱)映射到我們應用程序的類。

由于我們正在創(chuàng)建一個新類別,因此我們還沒有ID,這意味著我們需要一種資源來表示僅包含其名稱的類別。

在Resources文件夾中,添加一個新類SaveCategoryResource:

123456789101112using System.ComponentModel.DataAnnotations; namespace Supermarket.API.Resources { public class SaveCategoryResource { [Required] [MaxLength(30)] public string Name { get; set; } } }

注意Name屬性上的Required和MaxLength特性。這些屬性稱為數(shù)據(jù)注釋。ASP.NET Core管道使用此元數(shù)據(jù)來驗證請求和響應。顧名思義,類別名稱是必填項,最大長度為30個字符。
現(xiàn)在,讓我們定義新API端點的形狀。將以下代碼添加到類別控制器:

1234[HttpPost] public async Task PostAsync([FromBody] SaveCategoryResource resource) { }

我們使用HttpPost特性告訴框架這是一個HTTP POST端點。
注意此方法的響應類型Task。控制器類中存在的方法稱為動作,它們具有此簽名,因為在應用程序執(zhí)行動作之后,我們可以返回一個以上的可能結果。

在這種情況下,如果類別名稱無效或出現(xiàn)問題,我們必須返回400代碼(錯誤請求)響應,該響應通常包含一條錯誤消息,客戶端應用程序可以使用該錯誤消息來解決該問題,或者我們可以如果一切正常,則對數(shù)據(jù)進行200次響應(成功)。

可以將多種類型的操作類型用作響應,但是通常,我們可以使用此接口,并且ASP.NET Core將為此使用默認類。

該FromBody屬性告訴ASP.NET Core將請求正文數(shù)據(jù)解析為我們的新資源類。這意味著當包含類別名稱的JSON發(fā)送到我們的應用程序時,框架將自動將其解析為我們的新類。

現(xiàn)在,讓我們實現(xiàn)路由邏輯。我們必須遵循一些步驟才能成功創(chuàng)建新類別:

  • 首先,我們必須驗證傳入的請求。如果請求無效,我們必須返回包含錯誤消息的錯誤請求響應;

  • 然后,如果請求有效,則必須使用AutoMapper將新資源映射到類別模型類。

  • 現(xiàn)在,我們需要調(diào)用我們的服務,告訴它保存我們的新類別。如果執(zhí)行保存邏輯沒有問題,它將返回一個包含我們新類別數(shù)據(jù)的響應。如果沒有,它應該給我們一個指示,表明該過程失敗了,并可能出現(xiàn)錯誤消息。

  • 最后,如果有錯誤,我們將返回錯誤的請求。如果沒有,我們將新的類別模型映射到類別資源,并向客戶端返回包含新類別數(shù)據(jù)的成功響應。

這似乎很復雜,但是使用為API構建的服務架構來實現(xiàn)此邏輯確實很容易。

讓我們開始驗證傳入的請求。

步驟10-使用模型狀態(tài)驗證請求主體

ASP.NET Core控制器具有名為ModelState的屬性。在執(zhí)行我們的操作之前,該屬性在請求執(zhí)行期間填充。它是ModelStateDictionary的實例,該類包含諸如請求是否有效以及潛在的驗證錯誤消息之類的信息。

如下更改端點代碼:

123456[HttpPost] public async Task PostAsync([FromBody] SaveCategoryResource resource) { if (!ModelState.IsValid) return BadRequest(ModelState.GetErrorMessages()); }

這段代碼檢查模型狀態(tài)(在這種情況下為請求正文中發(fā)送的數(shù)據(jù))是否無效,并檢查我們的數(shù)據(jù)注釋。如果不是,則API返回錯誤的請求(狀態(tài)代碼400),以及我們的注釋元數(shù)據(jù)提供的默認錯誤消息。
該ModelState.GetErrorMessages()方法尚未實現(xiàn)。這是一種擴展方法(一種擴展現(xiàn)有類或接口功能的方法),我將實現(xiàn)該方法將驗證錯誤轉換為簡單的字符串以返回給客戶端。

Extensions在我們的API的根目錄中添加一個新文件夾,然后添加一個新類ModelStateExtensions。

1234567891011121314151617using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Mvc.ModelBinding; namespace Supermarket.API.Extensions { public static class ModelStateExtensions { public static List GetErrorMessages(this ModelStateDictionary dictionary) { return dictionary.SelectMany(m => m.Value.Errors) .Select(m => m.ErrorMessage) .ToList(); } } }

所有擴展方法以及聲明它們的類都應該是靜態(tài)的。** **這意味著它們不處理特定的實例數(shù)據(jù),并且在應用程序啟動時僅被加載一次。
this參數(shù)聲明前面的關鍵字告訴C#編譯器將其視為擴展方法。結果是我們可以像此類的常規(guī)方法一樣調(diào)用它,因為我們在要使用擴展的地方包含的特定的using代碼。

該擴展使用LINQ查詢,這是.NET的非常有用的功能,它使我們能夠使用鏈式語法來查詢和轉換數(shù)據(jù)。此處的表達式將驗證錯誤方法轉換為包含錯誤消息的字符串列表。

Supermarket.API.Extensions在進行下一步之前,將名稱空間導入Categories控制器。

1using Supermarket.API.Extensions;

讓我們通過將新資源映射到類別模型類來繼續(xù)實現(xiàn)端點邏輯。

步驟11-映射新資源

我們已經(jīng)定義了映射配置文件,可以將模型轉換為資源。現(xiàn)在,我們需要一個與之相反的新配置項。

ResourceToModelProfile在Mapping文件夾中添加一個新類:

123456789101112131415using AutoMapper; using Supermarket.API.Domain.Models; using Supermarket.API.Resources; namespace Supermarket.API.Mapping { public class ResourceToModelProfile : Profile { public ResourceToModelProfile() { CreateMap(); } } }

這里沒有新內(nèi)容。由于依賴注入的魔力,AutoMapper將在應用程序啟動時自動注冊此配置文件,而我們無需更改任何其他位置即可使用它。
現(xiàn)在,我們可以將新資源映射到相應的模型類:

123456789[HttpPost] public async Task PostAsync([FromBody] SaveCategoryResource resource) { if (!ModelState.IsValid) return BadRequest(ModelState.GetErrorMessages()); var category = _mapper.Map(resource); }

第12步-應用請求-響應模式來處理保存邏輯

現(xiàn)在我們必須實現(xiàn)最有趣的邏輯:保存一個新類別。我們希望我們的服務能夠做到。

由于連接到數(shù)據(jù)庫時出現(xiàn)問題,或者由于任何內(nèi)部業(yè)務規(guī)則使我們的數(shù)據(jù)無效,因此保存邏輯可能會失敗。

如果出現(xiàn)問題,我們不能簡單地拋出一個錯誤,因為它可能會停止API,并且客戶端應用程序也不知道如何處理該問題。另外,我們可能會有某種日志記錄機制來記錄錯誤。

保存方法的約定(即方法的簽名和響應類型)需要指示我們是否正確執(zhí)行了該過程。如果處理正常,我們將接收類別數(shù)據(jù)。如果沒有,我們至少必須收到一條錯誤消息,告訴您該過程失敗的原因。

我們可以通過應用request-response模式來實現(xiàn)此功能。這種企業(yè)設計模式將我們的請求和響應參數(shù)封裝到類中,以封裝我們的服務將用于處理某些任務并將信息返回給正在使用該服務的類的信息。

這種模式為我們提供了一些優(yōu)勢,例如:

  • 如果我們需要更改服務以接收更多參數(shù),則不必破壞其簽名;

  • 我們可以為我們的請求和/或響應定義標準合同;

  • 我們可以在不停止應用程序流程的情況下處理業(yè)務邏輯和潛在的失敗,并且我們不需要使用大量的try-catch塊。

讓我們?yōu)樘幚頂?shù)據(jù)更改的服務方法創(chuàng)建一個標準響應類型。對于這種類型的每個請求,我們都想知道該請求是否被正確執(zhí)行。如果失敗,我們要向客戶端返回錯誤消息。

在Domain文件夾的內(nèi)部Services,添加一個名為的新目錄Communication。在此處添加一個名為的新類BaseResponse。

123456789101112131415namespace Supermarket.API.Domain.Services.Communication { public abstract class BaseResponse { public bool Success { get; protected set; } public string Message { get; protected set; } public BaseResponse(bool success, string message) { Success = success; Message = message; } } }

那是我們的響應類型將繼承的抽象類。
抽象定義了一個Success屬性和一個Message屬性,該屬性將告知請求是否已成功完成,如果失敗,該屬性將顯示錯誤消息。

請注意,這些屬性是必需的,只有繼承的類才能設置此數(shù)據(jù),因為子類必須通過構造函數(shù)傳遞此信息。

提示:為所有內(nèi)容定義基類不是一個好習慣,因為基類會耦合您的代碼并阻止您輕松對其進行修改。優(yōu)先使用組合而不是繼承。

在此API的范圍內(nèi),使用基類并不是真正的問題,因為我們的服務不會增長太多。如果您意識到服務或應用程序會經(jīng)常增長和更改,請避免使用基類。

現(xiàn)在,在同一文件夾中,添加一個名為的新類SaveCategoryResponse。

12345678910111213141516171819202122232425262728293031323334using Supermarket.API.Domain.Models; namespace Supermarket.API.Domain.Services.Communication { public class SaveCategoryResponse : BaseResponse { public Category Category { get; private set; } private SaveCategoryResponse(bool success, string message, Category category) : base(success, message) { Category = category; } /// /// Creates a success response. /// /// Saved category. /// Response. public SaveCategoryResponse(Category category) : this(true, string.Empty, category) { } /// /// Creates am error response. /// /// Error message. /// Response. public SaveCategoryResponse(string message) : this(false, message, null) { } } }

響應類型還設置了一個Category屬性,如果請求成功完成,該屬性將包含我們的類別數(shù)據(jù)。
請注意,我為此類定義了三種不同的構造函數(shù):

  • 一個私有的,它將把成功和消息參數(shù)傳遞給基類,并設置Category屬性。

  • 僅接收類別作為參數(shù)的構造函數(shù)。這將創(chuàng)建一個成功的響應,調(diào)用私有構造函數(shù)來設置各自的屬性;

  • 第三個構造函數(shù)僅指定消息。這將用于創(chuàng)建故障響應。

因為C#支持多個構造函數(shù),所以我們僅通過使用不同的構造函數(shù)就簡化了響應的創(chuàng)建過程,而無需定義其他方法來處理此問題。

現(xiàn)在,我們可以更改服務界面以添加新的保存方法合同。

更改ICategoryService接口,如下所示:

1234567891011121314using System.Collections.Generic; using System.Threading.Tasks; using Supermarket.API.Domain.Models; using Supermarket.API.Domain.Services.Communication; namespace Supermarket.API.Domain.Services { public interface ICategoryService { Task> ListAsync(); Task SaveAsync(Category category); } }

我們只需將類別傳遞給此方法,它將處理保存模型數(shù)據(jù),編排倉儲和其他必要服務所需的所有邏輯。
請注意,由于我們不需要任何其他參數(shù)來執(zhí)行此任務,因此我不在此處創(chuàng)建特定的請求類。計算機編程中有一個名為KISS的概念?—Keep It Simple,Stupid的簡稱。基本上,它說您應該使您的應用程序盡可能簡單。

設計應用程序時請記住這一點:僅應用解決問題所需的內(nèi)容。不要過度設計您的應用程序。

現(xiàn)在我們可以完成端點邏輯:

123456789101112131415161718[HttpPost] public async Task PostAsync([FromBody] SaveCategoryResource resource) { if (!ModelState.IsValid) return BadRequest(ModelState.GetErrorMessages()); var category = _mapper.Map(resource); var result = await _categoryService.SaveAsync(category); if (!result.Success) return BadRequest(result.Message); var categoryResource = _mapper.Map(result.Category); return Ok(categoryResource); }

在驗證請求數(shù)據(jù)并將資源映射到我們的模型之后,我們將其傳遞給我們的服務以保留數(shù)據(jù)。
如果失敗,則API返回錯誤的請求。如果沒有,API會將新類別(現(xiàn)在包括諸如new的數(shù)據(jù)Id)映射到我們先前創(chuàng)建的類別CategoryResource,并將其發(fā)送給客戶端。

現(xiàn)在,讓我們?yōu)榉諏崿F(xiàn)真正的邏輯。

第13步—數(shù)據(jù)庫邏輯和工作單元模式

由于我們要將數(shù)據(jù)持久化到數(shù)據(jù)庫中,因此我們需要在倉儲中使用一種新方法。

向ICategoryRepository接口添加AddAsync新方法:

12345public interface ICategoryRepository { Task> ListAsync(); Task AddAsync(Category category); }

現(xiàn)在,讓我們在真正的倉儲類中實現(xiàn)此方法:

1234567891011121314151617public class CategoryRepository : BaseRepository, ICategoryRepository { public CategoryRepository(AppDbContext context) : base(context) { } public async Task> ListAsync() { return await _context.Categories.ToListAsync(); } public async Task AddAsync(Category category) { await _context.Categories.AddAsync(category); } }

在這里,我們只是在集合中添加一個新類別。
當我們向中添加類時DBSet<>,EF Core將開始跟蹤模型發(fā)生的所有更改,并在當前狀態(tài)下使用此數(shù)據(jù)生成將插入,更新或刪除模型的查詢。

當前的實現(xiàn)只是將模型添加到我們的集合中,但是我們的數(shù)據(jù)仍然不會保存。

在上下文類中提供了SaveChanges的方法,我們必須調(diào)用該方法才能真正將查詢執(zhí)行到數(shù)據(jù)庫中。我之所以沒有在這里調(diào)用它,是因為倉儲不應該持久化數(shù)據(jù),它只是一種內(nèi)存集合對象。

即使在經(jīng)驗豐富的.NET開發(fā)人員之間,該主題也引起很大爭議,但是讓我向您解釋為什么您不應該在倉儲類中調(diào)用SaveChanges方法。

我們可以從概念上將倉儲像.NET框架中存在的任何其他集合一樣。在.NET(和許多其他編程語言,例如Javascript和Java)中處理集合時,通常可以:

  • 向其中添加新項(例如,當您將數(shù)據(jù)推送到列表,數(shù)組和字典時);

  • 查找或過濾項目;

  • 從集合中刪除一個項目;

  • 替換給定的項目,或更新它。

想一想現(xiàn)實世界中的清單。想象一下,您正在編寫一份購物清單以在超市購買東西(巧合,不是嗎?)。

在列表中,寫下您需要購買的所有水果。您可以將水果添加到此列表中,如果放棄購買就刪除水果,也可以替換水果的名稱。但是您無法將水果保存到列表中。用簡單的英語說這樣的話是沒有意義的。

提示:在使用面向對象的編程語言設計類和接口時,請嘗試使用自然語言來檢查您所做的工作是否正確。

例如,說人實現(xiàn)了person的接口是有道理的,但是說一個人實現(xiàn)了一個帳戶卻沒有道理。

如果您要“保存”水果清單(在這種情況下,要購買所有水果),請付款,然后超市會處理庫存數(shù)據(jù)以檢查他們是否必須從供應商處購買更多水果。

編程時可以應用相同的邏輯。倉儲不應保存,更新或刪除數(shù)據(jù)。相反,他們應該將其委托給其他類來處理此邏輯。

將數(shù)據(jù)直接保存到倉儲中時,還有另一個問題:您不能使用transaction。

想象一下,我們的應用程序具有一種日志記錄機制,該機制存儲一些用戶名,并且每次對API數(shù)據(jù)進行更改時都會執(zhí)行操作。

現(xiàn)在想象一下,由于某種原因,您調(diào)用了一個更新用戶名的服務(這是不常見的情況,但讓我們考慮一下)。

您同意要更改虛擬用戶表中的用戶名,首先必須更新所有日志以正確告訴誰執(zhí)行了該操作,對嗎?

現(xiàn)在想象我們已經(jīng)為用戶和不同倉儲中的日志實現(xiàn)了update方法,它們都調(diào)用了SaveChanges。如果這些方法之一在更新過程中失敗,會發(fā)生什么?最終會導致數(shù)據(jù)不一致。

只有在一切完成之后,我們才應該將更改保存到數(shù)據(jù)庫中。為此,我們必須使用transaction,這基本上是大多數(shù)數(shù)據(jù)庫實現(xiàn)的功能,只有在完成復雜的操作后才能保存數(shù)據(jù)。

“-好的,所以如果我們不能在這里保存東西,我們應該在哪里做?”

處理此問題的常見模式是工作單元模式。此模式包含一個類,該類將我們的AppDbContext實例作為依賴項接收,并公開用于開始,完成或中止事務的方法。

在這里,我們將使用工作單元的簡單實現(xiàn)來解決我們的問題。

Repositories在Domain層的倉儲文件夾Repositories內(nèi)添加一個新接口IUnitOfWork:

12345678910using System.Threading.Tasks; namespace Supermarket.API.Domain.Repositories { public interface IUnitOfWork { Task CompleteAsync(); } }

如您所見,它僅公開一種將異步完成數(shù)據(jù)管理操作的方法。
現(xiàn)在讓我們添加實際的實現(xiàn)。

在Persistence層RepositoriesRepositories文件夾中的添加一個名為的UnitOfWork的新類:

123456789101112131415161718192021222324using System.Threading.Tasks; using Supermarket.API.Domain.Repositories; using Supermarket.API.Persistence.Contexts; namespace Supermarket.API.Persistence.Repositories { public class UnitOfWork : IUnitOfWork { private readonly AppDbContext _context; public UnitOfWork(AppDbContext context) { _context = context; } public async Task CompleteAsync() { await _context.SaveChangesAsync(); } } }

這是一個簡單,干凈的實現(xiàn),僅在使用倉儲修改完所有更改后,才將所有更改保存到數(shù)據(jù)庫中。
如果研究工作單元模式的實現(xiàn),則會發(fā)現(xiàn)實現(xiàn)回滾操作的更復雜的模式。

由于EF Core已經(jīng)在后臺實現(xiàn)了倉儲模式和工作單元,因此我們不必在意回滾方法。

“ - 什么?那么為什么我們必須創(chuàng)建所有這些接口和類?”

將持久性邏輯與業(yè)務規(guī)則分開在代碼可重用性和維護方面具有許多優(yōu)勢。如果直接使用EF Core,我們最終將擁有更復雜的類,這些類將很難更改。

想象一下,將來您決定將ORM框架更改為其他框架,例如Dapper,或者由于性能而必須實施純SQL查詢。如果將查詢邏輯與服務耦合在一起,將很難更改該邏輯,因為您必須在許多類中進行此操作。

使用倉儲模式,您可以簡單地實現(xiàn)一個新的倉儲類并使用依賴注入將其綁定。

因此,基本上,如果您直接在服務中使用EF Core,并且必須進行一些更改,那么您將獲得:

就像我說的那樣,EF Core在后臺實現(xiàn)了工作單元和倉儲模式。我們可以將DbSet<>屬性視為倉儲。而且,SaveChanges僅在所有數(shù)據(jù)庫操作成功的情況下才保留數(shù)據(jù)。

現(xiàn)在,您知道什么是工作單元以及為什么將其與倉儲一起使用,讓我們實現(xiàn)真實服務的邏輯。

1234567891011121314151617181920212223242526272829303132333435public class CategoryService : ICategoryService { private readonly ICategoryRepository _categoryRepository; private readonly IUnitOfWork _unitOfWork; public CategoryService(ICategoryRepository categoryRepository, IUnitOfWork unitOfWork) { _categoryRepository = categoryRepository; _unitOfWork = unitOfWork; } public async Task> ListAsync() { return await _categoryRepository.ListAsync(); } public async Task SaveAsync(Category category) { try { await _categoryRepository.AddAsync(category); await _unitOfWork.CompleteAsync(); return new SaveCategoryResponse(category); } catch (Exception ex) { // Do some logging stuff return new SaveCategoryResponse($"An error occurred when saving the category: {ex.Message}"); } } }

多虧了我們的解耦架構,我們可以簡單地將實例UnitOfWork作為此類的依賴傳遞。
我們的業(yè)務邏輯非常簡單。

首先,我們嘗試將新類別添加到數(shù)據(jù)庫中,然后API嘗試保存新類別,將所有內(nèi)容包裝在try-catch塊中。

如果失敗,則API會調(diào)用一些虛構的日志記錄服務,并返回指示失敗的響應。

如果該過程順利完成,則應用程序將返回成功響應,并發(fā)送我們的類別數(shù)據(jù)。簡單吧?

提示:在現(xiàn)實世界的應用程序中,您不應將所有內(nèi)容包裝在通用的try-catch塊中,而應分別處理所有可能的錯誤。

簡單地添加一個try-catch塊并不能解決大多數(shù)可能的失敗情況。請確保正確實現(xiàn)錯誤處理。

測試我們的API之前的最后一步是將工作單元接口綁定到其各自的類。

將此新行添加到類的ConfigureServices方法中Startup:

1services.AddScoped();

現(xiàn)在讓我們測試一下!第14步-使用Postman測試我們的POST端點

重新啟動我們的應用程序dotnet run。

我們無法使用瀏覽器測試POST端點。讓我們使用Postman測試我們的端點。這是測試RESTful API的非常有用的工具。

打開Postman,然后關閉介紹性消息。您會看到這樣的屏幕:

屏幕顯示測試端點的選項

GET默認情況下,將所選內(nèi)容更改為選擇框POST。

在Enter request URL字段中輸入API地址。

我們必須提供請求正文數(shù)據(jù)以發(fā)送到我們的API。單擊Body菜單項,然后將其下方顯示的選項更改為raw。

Postman將在右側顯示一個Text選項,將其更改為JSON (application/json)并粘貼以下JSON數(shù)據(jù):

123{ "name": ""}

發(fā)送請求前的屏幕

如您所見,我們將向我們的新端點發(fā)送一個空的名稱字符串。

點擊Send按鈕。您將收到如下輸出:

如您所見,我們的驗證邏輯有效!

您還記得我們?yōu)槎它c創(chuàng)建的驗證邏輯嗎?此輸出是它起作用的證明!

還要注意右側顯示的400狀態(tài)代碼。該BadRequest結果自動將此狀態(tài)碼的響應。

現(xiàn)在,讓我們將JSON數(shù)據(jù)更改為有效數(shù)據(jù),以查看新的響應:

最后,我們期望得到的結果

API正確創(chuàng)建了我們的新資源。

到目前為止,我們的API可以列出和創(chuàng)建類別。您學到了很多有關C#語言,ASP.NET Core框架以及構造API的通用設計方法的知識。

讓我們繼續(xù)我們的類別API,創(chuàng)建用于更新類別的端點。

從現(xiàn)在開始,由于我向您解釋了大多數(shù)概念,因此我將加快解釋速度,并專注于新主題,以免浪費您的時間。Let’s go!

第15步-更新類別

要更新類別,我們需要一個HTTP PUT端點。

我們必須編寫的邏輯與POST邏輯非常相似:

  • 首先,我們必須使用來驗證傳入的請求ModelState。

  • 如果請求有效,則API應使用AutoMapper將傳入資源映射到模型類。

  • 然后,我們需要調(diào)用我們的服務,告訴它更新類別,提供相應的類別Id和更新的數(shù)據(jù);

  • 如果Id數(shù)據(jù)庫中沒有給定的類別,我們將返回錯誤的請求。我們可以使用NotFound結果來代替,但是對于這個范圍而言,這并不重要,因為我們向客戶端應用程序提供了錯誤消息。

  • 如果正確執(zhí)行了保存邏輯,則服務必須返回包含更新的類別數(shù)據(jù)的響應。如果不是,它應該給我們指示該過程失敗,并顯示一條消息指示原因;

  • 最后,如果有錯誤,則API返回錯誤的請求。如果不是,它將更新的類別模型映射到類別資源,并將成功響應返回給客戶端應用程序。

讓我們將新PutAsync方法添加到控制器類中:

123456789101112131415161718[HttpPut("{id}")] public async Task PutAsync(int id, [FromBody] SaveCategoryResource resource) { if (!ModelState.IsValid) return BadRequest(ModelState.GetErrorMessages()); var category = _mapper.Map(resource); var result = await _categoryService.UpdateAsync(id, category); if (!result.Success) return BadRequest(result.Message); var categoryResource = _mapper.Map(result.Category); return Ok(categoryResource); }

如果將其與POST邏輯進行比較,您會注意到這里只有一個區(qū)別:HttPut屬性指定給定路由應接收的參數(shù)。
我們將調(diào)用此端點,將類別指定Id 為最后一個URL片段,例如/api/categories/1。ASP.NET Core管道將此片段解析為相同名稱的參數(shù)。

現(xiàn)在我們必須UpdateAsync在ICategoryService接口中定義方法簽名:

123456public interface ICategoryService { Task> ListAsync(); Task SaveAsync(Category category); Task UpdateAsync(int id, Category category); }

現(xiàn)在讓我們轉向真正的邏輯。

第16步-更新邏輯

首先,要更新類別,我們需要從數(shù)據(jù)庫中返回當前數(shù)據(jù)(如果存在)。我們還需要將其更新到我們的中DBSet<>。

讓我們在ICategoryService界面中添加兩個新的方法約定:

1234567public interface ICategoryRepository { Task> ListAsync(); Task AddAsync(Category category); Task FindByIdAsync(int id); void Update(Category category); }

我們已經(jīng)定義了FindByIdAsync方法,該方法將從數(shù)據(jù)庫中異步返回一個類別,以及該Update方法。請注意,該Update方法不是異步的,因為EF Core API不需要異步方法來更新模型。
現(xiàn)在,讓我們在CategoryRepository類中實現(xiàn)真正的邏輯:

12345678910public async Task FindByIdAsync(int id) { return await _context.Categories.FindAsync(id); } public void Update(Category category) { _context.Categories.Update(category); }

最后,我們可以對服務邏輯進行編碼:

1234567891011121314151617181920212223242526public async Task UpdateAsync(int id, Category category) { var existingCategory = await _categoryRepository.FindByIdAsync(id); if (existingCategory == null) return new SaveCategoryResponse("Category not found."); existingCategory.Name = category.Name; try { _categoryRepository.Update(existingCategory); await _unitOfWork.CompleteAsync(); return new SaveCategoryResponse(existingCategory); } catch (Exception ex) { // Do some logging stuff return new SaveCategoryResponse($"An error occurred when updating the category: {ex.Message}"); } }

API嘗試從數(shù)據(jù)庫中獲取類別。如果結果為null,我們將返回一個響應,告知該類別不存在。如果類別存在,我們需要設置其新名稱。
然后,API會嘗試保存更改,例如創(chuàng)建新類別時。如果該過程完成,則該服務將返回成功響應。如果不是,則執(zhí)行日志記錄邏輯,并且端點接收包含錯誤消息的響應。

現(xiàn)在讓我們對其進行測試。首先,讓我們添加一個新類別Id以使用有效類別。我們可以使用播種到數(shù)據(jù)庫中的類別的標識符,但是我想通過這種方式向您展示我們的API將更新正確的資源。

再次運行該應用程序,然后使用Postman將新類別發(fā)布到數(shù)據(jù)庫中:

添加新類別以供日后更新

使用一個可用的數(shù)據(jù)Id,將POST 選項更改PUT為選擇框,然后在URL的末尾添加ID值。將name屬性更改為其他名稱,然后發(fā)送請求以檢查結果:

類別數(shù)據(jù)已成功更新

您可以將GET請求發(fā)送到API端點,以確保您正確編輯了類別名稱:

那是現(xiàn)在GET請求的結果

我們必須對類別執(zhí)行的最后一項操作是排除類別。讓我們創(chuàng)建一個HTTP Delete端點。

第17步-刪除類別

刪除類別的邏輯確實很容易實現(xiàn),因為我們所需的大多數(shù)方法都是先前構建的。

這些是我們工作路線的必要步驟:

  • API需要調(diào)用我們的服務,告訴它刪除我們的類別,并提供相應的Id;

  • 如果數(shù)據(jù)庫中沒有具有給定ID的類別,則該服務應返回一條消息指出該類別;

  • 如果執(zhí)行刪除邏輯沒有問題,則服務應返回包含我們已刪除類別數(shù)據(jù)的響應。如果沒有,它應該給我們一個指示,表明該過程失敗了,并可能出現(xiàn)錯誤消息。

  • 最后,如果有錯誤,則API返回錯誤的請求。如果不是,則API會將更新的類別映射到資源,并向客戶端返回成功響應。

讓我們開始添加新的端點邏輯:

12345678910111213[HttpDelete("{id}")] public async Task DeleteAsync(int id) { var result = await _categoryService.DeleteAsync(id); if (!result.Success) return BadRequest(result.Message); var categoryResource = _mapper.Map(result.Category); return Ok(categoryResource); }

該HttpDelete屬性還定義了一個id 模板。
在將DeleteAsync簽名添加到我們的ICategoryService接口之前,我們需要做一些小的重構。

新的服務方法必須返回包含類別數(shù)據(jù)的響應,就像對PostAsyncand UpdateAsync方法所做的一樣。我們可以SaveCategoryResponse為此目的重用,但在這種情況下我們不會保存數(shù)據(jù)。

為了避免創(chuàng)建具有相同形狀的新類來滿足此要求,我們可以將我們重命名SaveCategoryResponse為CategoryResponse。

如果您使用的是Visual Studio Code,則可以打開SaveCategoryResponse類,將鼠標光標放在類名上方,然后使用選項Change All Occurrences* *來重命名該類:

確保也重命名文件名。

讓我們將DeleteAsync方法簽名添加到ICategoryService 接口中:

1234567public interface ICategoryService { Task> ListAsync(); Task SaveAsync(Category category); Task UpdateAsync(int id, Category category); Task DeleteAsync(int id); }

在實施刪除邏輯之前,我們需要在倉儲中使用一種新方法。
將Remove方法簽名添加到ICategoryRepository接口:

1void Remove(Category category);

現(xiàn)在,在倉儲類上添加真正的實現(xiàn):

1234public void Remove(Category category) { _context.Categories.Remove(category); }

EF Core要求將模型的實例傳遞給Remove方法,以正確了解我們要刪除的模型,而不是簡單地傳遞Id。
最后,讓我們在CategoryService類上實現(xiàn)邏輯:

1234567891011121314151617181920212223public async Task DeleteAsync(int id) { var existingCategory = await _categoryRepository.FindByIdAsync(id); if (existingCategory == null) return new CategoryResponse("Category not found."); try { _categoryRepository.Remove(existingCategory); await _unitOfWork.CompleteAsync(); return new CategoryResponse(existingCategory); } catch (Exception ex) { // Do some logging stuff return new CategoryResponse($"An error occurred when deleting the category: {ex.Message}"); } }

這里沒有新內(nèi)容。該服務嘗試通過ID查找類別,然后調(diào)用我們的倉儲以刪除類別。最后,工作單元完成將實際操作執(zhí)行到數(shù)據(jù)庫中的事務。“-嘿,但是每個類別的產(chǎn)品呢?為避免出現(xiàn)錯誤,您是否不需要先創(chuàng)建倉儲并刪除產(chǎn)品?”

答案是否定的。借助EF Core跟蹤機制,當我們從數(shù)據(jù)庫中加載模型時,框架便知道了該模型具有哪些關系。如果我們刪除它,EF Core知道它應該首先遞歸刪除所有相關模型。

在將類映射到數(shù)據(jù)庫表時,我們可以禁用此功能,但這在本教程的范圍之外。如果您想了解此功能,請看這里。

現(xiàn)在是時候測試我們的新端點了。再次運行該應用程序,并使用Postman發(fā)送DELETE請求,如下所示:

如您所見,API毫無問題地刪除了現(xiàn)有類別

我們可以通過發(fā)送GET請求來檢查我們的API是否正常工作:

我們已經(jīng)完成了類別API。現(xiàn)在是時候轉向產(chǎn)品API。

步驟18-產(chǎn)品API

到目前為止,您已經(jīng)學習了如何實現(xiàn)所有基本的HTTP動詞來使用ASP.NET Core處理CRUD操作。讓我們進入實現(xiàn)產(chǎn)品API的下一個層次。

我將不再詳細介紹所有HTTP動詞,因為這將是詳盡無遺的。在本教程的最后一部分,我將僅介紹GET請求,以向您展示在從數(shù)據(jù)庫查詢數(shù)據(jù)時如何包括相關實體,以及如何使用Description我們?yōu)镋UnitOfMeasurement 枚舉值定義的屬性。

將新控制器ProductsController添加到名為Controllers的文件夾中。

在這里編寫任何代碼之前,我們必須創(chuàng)建產(chǎn)品資源。

讓我刷新您的記憶,再次顯示我們的資源應如何:

123456789101112131415{ [ { "id": 1, "name": "Sugar", "quantityInPackage": 1, "unitOfMeasurement": "KG" "category": { "id": 3, "name": "Sugar" } }, … // Other products ]}

我們想要一個包含數(shù)據(jù)庫中所有產(chǎn)品的JSON數(shù)組。
JSON數(shù)據(jù)與產(chǎn)品模型有兩點不同:

  • 測量單位以較短的方式顯示,僅顯示其縮寫。

  • 我們輸出類別數(shù)據(jù)而不包括CategoryId屬性。

為了表示度量單位,我們可以使用簡單的字符串屬性代替枚舉類型(順便說一下,我們沒有JSON數(shù)據(jù)的默認枚舉類型,因此我們必須將其轉換為其他類型)。

現(xiàn)在,我們現(xiàn)在要塑造新資源,讓我們創(chuàng)建它。ProductResource在Resources文件夾中添加一個新類:

1234567891011namespace Supermarket.API.Resources { public class ProductResource { public int Id { get; set; } public string Name { get; set; } public int QuantityInPackage { get; set; } public string UnitOfMeasurement { get; set; } public CategoryResource Category {get;set;} } }

現(xiàn)在,我們必須配置模型類和新資源類之間的映射。
映射配置將與用于其他映射的配置幾乎相同,但是在這里,我們必須處理將EUnitOfMeasurement枚舉轉換為字符串的操作。

您還記得StringValue應用于枚舉類型的屬性嗎?現(xiàn)在,我將向您展示如何使用.NET框架的強大功能:反射 API提取此信息。

反射 API是一組強大的資源工具集,可讓我們提取和操作元數(shù)據(jù)。許多框架和庫(包括ASP.NET Core本身)都利用這些資源來處理許多后臺工作。

現(xiàn)在讓我們看看它在實踐中是如何工作的。將新類添加到Extensions名為的文件夾中EnumExtensions。

123456789101112131415161718using System.ComponentModel; using System.Reflection; namespace Supermarket.API.Extensions { public static class EnumExtensions { public static string ToDescriptionString(this TEnum @enum) { FieldInfo info = @enum.GetType().GetField(@enum.ToString()); var attributes = (DescriptionAttribute[])info.GetCustomAttributes(typeof(DescriptionAttribute), false); return attributes?[0].Description ?? @enum.ToString(); } } }

第一次看代碼可能會讓人感到恐懼,但這并不復雜。讓我們分解代碼定義以了解其工作原理。
首先,我們定義了一種通用方法(一種方法,該方法可以接收不止一種類型的參數(shù),在這種情況下,該方法由TEnum聲明表示),該方法接收給定的枚舉作為參數(shù)。

由于enum是C#中的保留關鍵字,因此我們在參數(shù)名稱前面添加了@,以使其成為有效名稱。

該方法的第一步是使用該方法獲取參數(shù)的類型信息(類,接口,枚舉或結構定義)GetType。

然后,該方法使用來獲取特定的枚舉值(例如Kilogram)GetField(@enum.ToString())。

下一行找到Description應用于枚舉值的所有屬性,并將其數(shù)據(jù)存儲到數(shù)組中(在某些情況下,我們可以為同一屬性指定多個屬性)。

最后一行使用較短的語法來檢查我們是否至少有一個枚舉類型的描述屬性。如果有,我們將返回Description此屬性提供的值。如果不是,我們使用默認的強制類型轉換將枚舉作為字符串返回。

?.操作者(零條件運算)檢查該值是否null訪問其屬性之前。

??運算符(空合并運算符)告訴應用程序在左邊的返回值,如果它不為空,或者在正確的,否則價值。

現(xiàn)在我們有了擴展方法來提取描述,讓我們配置模型和資源之間的映射。多虧了AutoMapper,我們只需要多一行就可以做到這一點。

打開ModelToResourceProfile類并通過以下方式更改代碼:

123456789101112131415161718192021using AutoMapper; using Supermarket.API.Domain.Models; using Supermarket.API.Extensions; using Supermarket.API.Resources; namespace Supermarket.API.Mapping { public class ModelToResourceProfile : Profile { public ModelToResourceProfile() { CreateMap(); CreateMap() .ForMember(src => src.UnitOfMeasurement, opt => opt.MapFrom(src => src.UnitOfMeasurement.ToDescriptionString())); } } }

此語法告訴AutoMapper使用新的擴展方法將我們的EUnitOfMeasurement值轉換為包含其描述的字符串。簡單吧?您可以閱讀官方文檔以了解完整語法。
注意,我們尚未為category屬性定義任何映射配置。因為我們之前為類別配置了映射,并且由于產(chǎn)品模型具有相同類型和名稱的category屬性,所以AutoMapper隱式知道應該使用各自的配置來映射它。

現(xiàn)在,我們添加端點代碼。更改ProductsController代碼:

12345678910111213141516171819202122232425262728293031323334using System.Collections.Generic; using System.Threading.Tasks; using AutoMapper; using Microsoft.AspNetCore.Mvc; using Supermarket.API.Domain.Models; using Supermarket.API.Domain.Services; using Supermarket.API.Resources; namespace Supermarket.API.Controllers { [Route("/api/[controller]")] public class ProductsController : Controller { private readonly IProductService _productService; private readonly IMapper _mapper; public ProductsController(IProductService productService, IMapper mapper) { _productService = productService; _mapper = mapper; } [HttpGet] public async Task> ListAsync() { var products = await _productService.ListAsync(); var resources = _mapper.Map, IEnumerable>(products); return resources; } } }

基本上,為類別控制器定義的結構相同。
讓我們進入服務部分。將一個新IProductService接口添加到Domain層中的Services文件夾中:

123456789101112using System.Collections.Generic; using System.Threading.Tasks; using Supermarket.API.Domain.Models; namespace Supermarket.API.Domain.Services { public interface IProductService { Task> ListAsync(); } }

您應該已經(jīng)意識到,在真正實現(xiàn)新服務之前,我們需要一個倉儲。
IProductRepository在相應的文件夾中添加一個名為的新接口:

123456789101112using System.Collections.Generic; using System.Threading.Tasks; using Supermarket.API.Domain.Models; namespace Supermarket.API.Domain.Repositories { public interface IProductRepository { Task> ListAsync(); } }

現(xiàn)在,我們實現(xiàn)倉儲。除了必須在查詢數(shù)據(jù)時返回每個產(chǎn)品的相應類別數(shù)據(jù)外,我們幾乎必須像對類別倉儲一樣實現(xiàn)。
默認情況下,EF Core在查詢數(shù)據(jù)時不包括與模型相關的實體,因為它可能非常慢(想象一個具有十個相關實體的模型,所有相關實體都有自己的關系)。

要包括類別數(shù)據(jù),我們只需要多一行:

123456789101112131415161718192021222324using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Supermarket.API.Domain.Models; using Supermarket.API.Domain.Repositories; using Supermarket.API.Persistence.Contexts; namespace Supermarket.API.Persistence.Repositories { public class ProductRepository : BaseRepository, IProductRepository { public ProductRepository(AppDbContext context) : base(context) { } public async Task> ListAsync() { return await _context.Products.Include(p => p.Category) .ToListAsync(); } } }

請注意對的調(diào)用Include(p => p.Category)。我們可以鏈接此語法,以在查詢數(shù)據(jù)時包含盡可能多的實體。執(zhí)行選擇時,EF Core會將其轉換為聯(lián)接。
現(xiàn)在,我們可以ProductService像處理類別一樣實現(xiàn)類:

12345678910111213141516171819202122232425using System.Collections.Generic; using System.Threading.Tasks; using Supermarket.API.Domain.Models; using Supermarket.API.Domain.Repositories; using Supermarket.API.Domain.Services; namespace Supermarket.API.Services { public class ProductService : IProductService { private readonly IProductRepository _productRepository; public ProductService(IProductRepository productRepository) { _productRepository = productRepository; } public async Task> ListAsync() { return await _productRepository.ListAsync(); } } }

讓我們綁定更改Startup類的新依賴項:

12345678910111213141516171819202122public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddDbContext(options => { options.UseInMemoryDatabase("supermarket-api-in-memory"); }); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddAutoMapper(); }

最后,在測試API之前,讓我們AppDbContext在初始化應用程序時更改類以包括一些產(chǎn)品,以便我們看到結果:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.Entity().ToTable("Categories"); builder.Entity().HasKey(p => p.Id); builder.Entity().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd().HasValueGenerator>(); builder.Entity().Property(p => p.Name).IsRequired().HasMaxLength(30); builder.Entity().HasMany(p => p.Products).WithOne(p => p.Category).HasForeignKey(p => p.CategoryId); builder.Entity().HasData ( new Category { Id = 100, Name = "Fruits and Vegetables" }, // Id set manually due to in-memory provider new Category { Id = 101, Name = "Dairy" } ); builder.Entity().ToTable("Products"); builder.Entity().HasKey(p => p.Id); builder.Entity().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd(); builder.Entity().Property(p => p.Name).IsRequired().HasMaxLength(50); builder.Entity().Property(p => p.QuantityInPackage).IsRequired(); builder.Entity().Property(p => p.UnitOfMeasurement).IsRequired(); builder.Entity().HasData ( new Product { Id = 100, Name = "Apple", QuantityInPackage = 1, UnitOfMeasurement = EUnitOfMeasurement.Unity, CategoryId = 100 }, new Product { Id = 101, Name = "Milk", QuantityInPackage = 2, UnitOfMeasurement = EUnitOfMeasurement.Liter, CategoryId = 101, } ); }

我添加了兩個虛構產(chǎn)品,將它們與初始化應用程序時我們播種的類別相關聯(lián)。
該測試了!再次運行API并發(fā)送GET請求以/api/products使用Postman:

就是這樣!恭喜你!

現(xiàn)在,您將了解如何使用解耦的代碼架構使用ASP.NET Core構建RESTful API。您了解了.NET Core框架的許多知識,如何使用C#,EF Core和AutoMapper的基礎知識以及在設計應用程序時要使用的許多有用的模式。

您可以檢查API的完整實現(xiàn),包括產(chǎn)品的其他HTTP動詞,并檢查Github倉儲:

evgomes / supermarket-api

使用ASP.NET Core 2.2構建的簡單RESTful API,展示了如何使用分離的,可維護的……創(chuàng)建RESTful服務。github.com

結論

ASP.NET Core是創(chuàng)建Web應用程序時使用的出色框架。它帶有許多有用的API,可用于構建干凈,可維護的應用程序。創(chuàng)建專業(yè)應用程序時,可以將其視為一種選擇。

本文并未涵蓋專業(yè)API的所有方面,但您已學習了所有基礎知識。您還學到了許多有用的模式,可以解決我們每天面臨的模式。

希望您喜歡這篇文章,希望對您有所幫助。期待你的反饋,以便我能進一步提高。

進一步學習的可用參考資料

.NET Core教程-Microsoft文檔

ASP.NET Core文檔-Microsoft文檔

本文首發(fā)于溪源的個人博客https://www.techq.xyz,純屬個人見解,不代表本號或長沙.NET技術社區(qū)觀點。

如有疏漏錯誤,還請批評指正。【DotNET技術圈】是長沙.NET技術社區(qū)運營的公眾號,歡迎大家投稿支持。

總結

以上是生活随笔為你收集整理的asp编程工具_使用ASP.NET Core构建RESTful API的技术指南的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

国产人妻大战黑人第1集 | 精品国产精品久久一区免费式 | 亚洲精品久久久久久久久久久 | 久精品国产欧美亚洲色aⅴ大片 | 在线а√天堂中文官网 | 成人av无码一区二区三区 | 国产精华av午夜在线观看 | 亚洲午夜久久久影院 | 十八禁真人啪啪免费网站 | 色综合久久久久综合一本到桃花网 | 亲嘴扒胸摸屁股激烈网站 | 狠狠色噜噜狠狠狠7777奇米 | 精品人妻中文字幕有码在线 | 波多野结衣av一区二区全免费观看 | 国产精品永久免费视频 | 中文字幕av无码一区二区三区电影 | 久久午夜夜伦鲁鲁片无码免费 | 久久久久久久人妻无码中文字幕爆 | 久久午夜夜伦鲁鲁片无码免费 | 国产高清av在线播放 | 国产精品爱久久久久久久 | 欧美freesex黑人又粗又大 | 67194成是人免费无码 | 少妇性俱乐部纵欲狂欢电影 | 欧美性猛交xxxx富婆 | 国产婷婷色一区二区三区在线 | 久久久久国色av免费观看性色 | 人人爽人人澡人人高潮 | 免费看男女做好爽好硬视频 | 97资源共享在线视频 | 大肉大捧一进一出视频出来呀 | 亚洲小说图区综合在线 | 成人女人看片免费视频放人 | 色欲av亚洲一区无码少妇 | 奇米影视7777久久精品人人爽 | 亚洲精品欧美二区三区中文字幕 | 国产精品永久免费视频 | 无码人妻丰满熟妇区毛片18 | 老太婆性杂交欧美肥老太 | 亚洲s码欧洲m码国产av | 免费中文字幕日韩欧美 | 荡女精品导航 | 天堂а√在线中文在线 | 纯爱无遮挡h肉动漫在线播放 | 中文字幕无码av波多野吉衣 | 国产乱人无码伦av在线a | 亚洲乱码日产精品bd | 亚洲成a人一区二区三区 | 欧美三级a做爰在线观看 | 亚洲色在线无码国产精品不卡 | 成在人线av无码免费 | 天干天干啦夜天干天2017 | 亚洲国产欧美日韩精品一区二区三区 | 亚洲国产成人av在线观看 | 久久久久99精品国产片 | 成人欧美一区二区三区 | 亚洲午夜久久久影院 | 欧美成人家庭影院 | 永久免费观看国产裸体美女 | 3d动漫精品啪啪一区二区中 | 又粗又大又硬又长又爽 | 麻豆国产人妻欲求不满 | 亚洲色偷偷偷综合网 | 无码av免费一区二区三区试看 | 精品国产乱码久久久久乱码 | 久久精品国产日本波多野结衣 | 国产一区二区三区四区五区加勒比 | 偷窥日本少妇撒尿chinese | 漂亮人妻洗澡被公强 日日躁 | www国产精品内射老师 | 亚洲成a人片在线观看无码3d | 亚洲中文字幕无码中文字在线 | 国产人妻精品午夜福利免费 | 亚洲熟悉妇女xxx妇女av | 无码免费一区二区三区 | 天海翼激烈高潮到腰振不止 | 中文毛片无遮挡高清免费 | 人人超人人超碰超国产 | 人人爽人人澡人人高潮 | 亚洲欧洲中文日韩av乱码 | 国产免费久久久久久无码 | 亚拍精品一区二区三区探花 | 久久久国产精品无码免费专区 | 国产亚洲美女精品久久久2020 | 人妻少妇精品无码专区动漫 | 人妻体内射精一区二区三四 | 亚洲va欧美va天堂v国产综合 | 极品尤物被啪到呻吟喷水 | 亚洲第一网站男人都懂 | 无码人妻少妇伦在线电影 | 久久久久久国产精品无码下载 | 丝袜美腿亚洲一区二区 | 色欲久久久天天天综合网精品 | 国产亚洲精品久久久久久国模美 | 少妇邻居内射在线 | 一本大道久久东京热无码av | 免费人成在线观看网站 | 中文精品无码中文字幕无码专区 | 曰韩无码二三区中文字幕 | 国产精品无码成人午夜电影 | 亚洲大尺度无码无码专区 | 久久熟妇人妻午夜寂寞影院 | 国产精品久久久 | 西西人体www44rt大胆高清 | 国产高清不卡无码视频 | 红桃av一区二区三区在线无码av | 激情国产av做激情国产爱 | 免费看男女做好爽好硬视频 | 人妻少妇精品无码专区动漫 | 中文久久乱码一区二区 | 男女猛烈xx00免费视频试看 | 亚洲男人av香蕉爽爽爽爽 | 九九久久精品国产免费看小说 | 人妻互换免费中文字幕 | 亚洲精品国产a久久久久久 | 久久久久久久人妻无码中文字幕爆 | 国精产品一品二品国精品69xx | 成人亚洲精品久久久久软件 | 亚洲中文无码av永久不收费 | 国产美女极度色诱视频www | 精品午夜福利在线观看 | 午夜性刺激在线视频免费 | 激情内射日本一区二区三区 | 国产激情精品一区二区三区 | 欧美成人家庭影院 | 国产人妖乱国产精品人妖 | 99久久亚洲精品无码毛片 | 精品国产一区二区三区av 性色 | 永久黄网站色视频免费直播 | 未满成年国产在线观看 | 国产黑色丝袜在线播放 | 国产亚洲精品久久久久久 | 精品人妻av区 | 给我免费的视频在线观看 | 亚洲日韩精品欧美一区二区 | 无码人妻精品一区二区三区下载 | 亚洲精品国偷拍自产在线麻豆 | 欧美日本免费一区二区三区 | 国产精品多人p群无码 | 日韩欧美中文字幕在线三区 | 亚洲小说图区综合在线 | 男女猛烈xx00免费视频试看 | 婷婷五月综合缴情在线视频 | 精品无码一区二区三区的天堂 | 精品人妻av区 | 噜噜噜亚洲色成人网站 | 国产精品内射视频免费 | 亚洲阿v天堂在线 | 成人欧美一区二区三区黑人 | 午夜福利一区二区三区在线观看 | 又湿又紧又大又爽a视频国产 | 帮老师解开蕾丝奶罩吸乳网站 | 一个人看的视频www在线 | 99麻豆久久久国产精品免费 | 丰满少妇弄高潮了www | 精品欧美一区二区三区久久久 | 福利一区二区三区视频在线观看 | 狠狠色欧美亚洲狠狠色www | 国产真实夫妇视频 | 国产综合久久久久鬼色 | 女人被爽到呻吟gif动态图视看 | 日本一区二区更新不卡 | 亚洲日韩一区二区 | 性欧美疯狂xxxxbbbb | 欧美 丝袜 自拍 制服 另类 | 一二三四在线观看免费视频 | 久久精品人妻少妇一区二区三区 | 亚洲 a v无 码免 费 成 人 a v | 西西人体www44rt大胆高清 | 在线亚洲高清揄拍自拍一品区 | 粗大的内捧猛烈进出视频 | 久久精品国产日本波多野结衣 | 国产亚洲人成在线播放 | 毛片内射-百度 | 最近中文2019字幕第二页 | 亚洲天堂2017无码 | 色婷婷香蕉在线一区二区 | 国产精品办公室沙发 | 狠狠躁日日躁夜夜躁2020 | 欧美日韩一区二区综合 | 中文字幕无码日韩专区 | 久久无码人妻影院 | 美女黄网站人色视频免费国产 | 精品国产一区二区三区四区在线看 | 麻豆av传媒蜜桃天美传媒 | 国产suv精品一区二区五 | 水蜜桃色314在线观看 | 亚洲中文字幕久久无码 | 国产精品久久久久7777 | 老子影院午夜伦不卡 | 领导边摸边吃奶边做爽在线观看 | 人妻无码αv中文字幕久久琪琪布 | √天堂中文官网8在线 | 熟女体下毛毛黑森林 | 蜜臀av在线播放 久久综合激激的五月天 | 国产精品久久久久久久9999 | 国产av剧情md精品麻豆 | 人人妻人人澡人人爽欧美一区九九 | 免费无码午夜福利片69 | 在线看片无码永久免费视频 | 国产真实夫妇视频 | 亚洲人成人无码网www国产 | 最新版天堂资源中文官网 | 日本精品人妻无码免费大全 | 国产免费观看黄av片 | 亚洲国产日韩a在线播放 | 成熟妇人a片免费看网站 | 亚洲中文字幕在线无码一区二区 | 国产免费久久久久久无码 | 大乳丰满人妻中文字幕日本 | 欧美 亚洲 国产 另类 | 久久精品女人的天堂av | 亚洲男人av香蕉爽爽爽爽 | 日本高清一区免费中文视频 | 婷婷五月综合激情中文字幕 | 色婷婷综合中文久久一本 | 亚洲 高清 成人 动漫 | 99精品无人区乱码1区2区3区 | 精品久久久久久亚洲精品 | 国产内射老熟女aaaa | 亚洲精品综合一区二区三区在线 | 无码任你躁久久久久久久 | 18精品久久久无码午夜福利 | 大胆欧美熟妇xx | 日本一区二区更新不卡 | 久精品国产欧美亚洲色aⅴ大片 | 国产精品手机免费 | 日韩精品无码一区二区中文字幕 | аⅴ资源天堂资源库在线 | 国产成人无码区免费内射一片色欲 | 奇米影视7777久久精品人人爽 | 野外少妇愉情中文字幕 | 亚洲码国产精品高潮在线 | 久久99精品国产.久久久久 | 人妻有码中文字幕在线 | 亚洲国产欧美国产综合一区 | 色婷婷av一区二区三区之红樱桃 | a片在线免费观看 | 亚洲日韩乱码中文无码蜜桃臀网站 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 欧洲熟妇色 欧美 | 无码乱肉视频免费大全合集 | 乱人伦中文视频在线观看 | 欧美日韩一区二区三区自拍 | 极品嫩模高潮叫床 | 午夜无码人妻av大片色欲 | 欧美人与禽zoz0性伦交 | 久热国产vs视频在线观看 | 人妻体内射精一区二区三四 | 国产区女主播在线观看 | 色情久久久av熟女人妻网站 | 精品少妇爆乳无码av无码专区 | 欧美人妻一区二区三区 | 男女爱爱好爽视频免费看 | 狂野欧美激情性xxxx | 国产性生大片免费观看性 | 国产精品永久免费视频 | 精品久久久中文字幕人妻 | 亚洲一区二区三区偷拍女厕 | 成人免费视频在线观看 | 自拍偷自拍亚洲精品10p | 天天躁夜夜躁狠狠是什么心态 | 亚洲精品久久久久中文第一幕 | 大肉大捧一进一出视频出来呀 | 伊人色综合久久天天小片 | 久久久久国色av免费观看性色 | 精品无码国产自产拍在线观看蜜 | 天天躁日日躁狠狠躁免费麻豆 | 日日摸夜夜摸狠狠摸婷婷 | 国产高清不卡无码视频 | 国产午夜福利亚洲第一 | 无套内谢的新婚少妇国语播放 | 97精品国产97久久久久久免费 | 樱花草在线播放免费中文 | 国产人妻人伦精品1国产丝袜 | 在线播放无码字幕亚洲 | 国模大胆一区二区三区 | 色老头在线一区二区三区 | 欧美性猛交xxxx富婆 | 中文字幕无码乱人伦 | 久久精品人妻少妇一区二区三区 | 纯爱无遮挡h肉动漫在线播放 | 国产97人人超碰caoprom | 亚洲精品国产第一综合99久久 | 国产午夜精品一区二区三区嫩草 | 亚洲 日韩 欧美 成人 在线观看 | 国产人妻精品一区二区三区不卡 | 对白脏话肉麻粗话av | 精品人妻人人做人人爽 | 国产亚洲精品久久久久久国模美 | 色欲久久久天天天综合网精品 | 久久精品成人欧美大片 | 免费观看又污又黄的网站 | 亚洲精品www久久久 | 老子影院午夜精品无码 | 亚洲熟妇自偷自拍另类 | 无码毛片视频一区二区本码 | 中文字幕人妻无码一夲道 | 无码人妻黑人中文字幕 | 无码一区二区三区在线观看 | 亚洲成色www久久网站 | 国产亚洲美女精品久久久2020 | 97久久精品无码一区二区 | 最近的中文字幕在线看视频 | 亚欧洲精品在线视频免费观看 | 天堂无码人妻精品一区二区三区 | 亚洲精品一区国产 | 午夜精品一区二区三区在线观看 | 最新国产乱人伦偷精品免费网站 | 四虎4hu永久免费 | 亚洲成色在线综合网站 | 麻豆国产人妻欲求不满谁演的 | 国产办公室秘书无码精品99 | 天天综合网天天综合色 | 999久久久国产精品消防器材 | 人妻aⅴ无码一区二区三区 | 色老头在线一区二区三区 | 欧美三级a做爰在线观看 | 国产超级va在线观看视频 | 国产亚av手机在线观看 | 免费无码肉片在线观看 | 又黄又爽又色的视频 | 对白脏话肉麻粗话av | 亚洲成av人片天堂网无码】 | 大肉大捧一进一出视频出来呀 | 国产亚洲精品久久久久久久 | 欧美zoozzooz性欧美 | 免费中文字幕日韩欧美 | 精品久久综合1区2区3区激情 | 日本在线高清不卡免费播放 | 国产激情精品一区二区三区 | 国精品人妻无码一区二区三区蜜柚 | 欧美激情内射喷水高潮 | 偷窥村妇洗澡毛毛多 | 激情内射日本一区二区三区 | 久久天天躁狠狠躁夜夜免费观看 | 少妇无码av无码专区在线观看 | 中文字幕色婷婷在线视频 | 99久久精品日本一区二区免费 | 国产xxx69麻豆国语对白 | 国产 浪潮av性色四虎 | 亚洲国产精品无码久久久久高潮 | 99久久精品国产一区二区蜜芽 | 国产精品久久久久久久9999 | 人人妻人人澡人人爽人人精品浪潮 | 亚洲精品国产品国语在线观看 | 亚洲人成网站免费播放 | 成 人 免费观看网站 | 精品国偷自产在线视频 | 日本大香伊一区二区三区 | 久久久久亚洲精品中文字幕 | 国产亚洲精品久久久久久 | 国产无遮挡吃胸膜奶免费看 | 日日碰狠狠躁久久躁蜜桃 | 久久亚洲精品成人无码 | 国产特级毛片aaaaaa高潮流水 | 亚洲成熟女人毛毛耸耸多 | 日产精品99久久久久久 | 国产一区二区三区影院 | 国产欧美精品一区二区三区 | 久久精品99久久香蕉国产色戒 | 亚洲精品成人av在线 | av无码电影一区二区三区 | 18无码粉嫩小泬无套在线观看 | 樱花草在线社区www | 久久人人爽人人爽人人片av高清 | 色婷婷av一区二区三区之红樱桃 | 九九久久精品国产免费看小说 | 牲欲强的熟妇农村老妇女视频 | 少妇性俱乐部纵欲狂欢电影 | 国产人妻大战黑人第1集 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 美女毛片一区二区三区四区 | 三级4级全黄60分钟 | 亚洲国产精品无码一区二区三区 | 又黄又爽又色的视频 | 麻花豆传媒剧国产免费mv在线 | 国产成人无码午夜视频在线观看 | 久久久久人妻一区精品色欧美 | 欧美成人家庭影院 | 亚洲色欲久久久综合网东京热 | 亚洲成av人综合在线观看 | 激情五月综合色婷婷一区二区 | 伦伦影院午夜理论片 | 久久精品人妻少妇一区二区三区 | 久久久久久久女国产乱让韩 | 狠狠噜狠狠狠狠丁香五月 | 久久久久亚洲精品中文字幕 | 午夜无码区在线观看 | 国产精品99爱免费视频 | 中文字幕无码视频专区 | 午夜嘿嘿嘿影院 | 性生交大片免费看女人按摩摩 | 大地资源网第二页免费观看 | 国产国产精品人在线视 | 亚洲欧美综合区丁香五月小说 | 日本爽爽爽爽爽爽在线观看免 | 日本精品人妻无码免费大全 | 国产卡一卡二卡三 | 亚洲人亚洲人成电影网站色 | 97色伦图片97综合影院 | 东京热男人av天堂 | yw尤物av无码国产在线观看 | 成人片黄网站色大片免费观看 | 久久综合给久久狠狠97色 | 无套内谢的新婚少妇国语播放 | 99久久婷婷国产综合精品青草免费 | 国产精品嫩草久久久久 | 天下第一社区视频www日本 | 日本一区二区三区免费播放 | 无码一区二区三区在线观看 | 疯狂三人交性欧美 | 乱人伦中文视频在线观看 | 伊人久久大香线蕉亚洲 | ass日本丰满熟妇pics | 国产成人亚洲综合无码 | 伦伦影院午夜理论片 | 久久久成人毛片无码 | 成在人线av无码免费 | 国产精品无码一区二区桃花视频 | 青青久在线视频免费观看 | 国产av无码专区亚洲a∨毛片 | 暴力强奷在线播放无码 | 乱人伦中文视频在线观看 | 99在线 | 亚洲 | 成年美女黄网站色大免费视频 | 精品成在人线av无码免费看 | 精品无人国产偷自产在线 | 亚洲一区二区三区国产精华液 | 久久99热只有频精品8 | 久久无码专区国产精品s | 午夜成人1000部免费视频 | 日日躁夜夜躁狠狠躁 | 高清国产亚洲精品自在久久 | 国内精品久久久久久中文字幕 | 噜噜噜亚洲色成人网站 | 日韩亚洲欧美精品综合 | 欧美激情内射喷水高潮 | 欧美喷潮久久久xxxxx | 麻豆精品国产精华精华液好用吗 | 精品一区二区不卡无码av | 精品一区二区三区波多野结衣 | 亚洲啪av永久无码精品放毛片 | 国产激情艳情在线看视频 | 亚洲精品无码人妻无码 | 亚洲无人区一区二区三区 | 久久99精品久久久久婷婷 | 日本精品少妇一区二区三区 | 性色欲情网站iwww九文堂 | 日韩人妻系列无码专区 | 久久久久亚洲精品中文字幕 | 欧美丰满熟妇xxxx性ppx人交 | 免费无码的av片在线观看 | 国产午夜精品一区二区三区嫩草 | 色欲人妻aaaaaaa无码 | 国产精品美女久久久久av爽李琼 | 中文字幕无码人妻少妇免费 | 久久天天躁夜夜躁狠狠 | 在线观看免费人成视频 | 麻豆成人精品国产免费 | 熟女少妇在线视频播放 | 成人精品一区二区三区中文字幕 | 亚洲日韩av一区二区三区四区 | 国产精品无码成人午夜电影 | 香蕉久久久久久av成人 | 人人妻人人澡人人爽欧美一区 | 国产精品怡红院永久免费 | 精品人妻人人做人人爽夜夜爽 | 中文精品无码中文字幕无码专区 | 人妻aⅴ无码一区二区三区 | 一本久久a久久精品vr综合 | 久久国内精品自在自线 | 高清不卡一区二区三区 | 在线观看欧美一区二区三区 | 色综合久久中文娱乐网 | 秋霞成人午夜鲁丝一区二区三区 | 好男人www社区 | 乱人伦中文视频在线观看 | 青草青草久热国产精品 | 亚洲精品无码人妻无码 | 一本大道久久东京热无码av | 亚洲日韩av片在线观看 | av无码久久久久不卡免费网站 | 国产精品美女久久久网av | 国产九九九九九九九a片 | 亚洲经典千人经典日产 | 欧美性色19p | 夜夜高潮次次欢爽av女 | 国产特级毛片aaaaaaa高清 | 欧美黑人性暴力猛交喷水 | 中文无码成人免费视频在线观看 | 久久99精品国产麻豆蜜芽 | 久久精品人人做人人综合 | 狠狠色色综合网站 | 98国产精品综合一区二区三区 | 精品无人区无码乱码毛片国产 | 精品国产一区二区三区四区 | 久久99精品国产.久久久久 | 久久熟妇人妻午夜寂寞影院 | 国产99久久精品一区二区 | 国产无遮挡又黄又爽免费视频 | 久久久久久久人妻无码中文字幕爆 | 人人妻人人澡人人爽精品欧美 | 伊人色综合久久天天小片 | 色欲久久久天天天综合网精品 | 国产精品成人av在线观看 | 午夜精品久久久久久久久 | 永久免费精品精品永久-夜色 | 亚洲va中文字幕无码久久不卡 | 免费人成在线视频无码 | 国产精品手机免费 | 久久精品一区二区三区四区 | 国产日产欧产精品精品app | 欧美亚洲日韩国产人成在线播放 | 国产 精品 自在自线 | 久久精品中文字幕大胸 | 国产av久久久久精东av | 成人无码影片精品久久久 | 色窝窝无码一区二区三区色欲 | 免费无码午夜福利片69 | 国产精品18久久久久久麻辣 | 国产在热线精品视频 | 久久精品人人做人人综合 | 老头边吃奶边弄进去呻吟 | 少妇性l交大片欧洲热妇乱xxx | 亚洲人成网站色7799 | 兔费看少妇性l交大片免费 | 精品国偷自产在线 | 国产成人一区二区三区在线观看 | 久久国语露脸国产精品电影 | 久久国产精品_国产精品 | 欧美freesex黑人又粗又大 | 成人无码影片精品久久久 | 无人区乱码一区二区三区 | 任你躁国产自任一区二区三区 | 亚洲精品一区国产 | 狂野欧美性猛交免费视频 | 亚洲国产综合无码一区 | 亚洲 日韩 欧美 成人 在线观看 | 性欧美熟妇videofreesex | 日韩精品无码免费一区二区三区 | 欧美精品无码一区二区三区 | 97久久精品无码一区二区 | 久久国产36精品色熟妇 | 香港三级日本三级妇三级 | 精品久久综合1区2区3区激情 | 亚洲乱亚洲乱妇50p | 国产99久久精品一区二区 | 亚洲国产一区二区三区在线观看 | 少妇性俱乐部纵欲狂欢电影 | 国产激情综合五月久久 | aa片在线观看视频在线播放 | 熟妇激情内射com | 熟女俱乐部五十路六十路av | 国产99久久精品一区二区 | 亚洲午夜无码久久 | 久久国产精品精品国产色婷婷 | 亚洲欧美色中文字幕在线 | 无码福利日韩神码福利片 | 色婷婷av一区二区三区之红樱桃 | 日本大香伊一区二区三区 | 久久久久成人片免费观看蜜芽 | 人人妻人人藻人人爽欧美一区 | 国产97在线 | 亚洲 | 激情综合激情五月俺也去 | 亚洲の无码国产の无码步美 | 又大又硬又黄的免费视频 | 国产精品久久久一区二区三区 | 亚洲爆乳精品无码一区二区三区 | 久久国产自偷自偷免费一区调 | 未满成年国产在线观看 | 亚洲人成无码网www | 在线亚洲高清揄拍自拍一品区 | 亚洲一区av无码专区在线观看 | 无码人妻黑人中文字幕 | 久久亚洲中文字幕精品一区 | 亚洲色大成网站www国产 | 真人与拘做受免费视频 | 牲欲强的熟妇农村老妇女视频 | 久久久久av无码免费网 | 亚洲国产精品美女久久久久 | 免费观看又污又黄的网站 | 男女超爽视频免费播放 | 日韩欧美群交p片內射中文 | 小sao货水好多真紧h无码视频 | 国产成人精品久久亚洲高清不卡 | 国产精品手机免费 | 高清国产亚洲精品自在久久 | 免费无码一区二区三区蜜桃大 | 西西人体www44rt大胆高清 | 亚洲精品午夜国产va久久成人 | 久久久久免费看成人影片 | 成人欧美一区二区三区黑人免费 | 国产人妻人伦精品1国产丝袜 | 美女极度色诱视频国产 | 99er热精品视频 | 99久久无码一区人妻 | 日韩精品无码一区二区中文字幕 | 乱码午夜-极国产极内射 | 亚洲 激情 小说 另类 欧美 | 欧美日本免费一区二区三区 | 国产亚洲精品久久久闺蜜 | 少妇的肉体aa片免费 | 久久综合激激的五月天 | 色欲久久久天天天综合网精品 | 76少妇精品导航 | 麻豆果冻传媒2021精品传媒一区下载 | 无码免费一区二区三区 | 福利一区二区三区视频在线观看 | 蜜臀av无码人妻精品 | 丰满妇女强制高潮18xxxx | 西西人体www44rt大胆高清 | 少妇无码av无码专区在线观看 | 又紧又大又爽精品一区二区 | 99精品无人区乱码1区2区3区 | 欧美亚洲国产一区二区三区 | 国产成人综合色在线观看网站 | 少妇厨房愉情理9仑片视频 | 国产两女互慰高潮视频在线观看 | 色综合久久88色综合天天 | 99精品无人区乱码1区2区3区 | 无码人中文字幕 | 国产精品沙发午睡系列 | 国产无遮挡吃胸膜奶免费看 | 男人的天堂av网站 | 男女猛烈xx00免费视频试看 | 亚洲人成影院在线观看 | 乱码av麻豆丝袜熟女系列 | 天天爽夜夜爽夜夜爽 | 国产国语老龄妇女a片 | 色综合久久网 | 55夜色66夜色国产精品视频 | 蜜桃视频插满18在线观看 | 人妻中文无码久热丝袜 | 伊人久久大香线蕉av一区二区 | 成 人 网 站国产免费观看 | 黑人大群体交免费视频 | 国产内射老熟女aaaa | 四虎永久在线精品免费网址 | 一本加勒比波多野结衣 | 亚洲熟妇色xxxxx亚洲 | 3d动漫精品啪啪一区二区中 | 性生交片免费无码看人 | 欧美精品免费观看二区 | 国产午夜亚洲精品不卡下载 | 亚洲熟妇色xxxxx欧美老妇y | 久久人人爽人人爽人人片ⅴ | 久久久成人毛片无码 | 亚洲国产欧美在线成人 | 国产激情无码一区二区app | 三上悠亚人妻中文字幕在线 | 国产成人无码区免费内射一片色欲 | 日本护士xxxxhd少妇 | 亚洲成a人一区二区三区 | 极品尤物被啪到呻吟喷水 | 日日橹狠狠爱欧美视频 | 亚无码乱人伦一区二区 | 丰满妇女强制高潮18xxxx | aⅴ亚洲 日韩 色 图网站 播放 | 亚洲综合久久一区二区 | 久久午夜夜伦鲁鲁片无码免费 | 内射爽无广熟女亚洲 | 一个人看的www免费视频在线观看 | 中文字幕 亚洲精品 第1页 | 在线亚洲高清揄拍自拍一品区 | 久久久久亚洲精品男人的天堂 | 精品乱子伦一区二区三区 | 无码人中文字幕 | 理论片87福利理论电影 | 日韩av无码一区二区三区 | a片在线免费观看 | 丝袜足控一区二区三区 | 又大又紧又粉嫩18p少妇 | 亚洲国产欧美在线成人 | 樱花草在线社区www | 女人高潮内射99精品 | 国产成人午夜福利在线播放 | 久久久久亚洲精品男人的天堂 | 久久精品人人做人人综合 | 日韩人妻少妇一区二区三区 | 成人毛片一区二区 | 国产97色在线 | 免 | 欧美黑人巨大xxxxx | 国产高清av在线播放 | 人妻天天爽夜夜爽一区二区 | 国产高清不卡无码视频 | 日本一区二区三区免费高清 | 麻豆成人精品国产免费 | 欧美喷潮久久久xxxxx | 日日碰狠狠丁香久燥 | 影音先锋中文字幕无码 | 欧美人与物videos另类 | 久久精品中文字幕大胸 | 人妻少妇精品无码专区动漫 | 国产真人无遮挡作爱免费视频 | 日韩视频 中文字幕 视频一区 | 国产精品人妻一区二区三区四 | 亚洲精品午夜无码电影网 | 久久99精品国产麻豆蜜芽 | av在线亚洲欧洲日产一区二区 | 欧美丰满老熟妇xxxxx性 | 中文字幕无线码 | 国产成人无码区免费内射一片色欲 | 成人动漫在线观看 | 老头边吃奶边弄进去呻吟 | 中文字幕人妻丝袜二区 | 中文字幕无码av波多野吉衣 | 午夜福利一区二区三区在线观看 | 国产乱人无码伦av在线a | 99国产欧美久久久精品 | 少妇无码一区二区二三区 | 女高中生第一次破苞av | 午夜无码区在线观看 | 在线观看国产午夜福利片 | 精品国产国产综合精品 | 啦啦啦www在线观看免费视频 | 少妇愉情理伦片bd | 67194成是人免费无码 | 水蜜桃色314在线观看 | 色欲人妻aaaaaaa无码 | 欧美一区二区三区 | 一本色道久久综合亚洲精品不卡 | 亚洲国产日韩a在线播放 | 四虎4hu永久免费 | 少妇人妻av毛片在线看 | 国产人妻大战黑人第1集 | 国产精品亚洲一区二区三区喷水 | 亚洲中文字幕成人无码 | 成人试看120秒体验区 | 亚洲人成影院在线观看 | 全黄性性激高免费视频 | 久久久久久亚洲精品a片成人 | 给我免费的视频在线观看 | 欧美肥老太牲交大战 | 国产九九九九九九九a片 | 丰满少妇弄高潮了www | 国产又粗又硬又大爽黄老大爷视 | 99久久精品日本一区二区免费 | 久久99精品久久久久久 | 国产成人无码一二三区视频 | 国产乱子伦视频在线播放 | 婷婷六月久久综合丁香 | 成人女人看片免费视频放人 | 波多野结衣av一区二区全免费观看 | 亚洲а∨天堂久久精品2021 | 日日夜夜撸啊撸 | 少妇无码吹潮 | 国产小呦泬泬99精品 | 乱人伦中文视频在线观看 | 精品无码一区二区三区爱欲 | 亚洲综合无码久久精品综合 | 纯爱无遮挡h肉动漫在线播放 | 乌克兰少妇性做爰 | 性做久久久久久久免费看 | 国产麻豆精品精东影业av网站 | 偷窥村妇洗澡毛毛多 | 免费无码肉片在线观看 | 国产黑色丝袜在线播放 | 人人妻人人澡人人爽人人精品浪潮 | 欧美午夜特黄aaaaaa片 | 精品无码成人片一区二区98 | 无码精品国产va在线观看dvd | 成人无码视频在线观看网站 | 极品嫩模高潮叫床 | 精品日本一区二区三区在线观看 | 亚洲精品成人av在线 | 亚洲成色www久久网站 | 亚洲精品国偷拍自产在线观看蜜桃 | 中文无码成人免费视频在线观看 | 亚洲国产成人av在线观看 | 亚洲熟妇色xxxxx亚洲 | 久久这里只有精品视频9 | 波多野结衣av在线观看 | 无遮无挡爽爽免费视频 | 国产成人精品久久亚洲高清不卡 | 97无码免费人妻超级碰碰夜夜 | 人人爽人人爽人人片av亚洲 | 人人妻人人藻人人爽欧美一区 | 乌克兰少妇xxxx做受 | 精品熟女少妇av免费观看 | v一区无码内射国产 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 色窝窝无码一区二区三区色欲 | 国产精品无码久久av | 国产疯狂伦交大片 | 亚洲啪av永久无码精品放毛片 | 97无码免费人妻超级碰碰夜夜 | 欧美国产日产一区二区 | 蜜桃视频韩日免费播放 | 国产麻豆精品一区二区三区v视界 | 亚洲日韩中文字幕在线播放 | 日本精品人妻无码免费大全 | 丰满妇女强制高潮18xxxx | 亚洲国产欧美国产综合一区 | 四虎永久在线精品免费网址 | 亚洲成a人片在线观看无码3d | 少妇被粗大的猛进出69影院 | 成人试看120秒体验区 | 东京热一精品无码av | 亚洲自偷精品视频自拍 | 欧美人与牲动交xxxx | 午夜不卡av免费 一本久久a久久精品vr综合 | 亚洲国产精品一区二区美利坚 | 国内精品一区二区三区不卡 | 在线成人www免费观看视频 | 狠狠色丁香久久婷婷综合五月 | www国产亚洲精品久久久日本 | 久久久久国色av免费观看性色 | 中文字幕乱码亚洲无线三区 | 国产午夜视频在线观看 | 久久99精品久久久久久动态图 | 欧美三级不卡在线观看 | 97精品国产97久久久久久免费 | 亚洲中文无码av永久不收费 | 76少妇精品导航 | 少妇性l交大片欧洲热妇乱xxx | 亚洲中文字幕av在天堂 | 欧美日韩视频无码一区二区三 | 97久久精品无码一区二区 | 欧美午夜特黄aaaaaa片 | 性生交片免费无码看人 | 荡女精品导航 | 欧美人与禽猛交狂配 | 中文字幕无码乱人伦 | 亚洲国产欧美在线成人 | 久久久久成人片免费观看蜜芽 | 999久久久国产精品消防器材 | 中文字幕无码人妻少妇免费 | 日产精品高潮呻吟av久久 | 女人被男人爽到呻吟的视频 | 国产高清av在线播放 | 中文字幕 亚洲精品 第1页 | 精品久久久久久人妻无码中文字幕 | 精品少妇爆乳无码av无码专区 | 国产九九九九九九九a片 | 人妻插b视频一区二区三区 | 鲁一鲁av2019在线 | 国产精品国产自线拍免费软件 | 国产办公室秘书无码精品99 | 玩弄人妻少妇500系列视频 | 人妻尝试又大又粗久久 | 99久久亚洲精品无码毛片 | 无码精品国产va在线观看dvd | 丁香啪啪综合成人亚洲 | 亚洲毛片av日韩av无码 | 免费中文字幕日韩欧美 | 少妇被粗大的猛进出69影院 | 亚洲精品一区三区三区在线观看 | 真人与拘做受免费视频一 | 午夜丰满少妇性开放视频 | 国模大胆一区二区三区 | 给我免费的视频在线观看 | 欧美黑人性暴力猛交喷水 | 一本一道久久综合久久 | 久久国产36精品色熟妇 | 日本免费一区二区三区最新 | 青草青草久热国产精品 | 国产av人人夜夜澡人人爽麻豆 | 国产口爆吞精在线视频 | 国产亚洲精品久久久久久国模美 | 在线精品国产一区二区三区 | 亚洲国产精品无码久久久久高潮 | 一本久道高清无码视频 | 国产精品久久久久久亚洲毛片 | 国产综合久久久久鬼色 | 扒开双腿吃奶呻吟做受视频 | 在线观看欧美一区二区三区 | 精品国产一区二区三区四区在线看 | 国产成人精品优优av | 成人性做爰aaa片免费看不忠 | 国产麻豆精品精东影业av网站 | 精品偷拍一区二区三区在线看 | 一区二区三区乱码在线 | 欧洲 | 网友自拍区视频精品 | 欧洲精品码一区二区三区免费看 | 欧美第一黄网免费网站 | 色欲av亚洲一区无码少妇 | 久久精品成人欧美大片 | 亚洲乱码国产乱码精品精 | 动漫av网站免费观看 | 国产一区二区三区四区五区加勒比 | 丰满肥臀大屁股熟妇激情视频 | 国产在线一区二区三区四区五区 | 亚洲国产欧美国产综合一区 | 亚洲一区二区三区含羞草 | 日韩av无码一区二区三区 | 曰韩无码二三区中文字幕 | 亚洲 a v无 码免 费 成 人 a v | 国产成人综合在线女婷五月99播放 | 在线精品国产一区二区三区 | 国产人妻久久精品二区三区老狼 | 无码人妻出轨黑人中文字幕 | 久久人人97超碰a片精品 | 日产国产精品亚洲系列 | 强辱丰满人妻hd中文字幕 | 亚洲一区二区三区 | 亚洲自偷精品视频自拍 | 亚洲国产精品一区二区第一页 | 国产成人精品视频ⅴa片软件竹菊 | 高清不卡一区二区三区 | 无码毛片视频一区二区本码 | 人人妻人人藻人人爽欧美一区 | 又色又爽又黄的美女裸体网站 | 狠狠躁日日躁夜夜躁2020 | 精品一二三区久久aaa片 | 日韩精品乱码av一区二区 | 日本又色又爽又黄的a片18禁 | 午夜精品一区二区三区的区别 | 国产成人精品视频ⅴa片软件竹菊 | 国产色在线 | 国产 | 国产三级久久久精品麻豆三级 | 亚洲精品久久久久久久久久久 | 国产色xx群视频射精 | 在线а√天堂中文官网 | 永久免费精品精品永久-夜色 | 女人色极品影院 | 精品国产乱码久久久久乱码 | 中文无码成人免费视频在线观看 | 久久97精品久久久久久久不卡 | 国产婷婷色一区二区三区在线 | 欧洲熟妇精品视频 | 在线观看欧美一区二区三区 | 国产一区二区三区四区五区加勒比 | 成 人 免费观看网站 | 狂野欧美激情性xxxx | 蜜桃av抽搐高潮一区二区 | 国产亚洲日韩欧美另类第八页 | 国产激情无码一区二区app | 丝袜 中出 制服 人妻 美腿 | 俄罗斯老熟妇色xxxx | 色一情一乱一伦一视频免费看 | 精品欧美一区二区三区久久久 | 免费无码一区二区三区蜜桃大 | 久久久久久av无码免费看大片 | 国产明星裸体无码xxxx视频 | 天天摸天天透天天添 | 无码一区二区三区在线观看 | 无码国产乱人伦偷精品视频 | 四虎影视成人永久免费观看视频 | 亚洲国产精品无码一区二区三区 | 1000部啪啪未满十八勿入下载 | 日韩精品无码一区二区中文字幕 | 精品国产乱码久久久久乱码 | 99在线 | 亚洲 | 国产精品美女久久久网av | 日韩av无码一区二区三区不卡 | 四虎国产精品一区二区 | 在线精品亚洲一区二区 | 日本丰满熟妇videos | 国产黑色丝袜在线播放 | 中文字幕无码av激情不卡 | 中文字幕人妻丝袜二区 | 黑森林福利视频导航 | 国产美女极度色诱视频www | 超碰97人人做人人爱少妇 | 99久久精品午夜一区二区 | 一本色道久久综合亚洲精品不卡 | 国产精品久久国产精品99 | 内射老妇bbwx0c0ck | 99久久无码一区人妻 | 亚洲综合在线一区二区三区 | 国产精品久久久午夜夜伦鲁鲁 | 麻豆国产人妻欲求不满 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 在线精品亚洲一区二区 | 18黄暴禁片在线观看 | 久久精品无码一区二区三区 | 未满成年国产在线观看 | 丁香花在线影院观看在线播放 | 亚洲娇小与黑人巨大交 | 强辱丰满人妻hd中文字幕 | 国产精品人妻一区二区三区四 | 5858s亚洲色大成网站www | 99久久精品日本一区二区免费 | 久久亚洲中文字幕无码 | 精品久久8x国产免费观看 | 精品久久久中文字幕人妻 | 天堂亚洲2017在线观看 | 亚洲日本一区二区三区在线 | 久久99精品久久久久久动态图 | 无码播放一区二区三区 | 久久久久成人精品免费播放动漫 | 久久精品人妻少妇一区二区三区 | 久久精品人人做人人综合 | 四十如虎的丰满熟妇啪啪 | 无码人妻精品一区二区三区下载 | 亚洲国产精品毛片av不卡在线 | 欧美日韩视频无码一区二区三 | 性生交大片免费看女人按摩摩 | 少妇性俱乐部纵欲狂欢电影 | 精品久久久无码中文字幕 | 无码人妻精品一区二区三区下载 | 好爽又高潮了毛片免费下载 | 亚洲欧美国产精品专区久久 | 中文字幕无码乱人伦 | 国产成人亚洲综合无码 | 婷婷综合久久中文字幕蜜桃三电影 | 久久99久久99精品中文字幕 | 老熟妇仑乱视频一区二区 | 日本精品高清一区二区 | 国内丰满熟女出轨videos | 狂野欧美激情性xxxx | 亚洲欧美精品伊人久久 | 影音先锋中文字幕无码 | 野外少妇愉情中文字幕 | 免费无码午夜福利片69 | 熟妇人妻无乱码中文字幕 | 久久综合色之久久综合 | 女人和拘做爰正片视频 | 亚洲小说春色综合另类 | 国产av剧情md精品麻豆 | www国产亚洲精品久久网站 | 日日干夜夜干 | 国产免费无码一区二区视频 | 亚洲精品成a人在线观看 | 牲交欧美兽交欧美 | 蜜桃视频韩日免费播放 | 国产又爽又猛又粗的视频a片 | 久久午夜无码鲁丝片午夜精品 | 欧美日韩一区二区免费视频 | 色一情一乱一伦 | 国产精品99久久精品爆乳 | 免费无码肉片在线观看 | 爱做久久久久久 | 成人亚洲精品久久久久 | 亚洲人成影院在线无码按摩店 | 国产精品亚洲综合色区韩国 | 四十如虎的丰满熟妇啪啪 | 国产精品成人av在线观看 | 帮老师解开蕾丝奶罩吸乳网站 | 久久午夜夜伦鲁鲁片无码免费 | 国色天香社区在线视频 | 亚洲国产av精品一区二区蜜芽 | 成人亚洲精品久久久久 | 亚洲国产欧美国产综合一区 | 亚洲日韩一区二区 | 国产成人无码专区 | 亚洲精品综合五月久久小说 | 老子影院午夜精品无码 | 国产一精品一av一免费 | 沈阳熟女露脸对白视频 | 老子影院午夜伦不卡 | 色狠狠av一区二区三区 | 久久久国产精品无码免费专区 | 亚洲国产欧美日韩精品一区二区三区 | 久久国产自偷自偷免费一区调 | 亚洲a无码综合a国产av中文 | 无码成人精品区在线观看 | 美女毛片一区二区三区四区 | 中文无码成人免费视频在线观看 | 欧美精品一区二区精品久久 | 欧美丰满老熟妇xxxxx性 | 精品国产一区二区三区av 性色 | 国产av人人夜夜澡人人爽麻豆 | 久久亚洲国产成人精品性色 | 日本熟妇人妻xxxxx人hd | 亚洲成在人网站无码天堂 | 在线а√天堂中文官网 | 人人妻人人澡人人爽人人精品浪潮 | 欧美日韩视频无码一区二区三 | 久久亚洲中文字幕精品一区 | 欧美日韩在线亚洲综合国产人 | 精品欧美一区二区三区久久久 | 99久久人妻精品免费二区 | 久久久久99精品国产片 | 真人与拘做受免费视频 | 日本大香伊一区二区三区 | 狂野欧美性猛交免费视频 | 国产成人精品久久亚洲高清不卡 | 精品国产成人一区二区三区 | 国产乱子伦视频在线播放 | 国产精品久久国产三级国 | 国产人妻人伦精品 | 日韩精品无码免费一区二区三区 | 国产国产精品人在线视 | 人人爽人人澡人人人妻 | 夫妻免费无码v看片 | 亚洲中文字幕久久无码 | 亚洲日本一区二区三区在线 | 亚洲熟熟妇xxxx | 两性色午夜视频免费播放 | 最新国产麻豆aⅴ精品无码 | 真人与拘做受免费视频一 | 欧美日韩久久久精品a片 | 青青青手机频在线观看 | 国产精品第一国产精品 | 国产精品办公室沙发 | 久久国产精品萌白酱免费 | 亚洲小说图区综合在线 | 在线亚洲高清揄拍自拍一品区 | 成人精品视频一区二区三区尤物 | 久久久久久a亚洲欧洲av冫 | 欧美老妇交乱视频在线观看 | 国产精品沙发午睡系列 | 亚洲熟悉妇女xxx妇女av | 久久zyz资源站无码中文动漫 | 99riav国产精品视频 | 国产精品毛多多水多 | 久久久中文久久久无码 | 亚洲区欧美区综合区自拍区 | 在线播放免费人成毛片乱码 | 在线观看欧美一区二区三区 | 丝袜足控一区二区三区 | 国产精品高潮呻吟av久久4虎 | 亚洲熟妇自偷自拍另类 | 秋霞特色aa大片 | 无码精品国产va在线观看dvd | 日韩av无码中文无码电影 | 人妻插b视频一区二区三区 | 久久午夜无码鲁丝片 | 男人的天堂av网站 | 亚洲精品国偷拍自产在线麻豆 | 妺妺窝人体色www婷婷 | 午夜福利不卡在线视频 | 亚洲精品一区二区三区在线观看 | 丝袜人妻一区二区三区 | 欧美肥老太牲交大战 | 少妇高潮喷潮久久久影院 | 亚洲成色www久久网站 | 一个人看的www免费视频在线观看 | 日韩欧美成人免费观看 | 中文精品久久久久人妻不卡 | 亚洲国产欧美日韩精品一区二区三区 | 国产成人av免费观看 | 国产精品欧美成人 | 国产精品久久久久久久9999 | 成熟女人特级毛片www免费 | 亚欧洲精品在线视频免费观看 | 伊人久久大香线蕉av一区二区 | 国产午夜精品一区二区三区嫩草 | 一区二区三区乱码在线 | 欧洲 | 久久久国产一区二区三区 | 欧美人妻一区二区三区 | 偷窥日本少妇撒尿chinese | 熟女少妇人妻中文字幕 | 久久久久成人精品免费播放动漫 | 成人试看120秒体验区 | 精品无码一区二区三区爱欲 | 天天躁日日躁狠狠躁免费麻豆 | 国产成人精品视频ⅴa片软件竹菊 | 东京无码熟妇人妻av在线网址 | 爆乳一区二区三区无码 | 乌克兰少妇性做爰 | 97夜夜澡人人爽人人喊中国片 | 精品人妻av区 | 中文字幕乱妇无码av在线 | 国产精品免费大片 | 日产精品高潮呻吟av久久 | www一区二区www免费 | 亚洲娇小与黑人巨大交 | 青青久在线视频免费观看 | 色妞www精品免费视频 | 一个人看的www免费视频在线观看 | 女人色极品影院 | 一区二区三区高清视频一 | 亚洲精品成人av在线 | 成人性做爰aaa片免费看不忠 | 秋霞成人午夜鲁丝一区二区三区 | 妺妺窝人体色www在线小说 | 国产精品久久久久久久影院 | 国产精品亚洲五月天高清 | 国内揄拍国内精品少妇国语 | 欧美丰满熟妇xxxx性ppx人交 | 蜜桃视频韩日免费播放 | 男女超爽视频免费播放 | 少妇被黑人到高潮喷出白浆 | 人人澡人摸人人添 | 国产色视频一区二区三区 | 人妻少妇精品无码专区二区 | 亚洲一区二区三区含羞草 | 人人妻人人澡人人爽人人精品浪潮 | 色妞www精品免费视频 | 男女爱爱好爽视频免费看 | 久久久久成人片免费观看蜜芽 | 亚洲精品午夜国产va久久成人 | 亚洲国产精品无码久久久久高潮 | 99riav国产精品视频 | 强开小婷嫩苞又嫩又紧视频 | av在线亚洲欧洲日产一区二区 | 国产一区二区三区四区五区加勒比 | 久久久久久久久蜜桃 | 美女扒开屁股让男人桶 | 国产亚洲精品久久久久久国模美 | 欧美激情内射喷水高潮 | 精品国产aⅴ无码一区二区 | 熟妇女人妻丰满少妇中文字幕 | 国产精品久久久久影院嫩草 | 日韩精品久久久肉伦网站 | 欧美人与动性行为视频 | 亚洲一区二区三区偷拍女厕 | 欧美日本精品一区二区三区 | 国产尤物精品视频 | 亚洲乱码日产精品bd | 丰满人妻一区二区三区免费视频 | 少妇厨房愉情理9仑片视频 | 男人的天堂2018无码 | 成人免费无码大片a毛片 | 人妻天天爽夜夜爽一区二区 | 成年女人永久免费看片 | 午夜精品久久久久久久久 | 久久国产36精品色熟妇 | 精品无人国产偷自产在线 | 少妇性l交大片欧洲热妇乱xxx | 一本久道久久综合婷婷五月 | 国产精品嫩草久久久久 | 99久久99久久免费精品蜜桃 | 国产综合在线观看 | 东京无码熟妇人妻av在线网址 | 欧洲美熟女乱又伦 | 无码人妻精品一区二区三区下载 | 无码人妻丰满熟妇区毛片18 | 亚洲熟妇自偷自拍另类 | 成人亚洲精品久久久久软件 | 乱码午夜-极国产极内射 | 久久人人爽人人爽人人片ⅴ | 欧美阿v高清资源不卡在线播放 | 国内丰满熟女出轨videos | 亚洲欧洲无卡二区视頻 | 欧美zoozzooz性欧美 | 久久www免费人成人片 | 日本精品人妻无码77777 天堂一区人妻无码 | 久久久久人妻一区精品色欧美 | 成在人线av无码免费 | 亚洲中文字幕无码一久久区 | 伊人久久大香线蕉午夜 | 丰满护士巨好爽好大乳 | 久久久久av无码免费网 | 午夜不卡av免费 一本久久a久久精品vr综合 | 久久 国产 尿 小便 嘘嘘 | 日韩亚洲欧美精品综合 | 男人的天堂av网站 | 亚洲精品久久久久久久久久久 | 久久人人爽人人人人片 | 欧美放荡的少妇 | 荫蒂被男人添的好舒服爽免费视频 | 日日摸天天摸爽爽狠狠97 | 精品无码一区二区三区爱欲 | 四虎永久在线精品免费网址 | 国产人妻大战黑人第1集 | 国产精品久久久久9999小说 | 高清无码午夜福利视频 | 中文字幕中文有码在线 | 精品一区二区三区波多野结衣 | 亚洲成av人片天堂网无码】 | 国产av人人夜夜澡人人爽麻豆 | 国产成人精品久久亚洲高清不卡 | 樱花草在线社区www | 两性色午夜视频免费播放 | 欧美精品免费观看二区 | 伊人久久婷婷五月综合97色 | 久久天天躁狠狠躁夜夜免费观看 | 欧美xxxxx精品 | 国产亚洲精品久久久久久国模美 | 免费人成在线视频无码 | 人人妻人人澡人人爽精品欧美 | 露脸叫床粗话东北少妇 | 人妻无码αv中文字幕久久琪琪布 | 国内揄拍国内精品少妇国语 | 伊人久久大香线蕉午夜 | 西西人体www44rt大胆高清 | 色欲人妻aaaaaaa无码 | 女人被男人爽到呻吟的视频 | 亚洲色成人中文字幕网站 | 又大又硬又爽免费视频 | 成 人影片 免费观看 | 国产区女主播在线观看 | 88国产精品欧美一区二区三区 | 无套内谢的新婚少妇国语播放 | 亚洲va欧美va天堂v国产综合 | 午夜理论片yy44880影院 | 欧美日韩综合一区二区三区 | 又色又爽又黄的美女裸体网站 | 成年美女黄网站色大免费全看 | 兔费看少妇性l交大片免费 | 国产精品无码永久免费888 | 狂野欧美激情性xxxx | 亚洲精品一区二区三区大桥未久 | 婷婷丁香六月激情综合啪 | 亚洲s色大片在线观看 | 奇米影视888欧美在线观看 | 国产xxx69麻豆国语对白 | 亚洲乱亚洲乱妇50p | 久久精品中文字幕大胸 | 天堂一区人妻无码 | 欧美成人家庭影院 | 亚洲午夜久久久影院 | 中文字幕av伊人av无码av | 人人澡人人妻人人爽人人蜜桃 | 99久久婷婷国产综合精品青草免费 | 蜜桃av抽搐高潮一区二区 | 无遮挡啪啪摇乳动态图 | 国产成人综合在线女婷五月99播放 | 一个人看的视频www在线 | 国产精品va在线观看无码 | 欧美刺激性大交 | 精品国产av色一区二区深夜久久 | 色婷婷久久一区二区三区麻豆 | 国内少妇偷人精品视频 | 国产三级精品三级男人的天堂 | 久久亚洲日韩精品一区二区三区 | 国内精品久久久久久中文字幕 | 亚洲国产精品美女久久久久 | 波多野结衣av在线观看 | 精品久久久久久亚洲精品 | 久久无码专区国产精品s | 国产无套内射久久久国产 | 宝宝好涨水快流出来免费视频 | 亚洲欧美国产精品专区久久 | 国产三级精品三级男人的天堂 | 亚洲精品国产a久久久久久 | 成年美女黄网站色大免费全看 | 7777奇米四色成人眼影 | www成人国产高清内射 | 久久 国产 尿 小便 嘘嘘 | 久久综合久久自在自线精品自 | 日韩少妇白浆无码系列 | 中文字幕av无码一区二区三区电影 | 欧美老妇交乱视频在线观看 | 人妻少妇精品无码专区二区 | 在线播放无码字幕亚洲 | 国产在线精品一区二区高清不卡 | 国产精品美女久久久 | 中文无码精品a∨在线观看不卡 | 国产精品va在线观看无码 | 亚洲男人av香蕉爽爽爽爽 | 丰满少妇人妻久久久久久 | 中国大陆精品视频xxxx | 99riav国产精品视频 | 日本乱偷人妻中文字幕 | 无码吃奶揉捏奶头高潮视频 | 精品国产乱码久久久久乱码 | 欧美性猛交内射兽交老熟妇 | 天干天干啦夜天干天2017 | 欧美人与牲动交xxxx | 国产偷抇久久精品a片69 | 国産精品久久久久久久 | 乱人伦人妻中文字幕无码 | 亚洲啪av永久无码精品放毛片 | 天天综合网天天综合色 | 国产成人精品一区二区在线小狼 | 又湿又紧又大又爽a视频国产 | 久精品国产欧美亚洲色aⅴ大片 | 亚洲成av人影院在线观看 | 天天综合网天天综合色 | 国精产品一品二品国精品69xx | 久精品国产欧美亚洲色aⅴ大片 | 给我免费的视频在线观看 | 国产另类ts人妖一区二区 | 日韩av无码一区二区三区 | 精品日本一区二区三区在线观看 | 午夜不卡av免费 一本久久a久久精品vr综合 | 国产艳妇av在线观看果冻传媒 | 久久99精品国产麻豆蜜芽 | 久久久国产精品无码免费专区 | 狠狠躁日日躁夜夜躁2020 | 国产成人无码av在线影院 | 久久精品中文字幕大胸 | 蜜桃臀无码内射一区二区三区 | 久久久久人妻一区精品色欧美 | 国产香蕉尹人综合在线观看 | 人妻人人添人妻人人爱 | 一本色道婷婷久久欧美 | 国产成人综合色在线观看网站 | 国精品人妻无码一区二区三区蜜柚 | 久久综合香蕉国产蜜臀av | 性欧美疯狂xxxxbbbb | 综合人妻久久一区二区精品 | 麻豆人妻少妇精品无码专区 | 亚洲成av人综合在线观看 | 男女超爽视频免费播放 | 精品成在人线av无码免费看 | 国产一区二区三区四区五区加勒比 | 中文字幕无线码免费人妻 | 99国产精品白浆在线观看免费 | 无码人妻丰满熟妇区五十路百度 | 亚洲精品国产精品乱码视色 | 日产国产精品亚洲系列 | 亚洲欧美日韩国产精品一区二区 | 亚洲欧美中文字幕5发布 | 亚洲色在线无码国产精品不卡 | 精品一二三区久久aaa片 | av无码不卡在线观看免费 | 亚洲精品一区二区三区在线 | 一本久道高清无码视频 | 四虎永久在线精品免费网址 | 嫩b人妻精品一区二区三区 | 国产精品无码成人午夜电影 | 扒开双腿吃奶呻吟做受视频 | 国产国产精品人在线视 | 久久久中文久久久无码 | 日韩人妻无码一区二区三区久久99 | 色窝窝无码一区二区三区色欲 | 久9re热视频这里只有精品 | 日本护士毛茸茸高潮 | 国语自产偷拍精品视频偷 | 亚洲精品国产a久久久久久 | 少妇久久久久久人妻无码 | 国产亚洲精品久久久久久 | 超碰97人人做人人爱少妇 | 黑人巨大精品欧美黑寡妇 | 亚洲色无码一区二区三区 | 澳门永久av免费网站 | 国产在线无码精品电影网 | 精品国产av色一区二区深夜久久 | 国产无遮挡又黄又爽免费视频 | 97久久国产亚洲精品超碰热 | 天干天干啦夜天干天2017 | 午夜精品一区二区三区在线观看 | 成 人 免费观看网站 | 久久五月精品中文字幕 | 乱人伦人妻中文字幕无码久久网 | 亚洲 高清 成人 动漫 | av无码电影一区二区三区 | 爆乳一区二区三区无码 | 亚洲精品欧美二区三区中文字幕 | 最新国产乱人伦偷精品免费网站 | 一个人看的www免费视频在线观看 | 久久精品无码一区二区三区 | 亚洲aⅴ无码成人网站国产app | 国产农村乱对白刺激视频 | 国内精品久久毛片一区二区 | www成人国产高清内射 | 亚洲a无码综合a国产av中文 | 中文字幕亚洲情99在线 | 日本一区二区三区免费高清 | 亚洲欧美中文字幕5发布 | 无码一区二区三区在线 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 131美女爱做视频 | 丝袜美腿亚洲一区二区 | 一区二区传媒有限公司 | 国产艳妇av在线观看果冻传媒 | 国产人妻精品一区二区三区 | 国产午夜福利100集发布 | 漂亮人妻洗澡被公强 日日躁 | 麻豆蜜桃av蜜臀av色欲av | 男女作爱免费网站 | 久久久中文久久久无码 | 久久国产精品萌白酱免费 | 精品乱码久久久久久久 | 久久亚洲精品中文字幕无男同 | 国产精品-区区久久久狼 | 婷婷色婷婷开心五月四房播播 | aⅴ亚洲 日韩 色 图网站 播放 | 成人免费无码大片a毛片 | 欧美午夜特黄aaaaaa片 | 激情亚洲一区国产精品 | 波多野结衣av一区二区全免费观看 | 国产精品久久久久9999小说 | 国产精品va在线播放 | 色欲综合久久中文字幕网 | 国产成人综合色在线观看网站 | 亚洲无人区一区二区三区 | 国产舌乚八伦偷品w中 | 亚洲狠狠色丁香婷婷综合 | 人妻互换免费中文字幕 | 天堂а√在线地址中文在线 | 久久精品成人欧美大片 | 少女韩国电视剧在线观看完整 | 国产精品成人av在线观看 | 国产内射爽爽大片视频社区在线 | 久久亚洲a片com人成 | 欧美成人午夜精品久久久 | 中文字幕乱码人妻二区三区 | 国产亚洲欧美日韩亚洲中文色 | 奇米影视7777久久精品人人爽 | 99久久人妻精品免费二区 | 国产午夜亚洲精品不卡下载 | 亚洲の无码国产の无码影院 | 无码人妻精品一区二区三区下载 | 日本一卡二卡不卡视频查询 | 在教室伦流澡到高潮hnp视频 | 成人精品视频一区二区 | 丝袜人妻一区二区三区 | 国模大胆一区二区三区 | 中文字幕无码av波多野吉衣 | 大地资源中文第3页 | 中文字幕无码人妻少妇免费 | 欧美日韩精品 | 国产av一区二区三区最新精品 | 荫蒂被男人添的好舒服爽免费视频 | 亚洲精品一区二区三区四区五区 | 亚洲综合在线一区二区三区 | 九九久久精品国产免费看小说 | 久久精品丝袜高跟鞋 | 疯狂三人交性欧美 | 国产精品久久久久无码av色戒 | 激情国产av做激情国产爱 | aⅴ亚洲 日韩 色 图网站 播放 | 国产成人人人97超碰超爽8 | 特黄特色大片免费播放器图片 | 国产精品亚洲五月天高清 | a片免费视频在线观看 | 大地资源网第二页免费观看 | 日本一卡二卡不卡视频查询 | 精品无码国产自产拍在线观看蜜 | 久久久精品成人免费观看 | 波多野42部无码喷潮在线 | 亚洲欧洲无卡二区视頻 | 男女猛烈xx00免费视频试看 | 亚洲精品一区三区三区在线观看 | 中文字幕无线码 | 国产人妖乱国产精品人妖 | 人妻与老人中文字幕 | 无码人妻少妇伦在线电影 | 国产精品久免费的黄网站 | 中文久久乱码一区二区 | 99精品无人区乱码1区2区3区 | 中文字幕人妻丝袜二区 | 亚洲一区二区三区含羞草 | 国产真实伦对白全集 | 欧美精品一区二区精品久久 | 人妻中文无码久热丝袜 | 少妇性俱乐部纵欲狂欢电影 | 97久久国产亚洲精品超碰热 | 国产精品99久久精品爆乳 | 久久99精品国产麻豆 | 欧美激情综合亚洲一二区 | 综合激情五月综合激情五月激情1 | 亚洲 a v无 码免 费 成 人 a v | 亚洲精品一区二区三区在线观看 | 老头边吃奶边弄进去呻吟 | 1000部啪啪未满十八勿入下载 | 国产精品久久久久久久影院 | 精品无码av一区二区三区 | 男女下面进入的视频免费午夜 | 午夜理论片yy44880影院 | 国产亚洲欧美日韩亚洲中文色 | 天堂在线观看www | 亚洲熟妇自偷自拍另类 | 国产色xx群视频射精 | 国产免费观看黄av片 | 日日橹狠狠爱欧美视频 | 欧美丰满熟妇xxxx性ppx人交 | 精品人妻人人做人人爽夜夜爽 | 日本熟妇浓毛 | 蜜臀av无码人妻精品 | 国产精品怡红院永久免费 | 色一情一乱一伦一视频免费看 | 国产情侣作爱视频免费观看 | 成熟妇人a片免费看网站 | 国产亚洲精品久久久久久大师 | 天堂一区人妻无码 | 久青草影院在线观看国产 | 久久天天躁狠狠躁夜夜免费观看 | 无码乱肉视频免费大全合集 | 粗大的内捧猛烈进出视频 | 天天爽夜夜爽夜夜爽 | 久久久久亚洲精品男人的天堂 | 中文字幕 人妻熟女 | 日韩精品久久久肉伦网站 | 国产av久久久久精东av | 一本色道婷婷久久欧美 | 国产婷婷色一区二区三区在线 | 免费看男女做好爽好硬视频 | 扒开双腿吃奶呻吟做受视频 | 无码免费一区二区三区 | 久久熟妇人妻午夜寂寞影院 | 亚洲热妇无码av在线播放 | 久久国产自偷自偷免费一区调 | 精品国产一区二区三区四区 | a在线观看免费网站大全 | 亚洲经典千人经典日产 | 天天躁日日躁狠狠躁免费麻豆 | 久久国语露脸国产精品电影 | 国产亚洲美女精品久久久2020 | 中文字幕乱妇无码av在线 | 日韩av无码中文无码电影 | 老熟妇仑乱视频一区二区 | 丰满岳乱妇在线观看中字无码 | 天堂久久天堂av色综合 | 色 综合 欧美 亚洲 国产 | 久久久久亚洲精品中文字幕 | 亚洲精品久久久久中文第一幕 | 亚洲色偷偷男人的天堂 | 日本爽爽爽爽爽爽在线观看免 | 国产亚洲精品久久久闺蜜 | 中文字幕精品av一区二区五区 | 国产精品99久久精品爆乳 | 奇米影视7777久久精品人人爽 | 色欲av亚洲一区无码少妇 | 国产亚洲欧美日韩亚洲中文色 | 国产在热线精品视频 | 日韩亚洲欧美中文高清在线 | 精品欧美一区二区三区久久久 | 高清国产亚洲精品自在久久 | 成人动漫在线观看 | 国产精品无码mv在线观看 | 色婷婷欧美在线播放内射 | 成人免费视频视频在线观看 免费 | 精品人妻中文字幕有码在线 | 搡女人真爽免费视频大全 | 国产人妻精品一区二区三区不卡 | 中文字幕中文有码在线 | 欧美亚洲国产一区二区三区 | 中国女人内谢69xxxxxa片 | 捆绑白丝粉色jk震动捧喷白浆 | 大肉大捧一进一出视频出来呀 | 日韩精品乱码av一区二区 | 熟妇人妻无码xxx视频 | 久久午夜无码鲁丝片秋霞 | 欧美精品免费观看二区 | 亚洲狠狠婷婷综合久久 | 色婷婷欧美在线播放内射 | av人摸人人人澡人人超碰下载 | 久久天天躁狠狠躁夜夜免费观看 | 亚洲欧美国产精品久久 | 久久99热只有频精品8 | 未满成年国产在线观看 | 久久精品成人欧美大片 | 无遮挡国产高潮视频免费观看 | 欧美zoozzooz性欧美 | 福利一区二区三区视频在线观看 | 国产精品久久久久久久影院 | 乌克兰少妇性做爰 | 国产性生大片免费观看性 | 国产又爽又猛又粗的视频a片 | 中文字幕无码视频专区 | 激情综合激情五月俺也去 | 久久午夜无码鲁丝片 | 亚洲狠狠色丁香婷婷综合 | 亚洲狠狠婷婷综合久久 | 成人试看120秒体验区 | 捆绑白丝粉色jk震动捧喷白浆 | 人人澡人人透人人爽 | 国产精品丝袜黑色高跟鞋 | 国产在线精品一区二区高清不卡 | 3d动漫精品啪啪一区二区中 | 亚洲国产精品成人久久蜜臀 | 欧美日本日韩 | 亚洲综合无码久久精品综合 | 亚洲狠狠色丁香婷婷综合 | 久精品国产欧美亚洲色aⅴ大片 | 亚洲色偷偷男人的天堂 | 玩弄中年熟妇正在播放 | 男女性色大片免费网站 | 亚洲中文字幕在线无码一区二区 | 国语自产偷拍精品视频偷 | 老司机亚洲精品影院无码 | av人摸人人人澡人人超碰下载 | 久久精品国产99精品亚洲 | 国产舌乚八伦偷品w中 | 少妇高潮一区二区三区99 | 欧美国产日韩亚洲中文 | 暴力强奷在线播放无码 | 国产精品久久国产三级国 | 国产激情无码一区二区app | 欧美 日韩 人妻 高清 中文 | 九九久久精品国产免费看小说 | 成人试看120秒体验区 | 国内精品久久毛片一区二区 | 免费男性肉肉影院 | 国产欧美亚洲精品a | 国产精品多人p群无码 | 久久综合久久自在自线精品自 | 76少妇精品导航 | 欧美日韩视频无码一区二区三 | 婷婷色婷婷开心五月四房播播 | 国产综合久久久久鬼色 | 欧美高清在线精品一区 | 亚洲爆乳无码专区 | 亚洲精品一区三区三区在线观看 | 国产亚洲欧美日韩亚洲中文色 | 国产成人无码专区 | 国产艳妇av在线观看果冻传媒 | 又大又硬又黄的免费视频 | 中文字幕精品av一区二区五区 | 国内丰满熟女出轨videos | 亚洲一区二区三区国产精华液 | 日本乱人伦片中文三区 | 东京一本一道一二三区 | 亚洲熟妇自偷自拍另类 | 国产 精品 自在自线 | 亚洲一区二区三区偷拍女厕 | 欧美丰满熟妇xxxx性ppx人交 | 麻豆国产人妻欲求不满 |