ASP.NET Core 6 Web API 实战:从 CRUD 到 JWT 认证的完整落地指南
简介这是一份面向 .NET 开发者的 ASP.NET Core 6.0 Web API 入门示例资源演示如何结合 Entity Framework Core 与 RESTful 风格构建数据接口。项目完整包含 Program.cs 服务配置、WeatherForecast 模型、Controllers 控制器、Migrations 数据迁移以及 appsettings.json 配置等核心文件适合需要快速掌握 .NET 6 依赖注入、中间件管道、ORM 映射与 CRUD 接口写法的初中级开发者。资源压缩包共 16 个文件以 cs 源码为主辅以 json 配置文件、解决方案和项目工程文件包体仅 13KB结构精简可直接用 Visual Studio 2022 打开学习或作为新项目脚手架。目前已有 392 人浏览学习。通过该示例可梳理从项目初始化到控制器实现数据操作的完整链路理解 Web API 路由、HTTP 方法映射和 EF Core 数据访问模式便于后续迁移到实际业务开发场景。1. 为什么这个 Web API 示例值得照着敲一遍从 Hello World 到可上线骨架差着一整层很多刚接触 .NET 6 Web API 的开发者都有过这种体验教程里跑通了一个 Hello World真去写生产接口时才发现控制器里塞满了业务逻辑、数据库连接字符串写死在代码里、接口没有认证、Swagger 在服务器上 404。这套 ASP.NET Core 6 Web API 示例做的就是把这层补齐——完整的 CRUD 接口、EF Core 仓储、DTO 映射、JWT 认证、环境相关的配置以及一条能直接部署的启动路径。它不是写给初学者背的是让想快速搭一个可维护后端的开发者照着改的。适合要上手 .NET 6 的从业者也适合要给团队交代一个后端接口规范的人。这篇就把它拆开讲从项目结构一路拆到部署时最容易翻车的地方。2. Web API 项目骨架先看目录和 DI 注册再动手拿到任何一套源码我习惯先不跑起来而是先把目录结构和Program.cs读完。因为这两个地方决定了整套代码的扩展方式新增一个接口时要改几个文件、换数据库时要动哪里、加中间件时插在哪个位置。这套示例的项目分层是典型的三层结构Controllers 只做参数绑定和状态码返回业务逻辑在 Services数据访问在 Repositories实体和请求模型分开存放。2.1 六个项目文件和它们的分工SampleApi/ ├── Controllers/ # 接口入口只做参数绑定、调用服务、返回状态码 ├── Models/ # 实体类对应数据库表结构 ├── DTOs/ # 请求模型和响应模型不直接暴露实体 ├── Data/ # EF Core 上下文、种子数据逻辑 ├── Services/ # 业务规则、事务处理、核心逻辑 ├── Repositories/ # 数据访问封装 EF Core 查询 ├── Program.cs # 应用入口、DI 注册、中间件管道 └── appsettings.json # 连接字符串、JWT 配置、日志级别实体的定义放在 Models但接口的入参和出参放在 DTOs这一层在示例里分得很清楚。一开始我不理解为什么要多写这么多类后来维护一个接口版本混乱的项目才明白实体是数据库的映射接口暴露的字段是交互契约两者混在一起数据库加一列前端就可能收到不想暴露的字段。Repositories 这层看需求如果只是简单的 CRUD直接在 Services 里用 DbContext 也行但示例里保留了它说明设计者是想把查询逻辑也隔离出来方便做单元测试或者以后换数据库。Program.cs 是整套示例的核心入口所有服务注册都集中在这里。看这一层时重点不是看它写了多少行而是看每个服务的生命周期选得对不对选错了到高并发时才暴露问题。2.2 DI 容器里该注册什么Scoped、Singleton、Transient 的选型依赖注入DI在 ASP.NET Core 6 里是内置能力不需要额外引入容器builder.Services就是注册入口。这套示例里可以看到三种生命周期混着用这是经过了实际业务验证的。var builder WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddDbContextAppDbContext(options options.UseSqlServer(builder.Configuration.GetConnectionString(DefaultConnection))); builder.Services.AddScopedIOrderRepository, OrderRepository(); builder.Services.AddScopedIOrderService, OrderService(); builder.Services.AddSingletonIMemoryCacheService, MemoryCacheService(); builder.Services.AddTransientIEmailSender, EmailSender();DbContext 必须注册为 Scoped。它在同一个请求内复用同一个实例请求结束就释放避免了跨请求共享连接带来的并发问题也避免了每次操作都新建连接的开销。OrderRepository 和 OrderService 同样注册为 Scoped因为它们都依赖 DbContext生命周期必须比 DbContext 短或相等否则就会出现“从 Singleton 服务里使用 Scoped 服务”的运行时异常。MemoryCacheService 注册为 Singleton因为内存缓存本身就是全局共享的每个请求各建一个缓存实例反而失去缓存意义。EmailSender 是发邮件的工具类没有内部状态注册为 Transient 最合适——每次请求都拿新实例成本几乎为零。选型边界就一句话有状态的、涉及数据库上下文的选 Scoped纯工具类选 Transient全局只允许存在一份的选 Singleton。常见反模式是图省事把 DbContext 注册成 Singleton启动时不报错第一个请求也没事并发一上来就出现 DbContext 线程安全问题这是最典型的依赖注入翻车现场。2.3 配置读取与环境变量覆盖appsettings.json 里集中存放连接字符串、JWT 密钥、日志级别和允许的主机来源。这份示例的配置结构算得上标准拿过来直接改连接字符串就能跑。{ ConnectionStrings: { DefaultConnection: Serverlocalhost;DatabaseSampleDb;User Idsa;Passwordyour_password;TrustServerCertificateTrue }, Jwt: { Key: a-32-char-secret-key-for-signing, Issuer: SampleApi, Audience: SampleClient, ExpiresMinutes: 120 }, Logging: { LogLevel: { Default: Information, Microsoft.AspNetCore: Warning } }, AllowedHosts: * }读取配置不推荐在控制器里直接IConfiguration[Jwt:Key]这种方式。示例用的是 Options 模式把配置绑定成一个强类型对象builder.Services.AddOptionsJwtOptions() .Bind(builder.Configuration.GetSection(Jwt)) .ValidateDataAnnotations();ValidateDataAnnotations 会在启动时校验 JwtOptions 类上的特性标注比如 Key 不能为空、ExpiresMinutes 必须大于零配置缺失时应用直接启动失败而不是等到用户请求时才报 500。这个习惯非常好能把配置错误提前暴露在部署阶段。部署到服务器时配置项会被环境变量覆盖。规则是 JSON 层级用双下划线分隔比如把ConnectionStrings:DefaultConnection写成环境变量就是ConnectionStrings__DefaultConnection。这套示例写清楚了这层逻辑但我见过不少人在此翻车后面避坑章会详细讲。注意Jwt 的 Key 在 appsettings.json 里只是占位符生产环境必须通过环境变量覆盖不能把真实密钥提交到代码仓库。3. 把 CRUD 做成可维护的接口仓储、DTO 映射与状态码的选择这套示例里最核心的是订单模块的完整 CRUD创建订单、按 ID 查询、列表查询、修改状态、删除。业务不复杂但代码写法上刻意做了几个约束——实体和数据库表映射用 Fluent API 集中在 DbContext 里配置、接口层出入参走 DTO、控制器内不直接写查询逻辑。这三个约束分别对应数据层、接口层和控制器的规范化。3.1 EF Core 上下文与实体映射建表、软删除和枚举EF Core 的惯例是实体类名对应表名、属性名对应列名但业务规则复杂时惯例不够用。示例在 DbContext 里用 Fluent API 统一配置而不是在实体类上堆 Attribute好处是所有表映射规则集中在一个文件里评审代码时一眼能看全。public class AppDbContext : DbContext { public AppDbContext(DbContextOptionsAppDbContext options) : base(options) { } public DbSetOrder Orders SetOrder(); public DbSetOrderItem OrderItems SetOrderItem(); protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.EntityOrder(entity { entity.HasKey(o o.Id); entity.Property(o o.OrderNo).HasMaxLength(32).IsRequired(); entity.Property(o o.Status).HasConversionstring().HasMaxLength(20); entity.HasQueryFilter(o !o.IsDeleted); }); modelBuilder.EntityOrderItem() .HasOne(oi oi.Order) .WithMany(o o.Items) .HasForeignKey(oi oi.OrderId); } }HasQueryFilter 是全局查询过滤器o !o.IsDeleted这个表达式会自动附加到所有针对 Order 的 LINQ 查询上实现逻辑删除。这样不需要在每个查询里手动加Where(o !o.IsDeleted)也不会漏。IsDeleted 字段在实体上用[NotMapped]之外的常规属性即可删除操作只更新这个字段不执行 DELETE 语句。Status 属性用 HasConversion 转成字符串存储数据库里是 Pending、Shipped 这样的可读文本而不是 0、1、2 这种魔法数字。排查线上数据时直接看表就能明白订单状态不需要再翻代码。索引和外键约束也建议在 OnModelCreating 里配比如 OrderNo 需要建唯一索引entity.HasIndex(o o.OrderNo).IsUnique();这样数据库层面防止重复订单号比在应用层判断更可靠。EF Core 的迁移命令dotnet ef migrations add Init会根据这些配置生成迁移脚本首次初始化时执行即可。3.2 DTO 与映射接口层不该暴露实体直接返回实体对象在小型项目里看着省事但实体被序列化成 JSON 时导航属性会把整个关联数据带出来使用者不需要的字段全暴露了甚至可能产生循环引用导致序列化异常。示例里把新增订单的请求单独抽成 CreateOrderDto响应单独抽成 OrderResponseDto中间用映射处理。public record CreateOrderDto(string CustomerName, ListCreateOrderItemDto Items); public record CreateOrderItemDto(string ProductName, int Quantity, decimal UnitPrice);public class OrderProfile : Profile { public OrderProfile() { CreateMapCreateOrderDto, Order() .ForMember(dest dest.OrderNo, opt opt.Ignore()); CreateMapCreateOrderItemDto, OrderItem(); CreateMapOrder, OrderResponseDto(); } }ForMember 配置了 OrderNo 忽略赋值因为订单号是服务层生成的业务编号不能由前端传进来。如果不写 IgnoreAutoMapper 会把 CreateOrderDto 里的 OrderNo 属性如果存在直接映射到实体上前端传什么订单号就存什么这是个安全隐患。AutoMapper 的 Profile 类在启动时注册builder.Services.AddAutoMapper(typeof(Program));然后在控制器构造函数里注入IMapper在 Service 层把实体映射成响应 DTO。这套写法的收益要等到接口字段调整时才明显——数据库加字段、实体加属性只要不涉及 DTO 变更接口契约就不变前端不需要同步修改。3.3 控制器动作与状态码201、404 和 NoContent控制器的每个动作只做三件事接收参数、调用服务、构造响应。示例里的写法值得直接抄[ApiController] [Route(api/[controller])] public class OrdersController : ControllerBase { private readonly IOrderService _orderService; public OrdersController(IOrderService orderService) { _orderService orderService; } [HttpPost] [ProducesResponseType(StatusCodes.Status201Created)] public async TaskActionResultOrderResponseDto Create(CreateOrderDto dto, CancellationToken ct) { var order await _orderService.CreateAsync(dto, ct); return CreatedAtAction(nameof(GetById), new { id order.Id }, order); } [HttpGet({id:guid})] [ProducesResponseType(StatusCodes.Status404NotFound)] public async TaskActionResultOrderResponseDto GetById(Guid id, CancellationToken ct) { var order await _orderService.GetByIdAsync(id, ct); if (order is null) return NotFound(); return Ok(order); } [HttpDelete({id:guid})] [ProducesResponseType(StatusCodes.Status204NoContent)] public async TaskIActionResult Delete(Guid id, CancellationToken ct) { await _orderService.DeleteAsync(id, ct); return NoContent(); } }创建成功返回 201 而不是 200这是 REST 接口一个常见细节。CreatedAtAction 会在响应头里带上 Location指向新资源的访问地址前端拿到 201 后可以通过 Location 直接查询详情。GetById 查不到时返回 404 而不是 200 null这能保证客户端逻辑不会判空出错。Delete 成功后返回 204 NoContent表示操作成功但没有响应体。接口方法的返回值建议用ActionResultT这样既能返回具体类型也能返回 NotFound 这类非泛型结果。CancellationToken 参数从请求进来开始贯穿到数据查询客户端断开连接时 EF Core 会取消正在执行的查询避免无效数据库操作占用连接。还有个实践是控制器方法参数上直接标路由约束{id:guid}格式不对的请求会在路由匹配阶段被拒绝返回 404而不是进到方法里再判断。4. JWT 认证与授权Token 生成、中间件顺序与策略边界没有认证的 Web API 只能用在纯内部调试环境。这套示例在 JWT 认证上做了完整闭环登录接口签发 Token受保护接口校验 Token管理员接口做授权策略限制。需要注意的不只是怎么写 Token 服务还有中间件注册顺序和策略设计边界。4.1 生成 TokenClaims、签名和过期时间JWT 的结构是 Header.Payload.Signature签名部分保证 Token 内容没有被篡改。示例用对称密钥 HS256 算法同一把密钥既用来签发也用来验证实现最简单单服务部署场景完全够用。public class TokenService { private readonly IConfiguration _config; public TokenService(IConfiguration config) { _config config; } public string CreateToken(string userId, string role) { var key new SymmetricSecurityKey( Encoding.UTF8.GetBytes(_config[Jwt:Key])); var credentials new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var claims new ListClaim { new Claim(JwtRegisteredClaimNames.Sub, userId), new Claim(ClaimTypes.Role, role), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) }; var token new JwtSecurityToken( issuer: _config[Jwt:Issuer], audience: _config[Jwt:Audience], claims: claims, expires: DateTime.UtcNow.AddMinutes(Convert.ToInt32(_config[Jwt:ExpiresMinutes])), signingCredentials: credentials); return new JwtSecurityTokenHandler().WriteToken(token); } }Claim 里放什么要克制。Sub 存用户 IDRole 存角色Jti 是 Token 的唯一标识用于刷新和注销场景。不要放密码、手机号这类敏感信息因为 JWT 的 Payload 默认只做 Base64 编码不是加密的能解码就能看到内容。密钥长度有硬性要求HS256 要求至少 32 字节长度不够时运行时直接抛异常。过期时间建议设置短一些比如 120 分钟然后配合刷新 Token 机制。示例里的过期时间取的是配置项方便在不同环境调整。这里有个小知识点DateTime.UtcNow.AddMinutes(...)用的是 UTC 时间而不是本地时间避免服务器时区不一致导致 Token 提前过期或延迟过期。登录接口里需要配合用户验证逻辑比如校验用户名密码校验通过才调用 CreateToken。密码校验推荐PasswordHasherTUser不要自己写哈希。4.2 中间件管道顺序认证在授权之前CORS 在认证之前Program.cs 里中间件的注册顺序就是请求处理顺序这一层顺序错了后面所有功能都白搭。示例把完整的管道结构写清楚了var app builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseCors(CustomPolicy); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();UseAuthentication 必须在 UseAuthorization 之前因为授权中间件要读取认证中间件填充的用户信息才能判断权限。顺序反了的表现是登录后拿着 Token 请求接口返回 401 而不是 403因为授权中间件执行时用户身份还没被识别。UseCors 放在了 UseAuthentication 之前这个顺序反映了一个关键细节浏览器的 CORS 预检请求OPTIONS不会携带 Authorization 头。如果 CORS 中间件在认证中间件之后执行预检请求会在认证环节被拦截前端报“CORS 错误”但实际是认证问题。先执行 CORS预检请求就能直接通过真正携带 Token 的请求再由认证中间件处理。UseSwagger 在生产环境默认不启用这层逻辑在 Program.cs 里体现为环境判断。如果内网联调需要生产环境的 Swagger可以用配置项控制而不是直接改成app.UseSwagger()后面避坑章会展开。4.3 授权策略角色、权限与自定义 Policy认证只管“你是谁”授权管“你能不能做这件事”。示例里的授权策略不局限于简单的角色判断而是把角色和操作权限组合在一起builder.Services.AddAuthorization(options { options.AddPolicy(AdminOnly, policy policy.RequireRole(Admin) .RequireClaim(Permission, ManageOrders)); });控制器上使用[Authorize(Policy AdminOnly)] [HttpDelete({id:guid})] public async TaskIActionResult Delete(Guid id, CancellationToken ct) { await _orderService.DeleteAsync(id, ct); return NoContent(); }RequireRole 和 RequireClaim 组合的含义是用户必须同时具备 Admin 角色和 ManageOrders 权限声明才能执行删除操作。单纯用角色控制粒度太粗比如“运营可以改价格但不能删订单”只靠角色就需要拆出多个角色。加一层权限声明后角色定义和操作权限解耦新增一个操作只需要调整策略不用修改用户角色。如果策略要求超出内置 API 的能力——比如“只有订单创建者本人可以修改订单”——就需要自定义 AuthorizationHandler。示例没有扩展到这么深但边界已经划定当策略是多个条件的 AND 组合时用内置 API 拼装当策略逻辑涉及数据查询或复杂状态判断时才需要实现IAuthorizationRequirement和AuthorizationHandlerT。注意JWT 里放的 Claims 不会自动同步到用户权限修改策略后必须重新签发 Token否则新策略不会生效。5. 部署与测试中的五个坑环境变量、容器网络与同步等待示例代码在本地跑通只是一半真正的问题都出在部署和联调阶段。以下五条是我在接手类似项目时踩过的真实坑每一条都按现象、原因、解决三步拆开你可以对照检查自己的部署配置。5.1 坑一连接字符串被环境变量覆盖了还是连不上现象本地连接数据库正常部署到服务器后接口一直报 SQL Server 连接超时或登录失败。原因服务器上设置了环境变量ConnectionStrings__DefaultConnection但值里的数据库地址写的是内网 IP服务器到那个 IP 的网络不通。或者环境变量名和配置层级拼写不一致导致覆盖不生效。最常见的是把ConnectionStrings__DefaultConnection写成了ConnectionStrings_DefaultConnection单下划线不会被识别为层级分隔符。解决先查实际解析出来的连接字符串而不是猜。# 在服务器上临时输出配置确认实际值 dotnet run --project SampleApi --urls http://0.0.0.0:5000 curl http://localhost:5000/api/debug/config或者直接在 Program.cs 启动时写一行临时日志var connStr builder.Configuration.GetConnectionString(DefaultConnection); app.Logger.LogWarning(ConnStr: {ConnStr}, connStr);看到实际解析出的值再排查网络和账号问题。环境变量覆盖规则是必须用双下划线分隔层级大小写不敏感Linux 和 Windows 行为一致。5.2 坑二容器里访问不到 SQL Server现象接口在 Docker 容器里启动正常但一请求数据库就超时。在宿主机上直接运行同一个连接字符串却能连上。原因容器内的 localhost 是容器自己不是宿主机。连接字符串写的Serverlocalhost在容器里指向了容器自身当然连不上宿主机的 SQL Server。解决根据运行环境区分三种写法并用 docker-compose 固定容器网络。services: api: image: sample-api:dev environment: - ConnectionStrings__DefaultConnectionServerdatabase;DatabaseSampleDb;User Idsa;PasswordPssw0rd;TrustServerCertificateTrue database: image: mssql-server:latest environment: - ACCEPT_EULAY - MSSQL_SA_PASSWORDPssw0rddocker-compose 会在同一个默认网络中创建服务Serverdatabase这里的 database 是 compose 里定义的 service 名DNS 会自动解析到数据库容器。本机直接运行时Windows 和 Mac 的 Docker 桌面支持Serverhost.docker.internal访问宿主机Linux 上需要额外加--add-hosthost.docker.internal:host-gateway。把连接字符串放在 compose 的环境变量里而不是镜像里换环境不用重新构建镜像这也是这套示例用环境变量覆盖配置的一个原因。5.3 坑三Swagger 在生产环境 404现象开发环境访问 /swagger 正常部署到测试服务器后一直 404页面打不开。原因Program.cs 里把 UseSwagger 和 UseSwaggerUI 包在了if (app.Environment.IsDevelopment())块里。生产环境默认不是 Development中间件根本没有注册所以 404 非常正常。解决如果是内网联调环境用配置项控制 Swagger 的开关不要直接用环境判断。if (app.Environment.IsDevelopment() || app.Configuration.GetValuebool(Swagger:Enabled)) { app.UseSwagger(); app.UseSwaggerUI(); }appsettings.json 增加Swagger: { Enabled: true }生产环境把Swagger:Enabled设为 false 即可。另一个常见错误是把UseSwaggerUI()写到了UseSwagger()前面中间件按顺序执行时先注册的 UseSwaggerUI 找不到 Swagger 文档生成器也会导致访问异常。顺序永远是先 UseSwagger 再 UseSwaggerUI。5.4 坑四CORS 配置看起来正确前端还是报跨域现象前端请求能发出但浏览器控制台提示Access-Control-Allow-Origin缺失或者请求在预检阶段直接失败。原因两种情况。一种是策略里同时调用了AllowAnyOrigin()和AllowCredentials()这两者在浏览器规范里是互斥的运行时直接抛异常。另一种是 CORS 中间件位置错误OPTIONS 预检请求被后面的认证中间件拦截浏览器收不到正确的 CORS 响应头。解决具体源用 WithOrigins不要用 AllowAnyOrigin。builder.Services.AddCors(options { options.AddPolicy(CustomPolicy, policy policy.WithOrigins(https://frontend.example.com) .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials()); });配合前文提到的管道顺序UseCors 一定要在 UseAuthentication 之前。如果前端在开发环境跑的端口是 5173源要精确到端口WithOrigins(http://localhost:5173)不要省略端口。写AllowAnyOrigin的教训是浏览器规范不准同时带上 Cookie 凭证需求一要求带凭证就得改回 WithOrigins。5.5 坑五异步接口用同步等待高并发后请求排起长队现象压测时接口响应时间从 50 毫秒逐渐涨到几秒CPU 占用和线程数持续走高QPS 上不去。原因控制器或服务里用了.Result或.Wait()同步等待异步方法。ASP.NET Core 的处理线程池是有限的同步阻塞会把线程占住不放并发一高线程池就饿死了。解决异步一路到底不在任何链路里同步阻塞。// 错误写法同步等待阻塞线程 var orders _orderService.GetAllAsync().Result; // 正确写法异步等待 var orders await _orderService.GetAllAsync();排查这类问题最简单的方法是把代码里所有.Result、.Wait()、.GetAwaiter().GetResult()搜出来逐个改掉。另外提一个最容易忽略的场景构造函数里不能 await有些人会把异步初始化改成.Result塞进构造函数这在高并发下就是灾难。构造函数只做赋值异步初始化放到一个显式的 InitializeAsync 方法里。注意async void 只用于事件处理器用在控制器动作里异常会直接抛出到线程池导致进程崩溃。6. 用一条命令验证整套示例OpenAPI 客户端与健康检查组合示例跑起来之后别急着写业务代码先做两件事配健康检查、把登录请求和受保护接口请求串成一条命令。这两步能确认数据库连接、JWT 签发、认证授权整条链路是通的比在 Swagger UI 里手动点几十次高效得多。先在 Program.cs 注册健康检查示例用的是 EF Core 的数据库连通性检查builder.Services.AddHealthChecks() .AddDbContextCheckAppDbContext(Database);然后在管道末尾映射健康检查端点app.MapHealthChecks(/health, new HealthCheckOptions { ResponseWriter async (context, report) { context.Response.ContentType application/json; var result JsonSerializer.Serialize(new { status report.Status.ToString(), checks report.Entries.Select(e new { name e.Key, status e.Value.Status.ToString() }) }); await context.Response.WriteAsync(result); } });启动后第一件事验证基础链路curl -s http://localhost:5000/health | jq看到checks里数据库状态是 Healthy说明连接字符串和网络层没问题。接下来把完整的认证流程串起来# 1. 登录获取 Token TOKEN$(curl -s -X POST http://localhost:5000/api/auth/login \ -H Content-Type: application/json \ -d {userName:admin,password:123456} | jq -r .token) # 2. 携带 Token 访问受保护接口 curl -s http://localhost:5000/api/orders \ -H Authorization: Bearer $TOKEN | jq第一条命令如果返回的不是 200先看登录接口的入参格式和默认账号是否与示例代码一致。第二条命令如果返回 401检查 Token 里是否包含正确的 Claims以及 Authorization 头格式是不是Bearer加一个空格再加 Token 字符串。这套命令跑通之后Swagger UI 里也能顺手验证——点 Authorize 按钮把 Token 粘进去后续请求自动带上 Authorization 头。示例代码里的 OpenAPI 描述还支持直接生成客户端但我不建议在项目初期引入代码生成器因为接口还没稳定生成的客户端代码改动成本很高。先用 curl 验证完核心链路再交给前端对接。从那以后我每次搭建接口项目都强制自己走一遍这个流程先配健康检查再写一条 curl 串起登录和业务请求确认链路通了才写业务逻辑。这个习惯帮我避开了好几次“代码看着没问题联调时全断”的尴尬局面。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

C#生产级Socket封装:解决粘包拆包、心跳保活与自动重连

C#生产级Socket封装:解决粘包拆包、心跳保活与自动重连

简介:这是一份面向C#初学者与.NET开发者的Socket通信实战封装组件,聚焦TCP协议下的稳定、可复用网络通信能力构建。资源已将Socket连接、数据收发、异常处理及连接管理等核心逻辑抽象为通用类(如SocketHelper.cs),支持…

2026/10/19 2:19:36 阅读更多 →
HDClone Free Edition整盘克隆实操:扇区复制与文件复制怎么选

HDClone Free Edition整盘克隆实操:扇区复制与文件复制怎么选

简介:HDClone Free Edition v5.1.4 是一款面向个人用户的硬盘克隆与数据迁移工具,可将源磁盘完整复制到另一块更大的硬盘上,适用于系统升级、数据备份与基础救援场景,能减少重新安装系统和应用的麻烦。资源为 zip 压缩包&#xff…

2026/10/19 2:19:36 阅读更多 →
Cocos Creator打包微信小游戏:从构建配置到二次开发实战

Cocos Creator打包微信小游戏:从构建配置到二次开发实战

简介:这是一份 Cocos Creator 打包微信小游戏的完整源代码资源,适合已掌握 Cocos Creator 基础、正准备将自己的 2D 游戏发布到微信小游戏平台的开发者。压缩包共 592 个文件,约 1.67MB,包含 289 个 json(场景、配置与…

2026/10/19 2:19:36 阅读更多 →

最新新闻

2019年CSP-S初赛选择题1-5题精讲:进制、位运算、栈、二叉树与KMP陷阱全解析

2019年CSP-S初赛选择题1-5题精讲:进制、位运算、栈、二叉树与KMP陷阱全解析

2019年的CSP-S初赛,是我第一次以“CSP”而不是“NOIP”的名义走进考场。说实话,当时心里多少有点没底——毕竟是新名字的第一年,谁也不知道命题风格会不会大改。等我翻到选择题第一页,看到前5题的时候反而松了口气:进制…

2026/10/19 3:15:05 阅读更多 →
腾讯云文本审核工具类封装实践:从SDK调用到一行接入

腾讯云文本审核工具类封装实践:从SDK调用到一行接入

做后端这几年,涉及用户输入的场景就没断过——评论、弹幕、私信、AI对话、论坛发帖,几乎每个能提交文字的地方都得过一道内容安全检查。一开始我也图省事,想着审核就审核,直接在业务代码里new一个请求对象,调一下腾讯云…

2026/10/19 3:15:05 阅读更多 →
神龙汽车新春守护直播:车企节点型用户关系运营如何落地

神龙汽车新春守护直播:车企节点型用户关系运营如何落地

看到“知音相伴 一路同行|神龙汽车‘新春守护暖心发布’专场直播即将温情启幕”这个标题时,我第一反应是:这不只是一场普通的车企直播预告,而是一个典型的“节点型用户关系运营”样本。作为一个常年做汽车品牌营销和直播策划的人&…

2026/10/19 3:15:05 阅读更多 →
JSON-RPC 2.0实战:Python实现、TCP/HTTP接入与避坑指南

JSON-RPC 2.0实战:Python实现、TCP/HTTP接入与避坑指南

简介:jsonRPC.rar是一份面向Java Web开发者的JSON-RPC入门案例资源包,集中演示了轻量级远程调用协议的基本原理与实现方式,解决分布式服务通信及前后端API交互场景中的实际搭建问题。资源共35个文件,包含25个jar依赖库&#xff08…

2026/10/19 3:15:05 阅读更多 →
TTHHO优化RBF神经网络参数:分类预测准确率突破91%的实战解析

TTHHO优化RBF神经网络参数:分类预测准确率突破91%的实战解析

如果你手头有一个分类任务,先试了试直接训练径向基函数网络(RBF),结果测试集准确率卡在八成出头再也上不去,你大概率会怀疑:是不是数据没处理好?特征选择有问题?我告诉你&#xff0c…

2026/10/19 3:15:04 阅读更多 →
Flink有界流与无界流:核心区别、运行时行为差异与选型避坑指南

Flink有界流与无界流:核心区别、运行时行为差异与选型避坑指南

1. 从一个反直觉的现象说起:为什么批处理跑得好好的,换成流处理就出问题刚接触 Flink 的人,十有八九会在某个时刻被一个看似简单的问题绊住:我明明写的是同一套算子逻辑,为什么换成流模式跑,结果要么迟迟不…

2026/10/19 3:14:03 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →