Z從壹開始前後端分離【 .NET Core2.0/3.0 +Vue2.0 】框架之二 || 後端項目搭建

来源:https://www.cnblogs.com/qingshan/archive/2019/11/21/11905046.html

本文梯子 前言 1、.net core 框架性能測試 2、.net core 執行過程 3、中間件執行過程 4、AOP切麵 5、整體框架結構與資料庫表UML 一、創建第一個Core 1、SDK 安裝 2、新建項目 2、新建項目(3.0SDK) 3、項目整體結構分析 二、重要文件說明 1、Progra ...


 

正文 

前言

至於為什麼要搭建.Net Core 平臺,這個網上的解釋以及鋪天蓋地,想了想,還是感覺重要的一點,跨平臺,嗯!沒錯,而且比.Net 更容易搭建,速度也更快,所有的包均有Nuget提供,不再像以前的單純引入組件,

已經沒有了之前的Assemblies和COM的引入,初次使用感覺會很彆扭,不過使用多了,發現還是很方便的,所以你一定要會使用Nuget,真的很強大,這點兒設計思路感覺更像Linux了。

下邊這三點,是先對 .net core  有一個初步的認識,看得懂或者看不懂都沒有關係,以後大家肯定都會明白的:

 

1、.net core 框架性能測試

http://www.techempower.com/benchmarks/ 我們可以通過這個web框架性能測試來看看 aspcore 的性能

 

2、.net core 執行過程

3、中間件執行過程

啟動的時候先執行該中間件類的構造函數,然後一路 Next() ;下去,返回的時候,正好是反向的,執行的是該類的邏輯部分:

 

4、AOP切麵

 

 

 

5、整體框架結構與資料庫表UML

 

 

 

 

 

一、創建第一個Core

    說了從零開始,就得從零開始,老生常談,開始。

1、SDK 安裝

當然,前提是你得安裝.Net Core,VS 2015也是可以,只不過需要單獨安裝.Net Core,首先你得裝個vs2015 並且保證已經升級至 update3及以上。

我的 VS 是 2017,我這裡只說2017,有不會的網友可以留言,只要在Visual Studio Installer 中安裝下圖中的Core 平臺即可。

下載 SDK 地址 :https://dotnet.microsoft.com/download

選擇指定的平臺即可安裝:

 

這裡說下,SDK 和 RunTime 的區別:
1、SDK 是用來開發 NetCore 的,內部捆綁了 Runtime 運行時;
2、但是如何只想運行 NetCore 項目的話,只需要在伺服器中安裝 Runtime 運行時即可;

 

怎麼判斷安裝成功了呢?直接運行命令,如果有結果證明成功了:

 

 

 

2、新建項目

1、File --> Project (記得文件名不要是中文,不然,你懂的)

2、然後選擇.Net Core 版本和項目類型,我選擇相對穩定的ASP.NET Core 2.2,然後選擇API的項目類型

至於其他的,大家可以自己玩一玩,還有就是是否Docker支持,這兩年Docker著實很火,我也會在以後的時間里,補上這塊兒的使用。。。

 

 

Duang ,然後就出現了,特別簡單的一個.Net Core API就這麼誕生了,嗯不錯,基本的分成這幾個部分,是不是特別像一個控制台程式?而且真是簡潔了不少。

 

這裡要註意下,關於Https選項問題,有很多小伙伴在以後的介面調用中,勾選了這個,但是還是一直使用 http 協議去訪問,導致找不到響應的介面地址。

 

1、是你的項目創建的時候,勾選了 Https 選項,如果你還沒有創建,那就可以不要勾選那個 HTTPS選項。

2、如果你的項目已經創建好了,每次訪問都是HTTPS的,但是你不想這麼做,可以在 launthSettings.json 文件中,把sslPort 埠號改成0即可

 

 

2、新建項目(3.0SDK)

 

 

點擊下一步。

然後創建模板:

 

 

 

 

3、項目整體結構分析

接下來咱們看看這個項目都包含了哪些東西: 

 

點開Controllers --> ValuesController 文件,你會發現四個方法,並且每個方法也有各自的特性,分別是HttpGet,HttpPost,HttpPut,HttpDelete,這四個就是傳說中的RESTful風格的編程。

為什麼會有這種風格呢:

RESTful 風格介面實際情況是,我們在前後端在約定介面的時候,可以約定各種風格的介面,但是,RESTful 介面是目前來說比較流行的,並且在運用中比較方便和常見的介面。

雖然它有一些缺陷,目前 github 也在主推 GraphQL 這種新的介面風格,但目前國內來說還是 RESTful 介面風格比較普遍。並且,在掌握了 RESTful 介面風格之後,會深入的理解這種介面的優缺點,到時候,你自然會去想解決方案,並且在項目中實行新的更好的理念,所以,我這系列的博文,依然採用 http://cnodejs.org/ 網站提供的 RESTful 介面來實戰。

瞭解程式開發的都應該知道,我們所做的大多數操作都是對資料庫的四格操作 “增刪改查” 對應到我們的介面操作分別是:post 插入新數據delete 刪除數據put 修改數據get 查詢數據

註意,這裡是我們約定,並非這些動作只能幹這件事情。從表層來說,除get外的其他方法,沒有什麼區別,都是一樣的。從深層來說包括 get在內的所有方法都是一模一樣的,沒有任何區別。但是,我們約定,每種動作對應不同的操作,這樣方便我們統一規範我們的所有操作。

假設,我們的介面是 /api/v1/love 這樣的介面,採用 RESTful 介面風格對應操作是如下的:get 操作 /api/v1/love獲取 /api/v1/love 的分頁列表數據,得到的主體,將是一個數組,我們可以用數據來遍歷迴圈列表post 操作 /api/v1/love我們會往 /api/v1/love 插入一條新的數據,我們插入的數據,將是JOSN利用對象傳輸的。get 操作 /api/v1/love/1我們獲取到一個 ID 為 1 的數據,數據一般為一個對象,裡面包含了 1 的各項欄位信息。put 操作 /api/v1/love/1我們向介面提交了一個新的信息,來修改 ID 為 1 的這條信息delete 操作 /api/v1/love/1我們向介面請求,刪除 ID 為 1 的這一條數據

由上述例子可知,我們實現了5種操作,但只用了兩個介面地址, /api/v1/love 和 /api/v1/love/1 。所以,採用這種介面風格,可以大幅的簡化我們的介面設計。

 

 
提醒:2.1以後,新建的controller 所繼承的基類的 ControllerBase,導致在介面的返回值中,不能使用 return Json();方法,你可以使用 return Ok(xxx),效果是一樣的,
如果你一定要使用 Json,那就修改基類,繼承 Controller 吧。

 

然後 F5 運行,就會看到介面地址,以及對應的內容,你可以根據自己的需要進行各種配置合適的路由,

這裡要註意下,如果出現特性相同,方法同名,參數一樣的,編譯會報錯,起名字很重要。

還有,這裡會自動跳轉到預設地址 api/values,當然是可以配置的,就在 Properties --> launchSettings.json 中

 


 

接下來點開 appsettings.json 文件,這裡就是整個系統app的配置地址,更類似以前的web.config,以後大家會用到。

 這裡還要說一下[HttpGet("{id}"),Name="Get"] ,這個有時候會帶 Name,很多小伙伴不知道這是幹啥的,我就簡單說一下:

[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(long id)

"{id}" 是 todo 項 的 ID 的占位符變數。 調用 GetById 時,它會將 URL 中“{id}”的值分配給方法的 id 參數。Name = "GetTodo" 創建一個命名的路由,使你能夠 HTTP 響應中鏈接到此路由。 稍後將使用示例進行解釋。 有關詳細信息,請參閱路由到控制器操作,還有這個Attribute Routing in Web API 2

 

一般來說,路由名稱都是和路由url一一對應的,儘量不要重覆,不過也很少有人寫這個,沒啥用,所以一般不要寫。

 

 

繼續往下,打開Startup.cs 文件這裡是整個項目的啟動文件,所有的啟動相關的都會在這裡配置,比如 依賴註入,跨域請求,Redis緩存等,更多詳情在以後的文章中都會有所提起

 


 

2018-09-23 更新

二、重要文件說明

1、Program.cs

複製代碼
namespace CoreBackend.Api
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
}
複製代碼

這個Program是程式的入口, 看起來很眼熟, 是因為asp.net core application實際就是控制台程式(console application).

它是一個調用asp.net core 相關庫的console application. 

Main方法裡面的內容主要是用來配置和運行程式的.

因為我們的web程式需要一個宿主, 所以 BuildWebHost這個方法就創建了一個WebHostBuilder. 而且我們還需要Web Server.

asp.net core 自帶了兩種http servers, 一個是WebListener, 它只能用於windows系統, 另一個是kestrel, 它是跨平臺的.

kestrel是預設的web server, 就是通過UseKestrel()這個方法來啟用的.

但是我們開發的時候使用的是IIS Express, 調用UseIISIntegration()這個方法是啟用IIS Express, 它作為Kestrel的Reverse Proxy server來用.

 

 

如果在windows伺服器上部署的話, 就應該使用IIS作為Kestrel的反向代理伺服器來管理和代理請求.

如果在linux上的話, 可以使用apache, nginx等等的作為kestrel的proxy server.

當然也可以單獨使用kestrel作為web 伺服器, 但是使用iis作為reverse proxy還是有很多有優點的: 例如,IIS可以過濾請求, 管理證書, 程式崩潰時自動重啟等.

UseStartup<Startup>(), 這句話表示在程式啟動的時候, 我們會調用Startup這個類.

Build()完之後返回一個實現了IWebHost介面的實例(WebHostBuilder), 然後調用Run()就會運行Web程式, 並且阻止這個調用的線程, 直到程式關閉.

 

如果想要對AspNetCore源碼進行研究,可以查看源碼,這裡提供兩個方法:

1、F12,當然這個不能看到詳細的,你需要安裝一個組件,VS2017 Resharper

2、查看Github 開源源碼 https://github.com/aspnet/MetaPackages/tree/master/src/Microsoft.AspNetCore 

 

2、Startup.cs

複製代碼
namespace CoreBackend.Api
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

        // 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(); } //這個就是一個簡單的中間件寫法 app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } } }
複製代碼

其實Startup算是程式真正的切入點.

ConfigureServices方法是用來把services(各種服務, 例如identity, ef, mvc等等包括第三方的, 或者自己寫的)加入(register)到container(asp.net core的容器)中去, 並配置這些services. 這個container是用來進行dependency injection的(依賴註入). 所有註入的services(此外還包括一些框架已經註冊好的services) 在以後寫代碼的時候, 都可以將它們註入(inject)進去. 例如上面的Configure方法的參數, app, env, loggerFactory都是註入進去的services.

 

Configure方法是asp.net core程式用來具體指定如何處理每個http請求的, 例如我們可以讓這個程式知道我使用mvc來處理http請求, 那就調用app.UseMvc()這個方法就行. 但是目前, 所有的http請求都會導致返回"Hello World!".

 

 

看一看我們項目的最後,Configure方法是如何配置的:

複製代碼
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                // 在開發環境中,使用異常頁面,這樣可以暴露錯誤堆棧信息,所以不要放在生產環境。
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // 在非開發環境中,使用HTTP嚴格安全傳輸(or HSTS) 對於保護web安全是非常重要的。
                // 強制實施 HTTPS 在 ASP.NET Core,配合 app.UseHttpsRedirection
                //app.UseHsts();

            }

            #region Swagger
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                //之前是寫死的
                //c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiHelp V1");
                //c.RoutePrefix = "";//路徑配置,設置為空,表示直接在根功能變數名稱(localhost:8001)訪問該文件,註意localhost:8001/swagger是訪問不到的,去launchSettings.json把launchUrl去掉

                //根據版本名稱倒序 遍歷展示
                typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version =>
                {
// 註意這個 ApiName 和 要和上邊 ConfigureServices 中配置swagger的name要大小寫一致,具體查看我的blog.core源碼 c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{ApiName} {version}"); }); }); #endregion #region Authen //app.UseMiddleware<JwtTokenAuth>();//註意此授權方法已經放棄,請使用下邊的官方驗證方法。但是如果你還想傳User的全局變數,還是可以繼續使用中間件 app.UseAuthentication(); #endregion #region CORS //跨域第二種方法,使用策略,詳細策略信息在ConfigureService中 app.UseCors("LimitRequests");//將 CORS 中間件添加到 web 應用程式管線中, 以允許跨域請求。 //跨域第一種版本,請要ConfigureService中配置服務 services.AddCors(); // app.UseCors(options => options.WithOrigins("http://localhost:8021").AllowAnyHeader() //.AllowAnyMethod()); #endregion // 跳轉https app.UseHttpsRedirection(); // 使用靜態文件 app.UseStaticFiles(); // 使用cookie app.UseCookiePolicy(); // 返回錯誤碼 app.UseStatusCodePages();//把錯誤碼返回前臺,比如是404 app.UseMvc(); }
複製代碼

 

 

3、調試方法

.net core 調試的兩種方法

1、通過IIS調試

 

2、項目自帶的Kestrel web應用調式

 

 

 

三、註冊並使用MVC

因為asp.net core 2.0使用了一個大而全的metapackage, 所以這些基本的services和middleware是不需要另外安裝的.

首先, 在ConfigureServices裡面向Container註冊MVC: services.AddMvc();

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(); // 註冊MVC到Container
        }

然後再Configure裡面告訴程式使用mvc中間件:

複製代碼
 
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
 
複製代碼

註意順序, 應該在處理異常的middleware後邊調用app.UseMvc(), 所以處理異常的middleware可以在把request交給mvc之間就處理異常, 更重要的是它還可以捕獲並處理返回MVC相關代碼執行中的異常.

然後別忘了把app.Run那部分代碼去掉. 然後改回到Develpment環境, 跑一下, 試試效果:

Chrome顯示了一個空白頁, 按F12, 顯示了404 Not Found錯誤.

這是因為我只添加了MVC middleware, 但是它啥也沒做, 也沒有找到任何可用於處理請求的代碼, 所以我們要添加Controller來返回數據/資源等等

四、核心知識點

1、Routing 路由

路由有兩種方式: Convention-based (按約定), attribute-based(基於路由屬性配置的). 

其中convention-based (基於約定的) 主要用於MVC (返回View或者Razor Page那種的).

Web api 推薦使用attribute-based.

這種基於屬性配置的路由可以配置Controller或者Action級別, uri會根據Http method然後被匹配到一個controller里具體的action上.

常用的Http Method有:

  • Get, 查詢, Attribute: HttpGet, 例如: '/api/product', '/api/product/1'
  • POST, 創建, HttpPost, '/api/product'
  • PUT 整體修改更新 HttpPut, '/api/product/1'
  • PATCH 部分更新, HttpPatch, '/api/product/1'
  • DELETE 刪除, HttpDelete, '/api/product/1

還有一個Route屬性(attribute)也可以用於Controller層, 它可以控制action級的URI首碼.

以下不是本系列,就看思路即可,不用敲代碼

複製代碼
 
//以下不是本系列教程,就看思路即可,不用敲代碼
namespace CoreBackend.Api.Controllers { //[Route("api/product")] [Route("api/[controller]")] public class ProductController: Controller { [HttpGet] public JsonResult GetProducts() { return new JsonResult(new List<Product> { new Product { Id = 1, Name = "牛奶", Price = 2.5f }, new Product { Id = 2, Name = "麵包", Price = 4.5f } }); } } }
 
複製代碼

使用[Route("api/[controller]")], 它使得整個Controller下麵所有action的uri首碼變成了"/api/product", 其中[controller]表示XxxController.cs中的Xxx(其實是小寫).

也可以具體指定, [Route("api/product")], 這樣做的好處是, 如果ProductController重構以後改名了, 只要不改Route裡面的內容, 那麼請求的地址不會發生變化.

然後在GetProducts方法上面, 寫上HttpGet, 也可以寫HttpGet(). 它裡面還可以加參數,例如: HttpGet("all"), 那麼這個Action的請求的地址就變成了 "/api/product/All".

 

2、內容協商 Content Negotiation

如果 web api提供了多種內容格式, 那麼可以通過Accept Header來選擇最好的內容返回格式: 例如:

application/json, application/xml等等

如果設定的格式在web api裡面沒有, 那麼web api就會使用預設的格式.

asp.net core 預設提供的是json格式, 也可以配置xml等格式.

目前只考慮 Output formatter, 就是返回的內容格式.

 

如果想輸出xml格式,就配置這裡:

 

 

3、創建Post Action

以下不是本系列,就看思路即可,不用敲代碼

複製代碼
 
 
//以下不是本系列教程,就看思路即可,不用敲代碼     
[Route("{id}", Name = "GetProduct")] public IActionResult GetProduct(int id) { var product = ProductService...(x => x.Id == id); if (product == null) { return NotFound(); } return Ok(product); } [HttpPost] public IActionResult Post([FromBody] ProductCreation product) { if (product == null) { return BadRequest(); } var maxId = ProductService.Max(x => x.Id); var newProduct = new Product { Id = ++maxId, Name = product.Name, Price = product.Price }; ProductService.Add(newProduct); return CreatedAtRoute("GetProduct", new { id = newProduct.Id }, newProduct); }
 
複製代碼

 

[HttpPost] 表示請求的謂詞是Post. 加上Controller的Route首碼, 那麼訪問這個Action的地址就應該是: 'api/product'

後邊也可以跟著自定義的路由地址, 例如 [HttpPost("create")], 那麼這個Action的路由地址就應該是: 'api/product/create'.

[FromBody] , 請求的body裡面包含著方法需要的實體數據, 方法需要把這個數據Deserialize成ProductCreation, [FromBody]就是乾這些活的.

客戶端程式可能會發起一個Bad的Request, 導致數據不能被Deserialize, 這時候參數product就會變成null. 所以這是一個客戶端發生的錯誤, 程式為讓客戶端知道是它引起了錯誤, 就應該返回一個Bad Request 400 (Bad Request表示客戶端引起的錯誤)的 Status Code.

傳遞進來的model類型是 ProductCreation, 而我們最終操作的類型是Product, 所以需要進行一個Map操作, 目前還是挨個屬性寫代碼進行Map吧, 以後會改成Automapper.

返回 CreatedAtRoute: 對於POST, 建議的返回Status Code 是 201 (Created), 可以使用CreatedAtRoute這個內置的Helper Method. 它可以返回一個帶有地址Header的Response, 這個Location Header將會包含一個URI, 通過這個URI可以找到我們新創建的實體數據. 這裡就是指之前寫的GetProduct(int id)這個方法. 但是這個Action必須有一個路由的名字才可以引用它, 所以在GetProduct方法上的Route這個attribute裡面加上Name="GetProduct", 然後在CreatedAtRoute方法第一個參數寫上這個名字就可以了, 儘管進行了引用, 但是Post方法走完的時候並不會調用GetProduct方法. CreatedAtRoute第二個參數就是對應著GetProduct的參數列表, 使用匿名類即可, 最後一個參數是我們剛剛創建的數據實體

運行程式試驗一下, 註意需要在Headers裡面設置Content-Type: application/json.

4、Validation 驗證

針對上面的Post方法,  如果請求沒有Body, 參數product就會是null, 這個我們已經判斷了; 如果body裡面的數據所包含的屬性在product中不存在, 那麼這個屬性就會被忽略.

但是如果body數據的屬性有問題, 比如說name沒有填寫, 或者name太長, 那麼在執行action方法的時候就會報錯, 這時候框架會自動拋出500異常, 表示是伺服器的錯誤, 這是不對的. 這種錯誤是由客戶端引起的, 所以需要返回400 Bad Request錯誤.

驗證Model/實體, asp.net core 內置可以使用 Data Annotations進行: 

複製代碼
 
//以下不是本系列教程,就看思路即可,不用敲代碼
using System; using System.ComponentModel.DataAnnotations; namespace CoreBackend.Api.Dtos { public class ProductCreation { [Display(Name = "產品名稱")] [Required(ErrorMessage = "{0}是必填項")] // [MinLength(2, ErrorMessage = "{0}的最小長度是{1}")] // [MaxLength(10, ErrorMessage = "{0}的長度不可以超過{1}")]
     [StringLength(10, MinimumLength = 2, ErrorMessage = "{0}的長度應該不小於{2}, 不大於{1}")] public string Name { get; set; } [Display(Name = "價格")] [Range(0, Double.MaxValue, ErrorMessage = "{0}的值必須大於{1}")] public float Price { get; set; } } }
 
複製代碼

這些Data Annotation (理解為用於驗證的註解), 可以在System.ComponentModel.DataAnnotation找到, 例如[Required]表示必填, [MinLength]表示最小長度, [StringLength]可以同時驗證最小和最大長度, [Range]表示數值的範圍等等很多.

[Display(Name="xxx")]的用處是, 給屬性起一個比較友好的名字.

其他的驗證註解都有一個屬性叫做 ErrorMessage (string), 表示如果驗證失敗, 就會把ErrorMessage的內容添加到錯誤結果裡面去. 這個ErrorMessage可以使用參數, {0}表示Display的Name屬性, {1}表示當前註解的第一個變數, {2}表示當前註解的第二個變數.

在Controller裡面添加驗證邏輯:

複製代碼
 
//以下不是本系列教程,就看思路即可,不用敲代碼     
[HttpPost] public IActionResult Post([FromBody] ProductCreation product) { if (product == null) { return BadRequest(); } if (!ModelState.IsValid) { return BadRequest(ModelState); } var maxId = ProductService.Max(x => x.Id); var newProduct = new Product { Id = ++maxId, Name = product.Name, Price = product.Price }; ProductService.Add(newProduct); return CreatedAtRoute("GetProduct", new { id = newProduct.Id }, newProduct); }
 
複製代碼

ModelState: 是一個Dictionary, 它裡面是請求提交到Action的Name和Value的對們, 一個name對應著model的一個屬性, 它也包含了一個針對每個提交的屬性的錯誤信息的集合.

每次請求進到Action的時候, 我們在ProductCreationModel添加的那些註解的驗證, 就會被檢查. 只要其中有一個驗證沒通過, 那麼ModelState.IsValid屬性就是False. 可以設置斷點查看ModelState裡面都有哪些東西.

如果有錯誤的話, 我們可以把ModelState當作 Bad Request的參數一起返回到前臺.

5、PUT請求

put應該用於對model進行完整的更新. 

首先最好還是單獨為Put寫一個Dto Model, 儘管屬性可能都是一樣的, 但是也建議這樣寫, 實在不想寫也可以.

ProducModification.cs

複製代碼
 
    public class ProductModification
    {
        [Display(Name = "產品名稱")]
        [Required(ErrorMessage = "{0}是必填項")]
        [StringLength(10, MinimumLength = 2, ErrorMessage = "{0}的長度應該不小於{2}, 不大於{1}")]
        public string Name { get; set; }

        [Display(Name = "價格")]
        [Range(0, Double.MaxValue, ErrorMessage = "{0}的值必須大於{1}")]
        public float Price { get; set; }
    }
 
複製代碼

然後編寫Controller的方法:

複製代碼
 
//以下不是本系列教程,就看思路即可,不用敲代碼     
[HttpPut("{id}")]
        public IActionResult Put(int id, [FromBody] ProductModification product)
        {
            if (product == null)
            {
                return BadRequest();
            }

            if (product.Name == "產品")
            {
                ModelState.AddModelError("Name", "產品的名稱不可以是'產品'二字");
            }

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            var model = ProductService.SingleOrDefault(x => x.Id == id);
            if (model == null)
            {
                return NotFound();
            }
            model.Name = product.Name;
            model.Price = product.Price;

            // return Ok(model);
            return NoContent();
        }
 
複製代碼

按照Http Put的約定, 需要一個id這樣的參數, 用於查找現有的model.

由於Put做的是完整的更新, 所以把ProducModification整個Model作為參數.

進來之後, 進行了一套和POST一模一樣的驗證, 這地方肯定可以改進, 如果驗證邏輯比較複雜的話, 到處寫同樣驗證邏輯肯定是不好的, 所以建議使用FluentValidation.

然後, 把ProductModification的屬性都映射查詢找到給Product, 這個以後用AutoMapper來映射.

返回: PUT建議返回NoContent(), 因為更新是客戶端發起的, 客戶端已經有了最新的值, 無須伺服器再給它傳遞一次, 當然了, 如果有些值是在後臺更新的, 那麼也可以使用Ok(xxx)然後把更新後的model作為參數一起傳到前臺.

 

 
 

五、結語

    好啦,項目搭建就這麼愉快的解決了,而且你也應該簡單瞭解了.Net Core API是如何安裝,創建,各個文件的意義以及如何運作,如何配置等,但是既然是介面,那一定是要前後端一起進行配置,使用,交流的平臺,從上文看出,每次都特別麻煩,而且不直觀,UI 不友好,怎麼辦呢?

    下一節我們就使用一個神器 Swagger,一個快速,輕量級的項目RESTFUL介面的文檔線上自動生成+功能測試功能軟體。


您的分享是我們最大的動力!

更多相關文章
  • 在前面簡單描述了下服務層,SOA面向服務架構,架構設計-業務邏輯層,以及一些面向設計原則理解和軟體架構設計箴言。這篇博客我們將繼續進入我們的下一層:數據訪問層。無論你用的是什麼開發模式或者是業務模式,到最後最必須具有持久化機制,持久化到持久化介質,並能對數據進行讀取和寫入CRUD。這就是數據訪問層。 ...
  • .NET Core 獲取資料庫上下文實例的方法和配置連接字元串 [TOC] 假設資料庫就兩個表:User、Blogs, 模型類如下 資料庫上下文大致這樣 ASP.NET Core 註入 ASP.NET Core 的資料庫註入是最為簡單方便的了,在 ConfigureServices 配置即可。 然後 ...
  • 一. 委托的分類 通過用什麼類型的方法來聲明為委托,可以分為兩類: 1. 委托靜態方法:把一個靜態方法給委托 2. 委托實例方法:把一個實例對象的成員方法給委托 (這兩個名字是博主取的,可能不是很專業只是為了好區分) 二. 原理部分 委托是將函數指針和實例對象打包在一起的類,它有兩個重要的成員,一個 ...
  • static void Main(string[] args) { string dir = @"C:\"; string[] dirs=Directory.GetDirectories(dir); long totalSize = 0; if(dirs!=null && di... ...
  • 在前面隨筆《在代碼生成工具Database2Sharp中使用ODP.NET(Oracle.ManagedDataAccess.dll)訪問Oracle資料庫,實現免安裝Oracle客戶端,相容32位64位Oracle驅動》中介紹了在代碼生成工具中使用ODP.NET(Oracle.ManagedDat... ...
  • 由於我們開發的輔助工具Database2Sharp需要支持多種資料庫,雖然我們一般使用SQLServer來開發應用較多,但是Oracle等其他資料庫也是常用的資料庫之一,因此也是支持使用Oracle等資料庫進行代碼的快速生成。在此之前我一直要求用戶使用代碼生成工具的時候,如果使用Oracle開發,則... ...
  • ERP即 企業資源計劃 (Enterprise Resource Planning),由美國 Gartner Group 公司於1990年提出。 ERP系統是指建立在信息技術基礎上,以系統化的管理思想,為企業決策層及員工提供決策運行手段的管理平臺。 ERP中三大訂單CO、PO、MO的意思分別如下: ...
  • 首先,小編要告訴大家一個殘酷的現實,那就是layui對按鈕沒有提供點擊事件的支持… 這裡的點擊事件是指單純的點擊事件,而不是提交事件,或者是數據表格中內嵌的button,對於這兩者,layui是有lay-submit和lay-event這個屬性進行支持的,所以這裡只能使用最原始的js和jq進行監聽點 ...
一周排行
  • 前言 上一篇文章介紹IOptions的註冊,本章我們繼續往下看 IOptions IOptions是一個介面裡面只有一個Values屬性,該介面通過OptionsManager實現 OptionsManager OptionsManager實現了IOptions和IOptionsSnapshot,他 ...
  • 在 EF 里有個 `ShadowProperty` (陰影屬性/影子屬性)的概念,你可以通過 FluentAPI 的方式來定義一個不在 .NET model 里定義的屬性,只能通過 EF 里的 `Change Tracker` 來操作這種屬性。 在導出 Excel 的時候,可能希望導出的列並不... ...
  • 使用NPOI操作Excel,無需Office COM組件 部分代碼來自於:https://docs.microsoft.com/zh-tw/previous-versions/ee818993(v=msdn.10)?redirectedfrom=MSDN using System.Data; usi ...
  • Spire.Cloud.Word.Sdk提供了介面SetBackgroudColor()、SetBackgroudImage()、DeleteBackground()、GetBackgroudColor()用於設置、刪除及讀取Word文檔背景。本文將以C#程式為例演示如何來調用API介面實現以上內容 ...
  • 說明:在同一視窗打開鏈接,只要稍加改造就可以實現,這裡實現的是在新Tab頁打開鏈接,並且支持帶type="POST" target="_blank"的鏈接 github和bitbucket上相關問題: 1、WPF empty POST data when using custom popup htt ...
  • 前言 公司項目需要做個畫線縮放,我司稱之為瞳距縮放,簡而言之就是:2張圖,從第一張圖畫一條線,再從第二個圖畫一條線,第二條線以第一條為基準,延長到一致的長度,並同比縮放圖片;文字太枯燥,請先實例圖 例子1:以皮卡丘為例,我要把路飛的拳頭縮放到皮卡丘頭那麼大 例子2:以皮卡丘的基準,縮小路飛,與其身高 ...
  • 9月份的時候,微軟宣佈正式發佈C 8.0,作為.NET Core 3.0發行版的一部分。C 8.0的新特性之一就是預設介面實現。在本文中,我們將一起來聊聊預設介面實現。 作者:依樂祝 原文鏈接:https://www.cnblogs.com/yilezhu/p/12034584.html 提前說下: ...
  • 對於地圖坐標偏移,以leaflet為例,有如下解決辦法 方法1、修改leaflet源碼,解決地圖坐標偏移問題 方法2、將點位真實的經緯度經過偏移演算法,添加到加密的地圖上 方法3、直接對離線地圖瓦片進行糾偏 方法1需要修改源碼 方法2有缺陷,地圖依然是偏移的,如果把地圖經緯度顯示出來,經緯度也是不對的 ...
  • 引用類庫 1.Install-Package Microsoft.Extensions.Caching.Memory MemoryCacheOptions 緩存配置 1.ExpirationScanFrequency 獲取或設置對過期項的連續掃描之間的最短時間間隔 2.SizeLimit 緩存是沒有 ...
  • 原文:https://blogs.msdn.microsoft.com/mazhou/2017/12/12/c-7-series-part-7-ref-returns/ 背景 有兩種方法可以將一個值傳遞給一個方法: 例如,FCL(.NET Framework Class Library)中的Arra ...
x