Asp.NetCoreWebApi - RESTful Api

目录 - [参考文章](#参考文章) - [REST](#rest) - [常用http动词](#常用http动词) - [WebApi 在 Asp.NetCore 中的实现](#webapi-在-aspnetcore-中的实现) - [创建WebApi项目.](#创建webapi项目) - [集成Entity Framework Core操作Mysql](#集成entity-framework-core操作mysql) - [安装相关的包(为Xxxx.Infrastructure项目安装)](#安装相关的包为xxxxinfrastructure项目安装) - [建立Entity和Context](#建立entity和context) - [ConfigureService中注入EF服务](#configureservice中注入ef服务) - [迁移数据库](#迁移数据库) - [迁移数据库失败, 提示 `Unable to create an object of type 'Context'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728`](#迁移数据库失败-提示-unable-to-create-an-object-of-type-xxxxcontext-for-the-different-patterns-supported-at-design-time-see-httpsgomicrosoftcomfwlinklinkid851728) - [数据库迁移结果](#数据库迁移结果) - [为数据库创建种子数据](#为数据库创建种子数据) - [支持https](#支持https) - [支持HSTS](#支持hsts) - [使用SerilLog](#使用serillog) - [安装nuget包](#安装nuget包) - [添加代码](#添加代码) - [自行测试](#自行测试) - [Asp.NetCore配置文件](#aspnetcore配置文件) - [默认配置文件](#默认配置文件) - [获得配置](#获得配置) - [自定义一个异常处理,ExceptionHandler](#自定义一个异常处理exceptionhandler) - [弄一个类,写一个扩展方法处理异常](#弄一个类写一个扩展方法处理异常) - [在Configuration中使用扩展方法](#在configuration中使用扩展方法) - [实现数据接口类(Resource),使用AutoMapper在Resource和Entity中映射](#实现数据接口类resource使用automapper在resource和entity中映射) - [为Entity类创建对应的Resource类](#为entity类创建对应的resource类) - [使用 AutoMapper](#使用-automapper) - [使用FluentValidation](#使用fluentvalidation) - [安装Nuget包](#安装nuget包) - [为每一个Resource配置验证器](#为每一个resource配置验证器) - [实现Http Get(翻页,过滤,排序)](#实现http-get翻页过滤排序) - [资源命名](#资源命名) - [资源应该使用名词,例](#资源应该使用名词例) - [资源命名层次结构](#资源命名层次结构) - [内容协商](#内容协商) - [翻页](#翻页) - [构造翻页请求参数类](#构造翻页请求参数类) - [Repository实现支持翻页请求参数的方法](#repository实现支持翻页请求参数的方法) - [搜索(过滤)](#搜索过滤) - [排序](#排序) - [>排序思路](#排序思路) - [资源塑形(Resource shaping)](#资源塑形resource-shaping) - [HATEOAS](#hateoas) - [创建供应商特定媒体类型](#创建供应商特定媒体类型) - [判断Media Type类型](#判断media-type类型) - [Post添加资源](#post添加资源) - [安全性和幂等性](#安全性和幂等性) - [代码实现](#代码实现) - [Delete](#delete) - [PUT & PATCH](#put--patch) - [PUT 整体更新](#put-整体更新) - [PATCH](#patch) - [Http常用方法总结](#http常用方法总结)

参考文章


REST

REST : 具象状态传输(Representational State Transfer,简称REST),是Roy Thomas Fielding博士于2000年在他的博士论文 "Architectural Styles and the Design of Network-based Software Architectures" 中提出来的一种万维网软件架构风格。
目前在三种主流的Web服务实现方案中,因为REST模式与复杂的SOAPXML-RPC相比更加简洁,越来越多的web服务开始采用REST风格设计和实现。例如,Amazon.com提供接近REST风格的Web服务执行图书查询;

符合REST设计风格的Web API称为RESTful API。它从以下三个方面资源进行定义:

  • 直观简短的资源地址:URI,比如:http://example.com/resources/ .
  • 传输的资源:Web服务接受与返回的互联网媒体类型,比如:JSON,XML,YAML等...
  • 对资源的操作:Web服务在该资源上所支持的一系列请求方法(比如:POST,GET,PUT或DELETE).

PUT和DELETE方法是幂等方法.GET方法是安全方法(不会对服务器端有修改,因此当然也是幂等的).

ps 关于幂等方法 :
看这篇 理解HTTP幂等性.
简单说,客户端多次请求服务端返回的结果都相同,那么就说这个操作是幂等的.(个人理解,详细的看上面给的文章)

不像基于SOAP的Web服务,RESTful Web服务并没有“正式”的标准。这是因为REST是一种架构,而SOAP只是一个协议。虽然REST不是一个标准,但大部分RESTful Web服务实现会使用HTTP、URI、JSON和XML等各种标准。

常用http动词

括号中是相应的SQL命令.

  • GET(SELECT) : 从服务器取出资源(一项或多项).
  • POST(CREATE) : 在服务器新建一个资源.
  • PUT(UPDATE) : 在服务器更新资源(客户端提供改变后的完整资源).
  • PATCH(UPDATE) : 在服务器更新资源(客户端提供改变的属性).
  • DELETE(DELETE) : 在服务器删除资源.

WebApi 在 Asp.NetCore 中的实现

这里以用户增删改查为例.

创建WebApi项目.

参考ASP.NET Core WebAPI 开发-新建WebAPI项目.

注意,本文建立的Asp.NetCore WebApi项目选择.net core版本是2.2,不建议使用其他版本,2.1版本下会遇到依赖文件冲突问题!所以一定要选择2.2版本的.net core.

集成Entity Framework Core操作Mysql

安装相关的包(为Xxxx.Infrastructure项目安装)

  • Microsoft.EntityFrameworkCore.Design
  • Pomelo.EntityFrameworkCore.MySql

这里注意一下,Mysql官方的包是 MySql.Data.EntityFrameworkCore,但是这个包有bug,我在github上看到有人说有替代方案 - Pomelo.EntityFrameworkCore.MySql,经过尝试,后者比前者好用.所有这里就选择后者了.使用前者的话可能会导致数据库迁移失败(Update的时候).

PS: Mysql文档原文:

Install the MySql.Data.EntityFrameworkCore NuGet package.
For EF Core 1.1 only: If you plan to scaffold a database, install the MySql.Data.EntityFrameworkCore.Design NuGet package as well.

EFCore - MySql文档
Mysql版本要求:
Mysql版本要高于5.7
使用最新版本的Mysql Connector(2019 6/27 目前是8.x).

为Xxxx.Infrastructure项目安装EFCore相关的包:
Asp.NetCoreWebApi - RESTful Api

为Xxxx.Api项目安装 Pomelo.EntityFrameworkCore.MySql
Asp.NetCoreWebApi - RESTful Api

建立Entity和Context

Asp.NetCoreWebApi - RESTful Api

ApiUser ```CSharp namespace ApiStudy.Core.Entities { using System; public class ApiUser { public Guid Guid { get; set; } public string Name { get; set; } public string Passwd { get; set; } public DateTime RegistrationDate { get; set; } public DateTime Birth { get; set; } public string ProfilePhotoUrl { get; set; } public string PhoneNumber { get; set; } public string Email { get; set; } } } ```
UserContext ```CSharp namespace ApiStudy.Infrastructure.Database { using ApiStudy.Core.Entities; using Microsoft.EntityFrameworkCore; public class UserContext:DbContext { public UserContext(DbContextOptions options): base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity().HasKey(u => u.Guid); base.OnModelCreating(modelBuilder); } public DbSet ApiUsers { get; set; } } } ```

ConfigureService中注入EF服务

services.AddDbContext<UserContext>(options =>
            {
                string connString = "Server=Xxx:xxx:xxx:xxx;Database=Xxxx;Uid=root;Pwd=Xxxxx; ";
                options.UseMySQL(connString);
            });

迁移数据库

  • 在Tools > NuGet Package Manager > Package Manager Console输入命令.
  • Add-Migration Xxx 添加迁移.
    PS : 如果迁移不想要,使用 Remove-Migration 命令删除迁移.
  • Update-Database 更新到数据库.

迁移数据库失败, 提示 Unable to create an object of type '<Xxxx>Context'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

原因应该是EfCore迁移工具不知道如何创建 DbContext 导致的.

解决方案

DbContext所在的项目下新建一个类:

/// <summary>
/// 设计时DbContext的创建, 告诉EF Core迁移工具如何创建DbContext
/// </summary>
public class <Xxxx>ContextFactory : IDesignTimeDbContextFactory<<Xxxx>Context>
{
    public <Xxxx>Context CreateDbContext(string[] args)
    {
        var optionsBuilder = new DbContextOptionsBuilder<<Xxxx>Context>();
        optionsBuilder.UseMySql(
            @"Server=[服务器ip];Database=[数据库]];Uid=[用户名];Pwd=[密码];");

        return new <Xxxx>Context(optionsBuilder.Options);
    }
}

数据库迁移结果

Asp.NetCoreWebApi - RESTful Api

为数据库创建种子数据

  • 写一个创建种子数据的类


    UserContextSeed

    namespace ApiStudy.Infrastructure.Database
    {
        using ApiStudy.Core.Entities;
        using Microsoft.Extensions.Logging;
        using System;
        using System.Linq;
        using System.Threading.Tasks;
    
        public class UserContextSeed
        {
            public static async Task SeedAsync(UserContext context,ILoggerFactory loggerFactory)
            {
                try
                {
                    if (!context.ApiUsers.Any())
                    {
                        context.ApiUsers.AddRange(
                            new ApiUser
                            {
                                Guid = Guid.NewGuid(),
                                Name = "la",
                                Birth = new DateTime(1998, 11, 29),
                                RegistrationDate = new DateTime(2019, 6, 28),
                                Passwd = "123587",
                                ProfilePhotoUrl = "https://www.laggage.top/",
                                PhoneNumber = "10086",
                                Email = "yu@outlook.com"
                            },
                            new ApiUser
                            {
                                Guid = Guid.NewGuid(),
                                Name = "David",
                                Birth = new DateTime(1995, 8, 29),
                                RegistrationDate = new DateTime(2019, 3, 28),
                                Passwd = "awt87495987",
                                ProfilePhotoUrl = "https://www.laggage.top/",
                                PhoneNumber = "1008611",
                                Email = "David@outlook.com"
                            },
                            new ApiUser
                            {
                                Guid = Guid.NewGuid(),
                                Name = "David",
                                Birth = new DateTime(2001, 8, 19),
                                RegistrationDate = new DateTime(2019, 4, 25),
                                Passwd = "awt87495987",
                                ProfilePhotoUrl = "https://www.laggage.top/",
                                PhoneNumber = "1008611",
                                Email = "David@outlook.com"
                            },
                            new ApiUser
                            {
                                Guid = Guid.NewGuid(),
                                Name = "Linus",
                                Birth = new DateTime(1999, 10, 26),
                                RegistrationDate = new DateTime(2018, 2, 8),
                                Passwd = "awt87495987",
                                ProfilePhotoUrl = "https://www.laggage.top/",
                                PhoneNumber = "17084759987",
                                Email = "Linus@outlook.com"
                            },
                            new ApiUser
                            {
                                Guid = Guid.NewGuid(),
                                Name = "YouYou",
                                Birth = new DateTime(1992, 1, 26),
                                RegistrationDate = new DateTime(2015, 7, 8),
                                Passwd = "grwe874864987",
                                ProfilePhotoUrl = "https://www.laggage.top/",
                                PhoneNumber = "17084759987",
                                Email = "YouYou@outlook.com"
                            },
                            new ApiUser
                            {
                                Guid = Guid.NewGuid(),
                                Name = "小白",
                                Birth = new DateTime(1997, 9, 30),
                                RegistrationDate = new DateTime(2018, 11, 28),
                                Passwd = "gewa749864",
                                ProfilePhotoUrl = "https://www.laggage.top/",
                                PhoneNumber = "17084759987",
                                Email = "BaiBai@outlook.com"
                            });
    
                        await context.SaveChangesAsync();
                    }
                }
                catch(Exception ex)
                {
                    ILogger logger = loggerFactory.CreateLogger<UserContextSeed>();
                    logger.LogError(ex, "Error occurred while seeding database");
                }
            }
        }
    }
    

上一篇:Linq在Lims系统中的用法


下一篇:Delphi - 生成GUID