跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C#

C# Web API 基础教程:从 Minimal APIs 到 EF Core

C# Web API 开发指南涵盖项目创建、Minimal APIs 使用、中间件管道、控制器路由实现、模型绑定、数据注解验证、过滤器机制及数据库集成。内容包含基于 Entity Framework Core 的 CRUD 操作,以及 JWT 令牌生成与安全验证流程。通过衬衫管理案例演示了从路由配置到异常处理的完整生命周期,适合初学者掌握 ASP.NET Core Web API 核心功能。

www发布于 2026/4/8更新于 2026/7/737 浏览

1、创建 WebApi 项目

文章配图

文章配图

文章配图

Program.cs 代码保留如下

var builder = WebApplication.CreateBuilder(args); // Add services to the container.
var app = builder.Build(); // Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.Run();

2、Minimal APIs 最小 API 使用

Program.cs 中进行最小 API 使用

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseHttpsRedirection();
//获取所有衬衫数据列表
app.MapGet("/shirts", () => {
    return "获取所有衬衫数据列表";
});
//获取指定 ID 的衬衫数据
app.MapGet("/shirts/{id}", (int id) => {
    return $"获取 ID 为 {id} 的衬衫数据";
});
//创建一件新的衬衫
app.MapPost("/shirts", () => {
    return "创建一件新的衬衫";
});
//更新指定 ID 的衬衫数据
app.MapPut("/shirts/{id}", (int id) => {
    return $"更新 ID 为 {id} 的衬衫数据";
});
//删除指定 ID 的衬衫数据
app.MapDelete("/shirts/{id}", (int id) => {
    return $"删除 ID 为 {id} 的衬衫数据";
});
app.Run();

3、ASP.NET Core 中间件管道

文章配图

文章配图

app 中的 Use 开头的方法都是中间件组件使用方法

文章配图

4、Web API 控制器实现 Web API

方法 1:将路由写在方法前面并指定操作动词

using Microsoft.AspNetCore.Mvc;
namespace WebApiTest.Controllers
{
    [ApiController]
    public class ShirtsController : ControllerBase
    {
        //获取所有衬衫数据列表
        [HttpGet]
        [Route("api/shirts")]
        public string GetShirts()
        {
            return "获取所有衬衫数据列表";
        }
        //获取指定 ID 的衬衫数据
        [HttpGet]
        [Route("api/shirts/{id}")]
        public string GetShirtById(int id)
        {
            return $"获取 ID 为 {id} 的衬衫数据";
        }
        //创建一件新的衬衫
        [HttpPost]
        [Route("api/shirts")]
        public string CreateShirt()
        {
            return "创建一件新的衬衫";
        }
        //更新指定 ID 的衬衫数据
        [HttpPut]
        [Route("api/shirts/{id}")]
        public string UpdateShirt(int id)
        {
            return $"更新 ID 为 {id} 的衬衫数据";
        }
        //删除指定 ID 的衬衫数据
        [HttpDelete]
        [Route("api/shirts/{id}")]
        public string DeleteShirt(int id)
        {
            return $"删除 ID 为 {id} 的衬衫数据";
        }
    }
}

方法 2:将路由写在类前面,再在方法前面指定操作动词

using Microsoft.AspNetCore.Mvc;
namespace WebApiTest.Controllers
{
    [ApiController]
    [Route("/api/[controller]")]
    public class ShirtsController : ControllerBase
    {
        //获取所有衬衫数据列表
        [HttpGet]
        public string GetShirts()
        {
            return "获取所有衬衫数据列表";
        }
        //获取指定 ID 的衬衫数据
        [HttpGet("{id}")]
        public string GetShirtById(int id)
        {
            return $"获取 ID 为 {id} 的衬衫数据";
        }
        //创建一件新的衬衫
        [HttpPost]
        public string CreateShirt()
        {
            return "创建一件新的衬衫";
        }
        //更新指定 ID 的衬衫数据
        [HttpPut("{id}")]
        public string UpdateShirt(int id)
        {
            return $"更新 ID 为 {id} 的衬衫数据";
        }
        //删除指定 ID 的衬衫数据
        [HttpDelete("{id}")]
        public string DeleteShirt(int id)
        {
            return $"删除 ID 为 {id} 的衬衫数据";
        }
    }
}

5、基于控制器的 Web API 的路由

使用注解 [Route("/shirts")] 设置 Web API 的 URL,可以在方法前使用,也可以在类前使用

方法前

文章配图

类前

文章配图

6、模型绑定,将 Http 请求中的数据映射到操作方法的参数

6.1 从路由绑定,在路由 Route("/shirts/{id}") 或在操作动词 HttpGet("{id}") 设置,在方法参数前设置 [FromRoute] 也可以省略

[HttpGet("{id}")]
public string GetShirtById([FromRoute]int id)
{
    return $"获取 ID 为 {id} 的衬衫数据";
}

6.2 从查询字符串中绑定,方法参数前使用注解 [FromQuery]

http://localhost:5186/api/shirts/1?color=红色

[HttpGet("{id}")]
public string GetShirtById([FromRoute]int id, [FromQuery]string color)
{
    return $"获取 ID 为 {id} 的衬衫数据";
}

6.3 从请求头 Header 中绑定,方法参数前使用注解 [FromHeader]

[HttpGet("{id}")]
public string GetShirtById([FromRoute]int id, [FromQuery]string color, [FromHeader]int size)
{
    return $"获取 ID 为 {id} 的衬衫数据,衬衫颜色为{color},大小为{size}";
}

6.4 从请求体 Body JSON 格式中绑定,方法参数前使用注解 [FromBody]

[HttpPost]
public string CreateShirt([FromBody]Shirt shirt)
{
    return $"创建一件新的衬衫,{shirt.Id}, {shirt.Name}, {shirt.Color}, {shirt.Gender}, {shirt.Price}";
}

6.5 从请求体 Body 表单中绑定,方法参数前使用注解 [FromForm]

[HttpPost]
public string CreateShirt([FromForm]Shirt shirt)
{
    return $"创建一件新的衬衫,{shirt.Id}, {shirt.Name}, {shirt.Color}, {shirt.Gender}, {shirt.Price}";
}

7、数据注解模型验证

文章配图

8、ValidationAttribute 模型验证,继承 ValidationAttribute

创建脚本 Shirt_EnsureCorrectSizingAttribute.cs

using System.ComponentModel.DataAnnotations;
using WebApiDemo.Models;
namespace WebApiTest.Models.Validations
{
    public class Shirt_EnsureCorrectSizingAttribute : ValidationAttribute
    {
        protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
        {
            var shirt = validationContext.ObjectInstance as Shirt;
            if(shirt != null && !string.IsNullOrEmpty(shirt.Gender))
            {
                if(shirt.Gender.Equals("men", StringComparison.OrdinalIgnoreCase) && shirt.Size < 8)
                {
                    return new ValidationResult("男性衬衫的尺码必须大于或等于 8。");
                }
                else if (shirt.Gender.Equals("women", StringComparison.OrdinalIgnoreCase) && shirt.Size < 6)
                {
                    return new ValidationResult("女性衬衫的尺码必须大于或等于 6。");
                }
            }
            return ValidationResult.Success;
        }
    }
}

在属性前添加注解

文章配图

9、Web API 返回类型

返回类型使用 IActionResult

正确返回使用 Ok(返回数据)

未找到使用 NotFound()

错误响应使用 BadRequest()

[HttpGet("{id}")]
public IActionResult GetShirtById([FromRoute]int id, [FromQuery]string color, [FromHeader]int size)
{
    if(id <= 0)
    {
        //错误响应
        return BadRequest();
    }
    else if(id > 10)
    {
        //未找到
        return NotFound();
    }
    //正确响应
    return Ok($"获取 ID 为 {id} 的衬衫数据,衬衫颜色为{color},大小为{size}");
}

10、操作过滤器进行模型验证,继承 ActionFilterAttribute

用户输入传递的 id 可能不符合规范,可以通过操作过滤器进行模型验证对 id 进行验证

创建脚本 Shirt_ValidateShirtIdFilterAttribute

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace WebApiTest.Filters.ActionFilters
{
    public class Shirt_ValidateShirtIdFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            base.OnActionExecuting(context);
            var id = context.ActionArguments["id"] as int?;
            if (id.HasValue)
            {
                if(id.Value <= 0)
                {
                    context.ModelState.AddModelError("Id", "衬衫 ID 必须大于 0。");
                    var problemDetails = new Microsoft.AspNetCore.Mvc.ValidationProblemDetails(context.ModelState)
                    {
                        Status = 400,
                        Title = "请求参数错误",
                    };
                    context.Result = new BadRequestObjectResult(problemDetails);
                }
            }
        }
    }
}

需要使用验证的方法前添加注解

文章配图

11、异常过滤器实现异常处理

更新衬衫之前可能其他请求把该衬衫已经删掉,更新时可能会报错,模拟该情景

创建脚本 Shirt_HandleUpdateExceptionFilterAttribute

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace WebApiTest.Filters.ExceptionFilters
{
    public class Shirt_HandleUpdateExceptionFilterAttribute : ExceptionFilterAttribute
    {
        public int?[] shirtsId = new int?[] { 1, 2, 3, 4, 5 };
        public override void OnException(ExceptionContext context)
        {
            base.OnException(context);
            //判断衬衫 ID 是否存在 暂时假设 shirtsId 为数据库中已有的衬衫 ID 列表
            var strShirtId = context.RouteData.Values["id"] as string;
            if (int.TryParse(strShirtId, out int shirtId))
            {
                if (shirtsId.FirstOrDefault(x => x == shirtId) == null)
                {
                    context.ModelState.AddModelError("Id", $"衬衫已经不存在");
                    var problemDetails = new ValidationProblemDetails(context.ModelState)
                    {
                        Status = StatusCodes.Status404NotFound,
                    };
                    context.Result = new NotFoundObjectResult(problemDetails);
                }
            }
        }
    }
}

在需要异常处理的方法前使用

文章配图

12、Web API 操作数据库

案例使用 SqlServer 数据库

12.1 下载安装 Sql Server Developer Edition 和 SQL Server Management Studio

Sql Server Developer Edition

SQL Server Management Studio

文章配图

打开 SQL Server Management Studio 连接本地数据库

文章配图

12.2 安装需要使用的包
  • EntityFrameworkCore
  • EntityFrameworkCore.Design
  • EntityFrameworkCore.Tools
  • EntityFrameworkCore.SqlServer

1)打开 管理 NuGet 程序包

文章配图

2)搜索安装指定的包

文章配图

3)双击查看是否安装成功

文章配图

文章配图

12.3 创建数据库上下文

1)创建脚本 ApplicationDbContext

using Microsoft.EntityFrameworkCore;
using WebApiDemo.Models;
namespace WebApiTest.Data
{
    public class ApplicationDbContext : DbContext
    {
        public DbSet<Shirt> Shirts { get; set; }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //数据播种
            modelBuilder.Entity<Shirt>().HasData(
                new Shirt { Id = 1, Name = "衬衫 1", Color = "red", Gender = "men", Size = 10, Price = 100 },
                new Shirt { Id = 2, Name = "衬衫 2", Color = "blue", Gender = "men", Size = 12, Price = 140 },
                new Shirt { Id = 3, Name = "衬衫 3", Color = "黑色", Gender = "women", Size = 11, Price = 132 },
                new Shirt { Id = 4, Name = "衬衫 4", Color = "白色", Gender = "women", Size = 7, Price = 151 }
            );
        }
    }
}
12.4 执行数据库迁移

1)添加连接字符串

文章配图

文章配图

2)获取连接字符串,视图->服务器资源管理器->数据连接

文章配图

文章配图

文章配图

文章配图

文章配图

//需要将 master 替换为需要创建的数据库名称
Data Source=(local);Initial Catalog=master;Integrated Security=True;Trust Server Certificate=True

3)数据库上下文中创建构造函数指定连接数据库位置

using Microsoft.EntityFrameworkCore;
using WebApiDemo.Models;
namespace WebApiTest.Data
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions options) : base(options) { }
        public DbSet<Shirt> Shirts { get; set; }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //数据播种
            modelBuilder.Entity<Shirt>().HasData(
                new Shirt { Id = 1, Name = "衬衫 1", Color = "red", Gender = "men", Size = 10, Price = 100 },
                new Shirt { Id = 2, Name = "衬衫 2", Color = "blue", Gender = "men", Size = 12, Price = 140 },
                new Shirt { Id = 3, Name = "衬衫 3", Color = "黑色", Gender = "women", Size = 11, Price = 132 },
                new Shirt { Id = 4, Name = "衬衫 4", Color = "白色", Gender = "women", Size = 7, Price = 151 }
            );
        }
    }
}

4)在 Program.cs 添加运行 EntityFrameworkCore 所需的服务

文章配图

builder.Services.AddDbContext<ApplicationDbContext>(options => {
    options.UseSqlServer(builder.Configuration.GetConnectionString("ShirtStoreManagement"));
});

5)创建迁移代码并进行迁移

文章配图

控制台运行命令 Add-Migration Init(Init 可以替换为该次进行操作说明)

文章配图

运行成功会创建在迁移文件夹下创建迁移代码

文章配图

在控制台运行迁移代码,Update-Database Init(Init 为迁移代码名称,不指定会执行最新的代码)

文章配图

执行成功可以在查看数据库创建成功

文章配图

12.5 使用 EF Core 实现 Get 端点

在 ShirtsController 构造函数中依赖注入 Db 上下文

文章配图

//获取所有衬衫数据列表
[HttpGet]
public IActionResult GetShirts()
{
    return Ok(db.Shirts.ToList());
}
12.6 使用 EF Core 实现 Get by Id 端点

修改 Shirt_ValidateShirtIdFilterAttribute,实现验证 ID 在数据库是否存在

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using WebApiTest.Data;
namespace WebApiTest.Filters.ActionFilters
{
    public class Shirt_ValidateShirtIdFilterAttribute : ActionFilterAttribute
    {
        private readonly ApplicationDbContext db;
        public Shirt_ValidateShirtIdFilterAttribute(ApplicationDbContext db)
        {
            this.db = db;
        }
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            base.OnActionExecuting(context);
            var id = context.ActionArguments["id"] as int?;
            if (id.HasValue)
            {
                if(id.Value <= 0)
                {
                    context.ModelState.AddModelError("Id", "衬衫 ID 必须大于 0。");
                    var problemDetails = new ValidationProblemDetails(context.ModelState)
                    {
                        Status = 400,
                        Title = "请求参数错误",
                    };
                    context.Result = new BadRequestObjectResult(problemDetails);
                }
                else
                {
                    var shirt = db.Shirts.Find(id.Value);
                    if(shirt == null)
                    {
                        context.ModelState.AddModelError("Id", $"ID 为{id.Value}的衬衫在数据库不存在");
                        var problemDetails = new ValidationProblemDetails(context.ModelState)
                        {
                            Status = 400,
                            Title = "衬衫 ID 不存在",
                        };
                        context.Result = new NotFoundObjectResult(problemDetails);
                    }
                    else
                    {
                        context.HttpContext.Items["shirt"] = shirt;
                    }
                }
            }
        }
    }
}

在操作过滤器中查询数据库存在 ID 衬衫在 context.HttpContext.Items 中进行存在,然后在控制器中获取,避免重复进行数据库查询减少性能开销

控制器查询代码

//获取指定 ID 的衬衫数据
[HttpGet("{id}")]
[TypeFilter(typeof(Shirt_ValidateShirtIdFilterAttribute))]
public IActionResult GetShirtById([FromRoute]int id)
{
    //正确响应
    return Ok(HttpContext.Items["shirt"]);
}
12.7 使用 EF Core 实现 Post 端点
//创建一件新的衬衫
[HttpPost]
public IActionResult CreateShirt([FromBody]Shirt shirt)
{
    this.db.Shirts.Add(shirt);
    this.db.SaveChanges();
    return CreatedAtAction(nameof(GetShirtById), new { id = shirt.Id}, shirt);
}
12.8 使用 EF Core 实现 Put 端点

修改代码 Shirt_HandleUpdateExceptionFilterAttribute,实现验证当前 ID 数据是否还存在,可能存在当前操作之前数据被删除的可能

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using WebApiTest.Data;
namespace WebApiTest.Filters.ExceptionFilters
{
    public class Shirt_HandleUpdateExceptionFilterAttribute : ExceptionFilterAttribute
    {
        private readonly ApplicationDbContext db;
        public Shirt_HandleUpdateExceptionFilterAttribute(ApplicationDbContext db)
        {
            this.db = db;
        }
        public override void OnException(ExceptionContext context)
        {
            base.OnException(context);
            //判断衬衫 ID 是否存在 暂时假设 shirtsId 为数据库中已有的衬衫 ID 列表
            var strShirtId = context.RouteData.Values["id"] as string;
            if (int.TryParse(strShirtId, out int shirtId))
            {
                if (this.db.Shirts.FirstOrDefault(x => x.Id == shirtId) == null)
                {
                    context.ModelState.AddModelError("Id", $"衬衫已经在数据库不存在,可能在此之前被删除了");
                    var problemDetails = new ValidationProblemDetails(context.ModelState)
                    {
                        Status = StatusCodes.Status404NotFound,
                    };
                    context.Result = new NotFoundObjectResult(problemDetails);
                }
            }
        }
    }
}

控制器更新代码

//更新指定 ID 的衬衫数据
[HttpPut("{id}")]
[TypeFilter(typeof(Shirt_ValidateShirtIdFilterAttribute))]
[TypeFilter(typeof(Shirt_HandleUpdateExceptionFilterAttribute))]
public IActionResult UpdateShirt(int id, [FromBody]Shirt shirt)
{
    var shirtToUpdate = HttpContext.Items["shirt"] as Shirt;
    shirtToUpdate.Name = shirt.Name;
    shirtToUpdate.Size = shirt.Size;
    shirtToUpdate.Color = shirt.Color;
    shirtToUpdate.Gender = shirt.Gender;
    shirtToUpdate.Price = shirt.Price;
    this.db.SaveChanges();
    return Ok($"ID 为{id}衬衫更新成功");
}
12.9 使用 EF Core 实现 Delete 端点
//删除指定 ID 的衬衫数据
[HttpDelete("{id}")]
[TypeFilter(typeof(Shirt_ValidateShirtIdFilterAttribute))]
public IActionResult DeleteShirt(int id)
{
    var shirtToDelete = HttpContext.Items["shirt"] as Shirt;
    this.db.Shirts.Remove(shirtToDelete);
    this.db.SaveChanges();
    return Ok(shirtToDelete);
}

13、Web Api 安全机制,使用 JWT 生成令牌和验证令牌

文章配图

1)创建应用程序信息类,Application.cs

namespace WebApiDemo.Authority
{
    public class Application
    {
        public int ApplicationId { get; set; }
        public string? ApplicationName { get; set; }
        public string? ClientId { get; set; }
        public string? Secret { get; set; }
        public string? Scopes { get; set; }
    }
}

2)创建应用内容存储,AppRepository.cs,替代已存储数据库的应用程序注册信息

namespace WebApiDemo.Authority
{
    public static class AppRepository
    {
        private static List<Application> _applications = new List<Application>
        {
            new Application
            {
                ApplicationId = 1,
                ApplicationName = "MVCWebApp",
                ClientId = "53D3C1E6-4587-4AD5-8C6E-A8E4BD59940E",
                Secret = "0673FC70-0514-4011-B4A3-DF9BC03201BC",
                Scopes = "read,write,delete"
            }
        };
        public static Application? GetApplicationByClientId(string clientId)
        {
            return _applications.FirstOrDefault(app => app.ClientId == clientId);
        }
    }
}

3)创建应用凭证类,AppCredential.cs,用于接受认证请求参数

namespace WebApiDemo.Authority
{
    public class AppCredential
    {
        public string? ClientId { get; set; } = string.Empty;
        public string? Secret { get; set; } = string.Empty;
    }
}

4)创建应用认证控制器接口,AuthorityController.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using WebApiDemo.Authority;
namespace WebApiDemo.Controllers
{
    [ApiController]
    public class AuthorityController : ControllerBase
    {
        private readonly IConfiguration configuration;
        public AuthorityController(IConfiguration configuration)
        {
            this.configuration = configuration;
        }
        [HttpPost("auth")]
        public IActionResult Authenticate([FromBody] AppCredential credential)
        {
            if (Authenticator.Authenticate(credential.ClientId, credential.Secret))
            {
                var expiresAt = DateTime.UtcNow.AddMinutes(10);
                return Ok(new { access_token = Authenticator.CreateToken(credential.ClientId, expiresAt, configuration["SecurityKey"] ?? string.Empty), expires_at = expiresAt });
            }
            else
            {
                ModelState.AddModelError("Unauthorized", "未被授权");
                var problemDetails = new ValidationProblemDetails(ModelState)
                {
                    Status = StatusCodes.Status401Unauthorized,
                };
                return new UnauthorizedObjectResult(problemDetails);
            }
        }
    }
}

5)设置密钥

文章配图

6)创建应用认证身份验证逻辑类,Authenticator.cs

using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
namespace WebApiDemo.Authority
{
    public static class Authenticator
    {
        public static bool Authenticate(string clientId, string secret)
        {
            var app = AppRepository.GetApplicationByClientId(clientId);
            if (app == null) return false;
            return (app.ClientId == clientId && app.Secret == secret);
        }
        public static string CreateToken(string clientId, DateTime expiresAt, string strSecretKey)
        {
            //安全算法
            //签名密钥
            //负载(声明)
            //生成签名
            //算法
            var signingCredentials = new SigningCredentials(
                new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(strSecretKey)),
                SecurityAlgorithms.HmacSha256Signature);
            //负载(声明)
            var app = AppRepository.GetApplicationByClientId(clientId);
            var claimsDictionary = new Dictionary<string, object>
            {
                { "AppName", app?.ApplicationName??string.Empty },
                //{ "Read", (app?.Scopes??string.Empty).Contains("read") ? "true" : "false" },
                //{ "Write", (app?.Scopes??string.Empty).Contains("write") ? "true" : "false" }
            };
            var scopes = app?.Scopes?.Split(',') ?? Array.Empty<string>();
            if(scopes.Length > 0)
            {
                foreach(var scope in scopes)
                {
                    claimsDictionary.Add(scope.Trim().ToLower(), "true");
                }
            }
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                SigningCredentials = signingCredentials,
                Claims = claimsDictionary,
                Expires = expiresAt,
                NotBefore = DateTime.UtcNow,
            };
            var tokenHandler = new JsonWebTokenHandler();
            return tokenHandler.CreateToken(tokenDescriptor);
        }
        public static async Task<IEnumerable<Claim>?> VerifyTokenAsync(string tokenString, string securityKey)
        {
            if(string.IsNullOrWhiteSpace(tokenString) || string.IsNullOrWhiteSpace(securityKey))
            {
                return null;
            }
            var keyBytes = System.Text.Encoding.UTF8.GetBytes(securityKey);
            var tokenHandle = new JsonWebTokenHandler();
            var validationParameters = new TokenValidationParameters
            {
                ValidateIssuer = false,
                IssuerSigningKey = new SymmetricSecurityKey(keyBytes),
                ValidateIssuerSigningKey = false,
                ValidateAudience = false,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero,
            };
            try
            {
                var result = await tokenHandle.ValidateTokenAsync(tokenString, validationParameters);
                if(result.SecurityToken != null)
                {
                    var tokenObject = tokenHandle.ReadJsonWebToken(tokenString);
                    return tokenObject.Claims ?? Enumerable.Empty<Claim>();
                }
                else
                {
                    return null;
                }
            }
            catch (SecurityTokenMalformedException)
            {
                return null;
            }
            catch (SecurityTokenExpiredException)
            {
                return null;
            }
            catch (SecurityTokenInvalidSignatureException)
            {
                return null;
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}

7)通过过滤器方式实现 JWT 令牌验证

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using WebApiDemo.Attributes;
using WebApiDemo.Authority;
namespace WebApiDemo.Filters.AuthFilters
{
    public class JwtTokenAuthFilterAttribute : Attribute, IAsyncAuthorizationFilter
    {
        public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            //1. 从请求头中获取授权标识
            if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out var authHeader))
            {
                context.Result = new UnauthorizedResult();
                return;
            }
            string tokenString = authHeader.ToString();
            //2. 去掉 Bearer 前缀
            if (tokenString.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
            {
                tokenString = tokenString.Substring("Bearer ".Length).Trim();
            }
            else
            {
                context.Result = new UnauthorizedResult();
                return;
            }
            //3. 获取配置和安全密钥
            var configuration = context.HttpContext.RequestServices.GetService<IConfiguration>();
            var securityKey = configuration?["SecurityKey"] ?? string.Empty;
            //4. 验证令牌 并且 提取声明
            //if(!await Authenticator.VerifyTokenAsync(tokenString, securityKey))
            //{
            //    context.Result = new UnauthorizedResult();
            //}
            var claims = await Authenticator.VerifyTokenAsync(tokenString, securityKey);
            if (claims == null)
            {
                context.Result = new UnauthorizedResult(); // 无效令牌 401
            }
            else
            {
                //5. 获取声明需求
                var requiredClaism = context.ActionDescriptor.EndpointMetadata
                    .OfType<RequiredClaimAttribute>()
                    .ToList();
                if(requiredClaism != null && !requiredClaism.All(rc => claims.Any(c => c.Type.Equals(rc.ClaimType, StringComparison.OrdinalIgnoreCase) && c.Value.Equals(rc.ClaimValue, StringComparison.OrdinalIgnoreCase))))
                {
                    context.Result = new StatusCodeResult(403); // 权限不足 403
                }
            }
        }
    }
}

8)授权、权限作用域校验类,RequiredClaimAttribute.cs

namespace WebApiDemo.Attributes
{
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,AllowMultiple = true)]
    public class RequiredClaimAttribute : Attribute
    {
        public string ClaimType { get; }
        public string ClaimValue { get; }
        public RequiredClaimAttribute(string claimType, string claimValue)
        {
            this.ClaimType = claimType;
            this.ClaimValue = claimValue;
        }
    }
}

目录

  1. 1、创建 WebApi 项目
  2. 2、Minimal APIs 最小 API 使用
  3. 3、ASP.NET Core 中间件管道
  4. 4、Web API 控制器实现 Web API
  5. 5、基于控制器的 Web API 的路由
  6. 6、模型绑定,将 Http 请求中的数据映射到操作方法的参数
  7. 7、数据注解模型验证
  8. 8、ValidationAttribute 模型验证,继承 ValidationAttribute
  9. 9、Web API 返回类型
  10. 10、操作过滤器进行模型验证,继承 ActionFilterAttribute
  11. 11、异常过滤器实现异常处理
  12. 12、Web API 操作数据库
  13. 12.1 下载安装 Sql Server Developer Edition 和 SQL Server Management Studio
  14. 12.2 安装需要使用的包
  15. 12.3 创建数据库上下文
  16. 12.4 执行数据库迁移
  17. 12.5 使用 EF Core 实现 Get 端点
  18. 12.6 使用 EF Core 实现 Get by Id 端点
  19. 12.7 使用 EF Core 实现 Post 端点
  20. 12.8 使用 EF Core 实现 Put 端点
  21. 12.9 使用 EF Core 实现 Delete 端点
  22. 13、Web Api 安全机制,使用 JWT 生成令牌和验证令牌
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Spatial Joy 2025 AR&AI 大赛:开发者资源、玩法与避坑全解析
  • 自然语言转 SQL:利用 AI 工具提升数据库交互效率
  • IntelliJ IDEA 构建时 OutOfMemoryError 错误排查与内存优化
  • DeepSeek 时代:前端开发的范式变革与实战指南
  • 无人机智能巡检系统:建筑物立面安全监测解决方案
  • 异构算力部署通义万相 2.1 文生图技术解析
  • Vue 状态管理实战:Bus 事件总线的核心用法与注意事项
  • 服务器 HBA 卡与 RAID 卡的区别与应用场景
  • SQLyog 11 注册信息整理
  • 开源 ASR 新选择:Fun-ASR 与 Whisper 对比评测
  • Rust 异步并发安全与内存管理最佳实践
  • RAG 的基石:大语言模型文本向量化能力对比
  • Java TCP 与 UDP 网络编程详解
  • 通义万相 2.1 图生视频模型部署指南
  • 高通机器人 RB5 开发套件上手与 AI 部署指南
  • VibeThinker-1.5B:轻量模型如何实现高阶推理突破
  • Linux System V 共享内存详解:从 shmget 到内存映射
  • 国内如何付费升级 GitHub Copilot 专业版
  • Spring Cloud Nacos 服务注册与配置中心实战
  • GitHub Copilot 学生认证及实战使用指南

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online