拓冰建站拓冰建站
首页 / 资讯中心 / 正文

.NET6 WebAPI + SQL Server + JWT:登录认证与增删改查实战详解

简介面向.NET开发者的这份示例项目围绕.NET6 Web API与SQL Server数据库的增删改查操作并结合JWT完成身份认证与权限控制。资源适合希望掌握ASP.NET Core API设计、Swagger接口文档、数据库迁移及ORM使用的中初级开发者可作为从零搭建安全Web服务的完整参考。压缩包共665个文件约34.81MB以dll、cs、pdb、json、csproj等为主涵盖编译产物、源代码、配置与工程文件清晰呈现项目结构和依赖关系。已有1059人学习下载。通过该资源可了解通过SqlSugar或Dapper等ORM访问SQL Server的实现方式并看到JWT登录验证、全局异常处理、RESTful接口规划的工程化写法同时可学习Entity Framework Core迁移命令管理数据库变更项目针对API安全也给出防SQL注入与HTTPS等建议。整体上是一份功能完整、适合动手实践和二次开发的.NET6 Web API练手与参考资料。 最近有个实际项目需要上一套支持登录认证的后端服务我选了.NET6 WebAPI SQL Server JWT这套组合把用户登录、Token鉴权和一套完整的增删改查接口全部跑通顺带还解决了前端Vue对接时的文件下载、中文文件名保持、Token续签这些细节问题。这篇文章就把整个落地过程从头到尾梳理一遍包含方案选型、核心代码、数据库设计和踩坑记录希望能给正在做同类项目的朋友省点时间。老规矩先说结论这套方案足够应对中小型业务系统JWT做无状态认证非常灵活SQL Server配合EF Core开发效率极高但前提是你得先搞清楚几个关键坑——比如JWT密钥怎么配、SQL Server连接串在不同版本下的行为差异、文件下载时文件名怎么编码才不会乱码。下面我按实际开发顺序来拆解。1. 项目整体设计与方案选型1.1 为什么选择.NET6 WebAPI SQL Server JWT选这套技术栈不是偶然。.NET6是LTS版本微软官方支持到2024年11月之后还有. NET 8继续接力对于企业级项目来说生命周期有保障。WebAPI模式天然适合前后端分离我们前端用Vue后端只需要暴露一组RESTful接口就行不需要关心页面渲染逻辑两边独立部署、独立迭代团队协作边界清晰。SQL Server做数据库在这个场景下非常合适。Windows环境下部署成本低SSMS图形化管理工具成熟开发人员的上手门槛低。用EF Core做ORM更是省事它能把数据库表结构映射成C#实体类增删改查操作变成LINQ表达式代码写起来直观迁移功能也方便后续改表结构。JWT解决的是身份认证问题。用户登录成功后服务端签发一个加密签名的Token返回给前端前端在后续请求的Header里带上“Authorization: Bearer {token}”服务端验证签名和有效期即可识别用户身份。整个过程服务器不需要存储会话状态适合水平扩展部署多台服务器也不用考虑Session同步问题。这套组合整下来开发效率高部署简单安全机制清晰在线业务系统和内部管理系统都够用。1.2 ORM选型EF Core好还是Dapper好很多人纠结对象关系映射用EF Core还是Dapper我的看法是分场景。追求极致性能、SQL语句想完全自己掌控Dapper更合适它本身就是一个轻量级的SQL执行器封装少、性能接近原生ADO.NET。但如果项目里有大量复杂业务逻辑需要频繁操作多个实体、跟踪状态、级联更新EF Core省下来的代码量非常可观。这次项目我选了EF Core主要原因是表结构不算特别复杂而且CRUD操作占了绝大多数EF的自动跟踪和延迟加载能直接提升编码速度。如果你后续要写复杂报表或者存储过程记得用FromSqlRawEF Core 7以后是SqlQuery方法照样可以执行原生SQL灵活性并不会被锁死。补充一句EF Core的经典陷阱就是N1查询问题也就是循环里查子表导致数据库请求爆炸。解决办法是使用Include方法预先加载导航属性或者干脆在查询时用Select直接投影出需要的字段避免触碰到未加载的关联数据。1.3 项目结构怎么划分最清爽我习惯把项目拆成几层哪怕只是一个小型Demo也保持清晰的边界不然等业务规模上来再重构就很痛苦。最外层是WebAPI项目只负责接收HTTP请求、返回JSON响应里面写Controller和DTO。业务逻辑层Services处理核心业务规则比如登录校验、Token生成、成绩计算这些。数据访问层Repositories或DbContext负责和数据库打交道封装增删改查操作。实体类Entities和数据库表一一对应放在独立的Domain/Models目录里。这样做的好处是Controller层很薄逻辑都在Service里接口也好测试将来换数据库或者加缓存都比较方便。项目规模不大时不用强行引入多个类库项目在同一个项目里按文件夹分好层就够了重点是保证依赖方向Controller依赖ServiceService依赖仓储层和DbContext别反向引用。2. 环境准备与项目骨架搭建2.1 SQL Server安装与SSMS工具准备如果你在全新Windows环境中装SQL Server建议直接上SQL Server 2022 Developer版功能跟Enterprise版一致只限制生产环境使用许可开发学习完全免费。安装包大概2GB多下载后一路下一步就行提醒几个关键点实例配置可以选择默认实例MSSQLSERVER连接串会短一点如果有多个环境并存再考虑命名实例。身份验证模式建议选“混合模式”因为开发环境经常需要SQL账号登录记得设好sa密码。把“添加当前用户”勾上给自己Windows管理员权限省得后面权限问题。装完之后马上装SSMSSql Server Management Studio这是官方图形化管理工具。现在SSMS是独立安装的不像以前集成在安装包内。安装好后连接服务器右键数据库可以新建数据库也可以直接附加已有MDF文件日常看数据、跑查询、看执行计划全靠它。这里有个我踩过的坑如果安装完成但SSMS连不上提示“provider: SQL Network Interfaces, error 26 – 定位指定的服务器/实例时出错”90%的情况是SQL Server服务没启动。打开“SQL Server配置管理器”找到“SQL Server服务”节点确认实例处于“正在运行”状态。SQL Server配置管理器在Windows搜索框里直接输入就能找到不用去控制面板翻。2.2 使用VS2022创建.NET6 WebAPI项目VS2022创建WebAPI项目的流程非常简单打开Visual Studio选择“创建新项目”搜索“ASP.NET Core Web API”选择C#版本Framework选.NET 6.0长按支持。创建时勾选“使用控制器”也行不勾选出来就是最小API风格我个人更愿意勾选因为自带Controller模板方便加接口。项目创建后先清理一下模板自带的WeatherForecast示例代码然后安装必须的NuGet包Microsoft.EntityFrameworkCore.SqlServerEF Core的SQL Server驱动。Microsoft.EntityFrameworkCore.Tools提供迁移命令Add-Migration、Update-Database等。Microsoft.AspNetCore.Authentication.JwtBearerJWT认证中间件。Swashbuckle.AspNetCoreSwagger接口文档工具模板通常自带。安装包的版本最好和.NET版本匹配比如.NET6项目建议用EF Core 6.0.x.NET8项目用8.0.x别随便拉最新版。跨大版本用EF Core经常遇到运行时警告或兼容性问题没必要冒险。2.3 连接字符串配置与数据库初始化在appsettings.json里配置连接字符串下面是标准写法{ ConnectionStrings: { DefaultConnection: Server.;DatabaseStudentDb;User Idsa;Passwordyour_password;TrustServerCertificateTrue; }, Jwt: { Key: your-256-bit-secret-key-at-least-32-chars, Issuer: YourApp, Audience: YourAppClient, ExpireMinutes: 120 } }几个关键点要说明第一Server.表示本机默认实例也可以写localhost或127.0.0.1效果一样。如果用命名实例写法是Serverlocalhost\SQLEXPRESS这种格式。第二TrustServerCertificateTrue必须加上这是新版驱动的要求。.NET 6及之后的SqlClient驱动默认对证书校验更严格不加这个会报“证书链是由不受信任的颁发机构颁发的”开发环境直接加True跳过校验即可。第三账号密码千万别用sa加弱口令这只是为了演示。正式项目建议建一个最小权限的专用账号只给某个数据库授予db_datareader和db_datawriter权限。数据库初始化我用EF Core迁移来搞。先在Package Manager Console里执行Add-Migration InitDb Update-DatabaseAdd-Migration会在项目里生成一个Migrations文件夹里面包含建表和索引的代码。Update-Database实际上执行SQL让数据库结构跟上代码模型。以后模型有变化再Add-Migration加一个名字再Update-Database就行比手工写SQL建表方便太多。3. 数据建模与增删改查核心实现3.1 实体类设计从学生成绩表说起增删改查得有具体业务载体这次我设计了一个学生成绩管理场景两个核心实体学生和成绩。数据库表字段的命名可以直接在C#里用对应属性来定义EF Core默认会把属性名映射成表字段名。如果你用SQL Server建表时习惯snake_case命名可以在实体上用[Column(student_name)]来指定但我个人建议项目统一用PascalCase省得来回映射。学生实体和成绩实体大致如下public class Student { public int Id { get; set; } public string Name { get; set; } string.Empty; public int Age { get; set; } public string? ClassName { get; set; } public DateTime CreateTime { get; set; } DateTime.Now; } public class Score { public int Id { get; set; } public int StudentId { get; set; } public string Subject { get; set; } string.Empty; public decimal ScoreValue { get; set; } public DateTime ExamDate { get; set; } }这里注意几个细节Age用intScoreValue用decimal而不是double因为浮点数在SQL Server里对应float精度会有误差做统计的时候容易出问题。ClassName用了可空字符串因为允许同学还没分班。CreateTime给默认值DateTime.Now减少插入时的手工赋值。Score实体里的StudentId就是外键EF Core可以通过导航属性自动关联如果不需要查询学生的时候带出成绩可以不写导航属性。我这个项目里Student实体上加了ICollection 方便联查。3.2 学生信息增删改查Controller Service实现Service负责核心逻辑Controller只做参数接收和结果返回。服务层暴露一组方法CreateStudent、UpdateStudent、DeleteStudent、GetStudentById、GetStudents。代码结构大致如下public class StudentService { private readonly AppDbContext _context; public StudentService(AppDbContext context) { _context context; } public async TaskStudent CreateStudent(StudentCreateDto dto) { var student new Student { Name dto.Name, Age dto.Age, ClassName dto.ClassName }; _context.Students.Add(student); await _context.SaveChangesAsync(); return student; } public async TaskStudent? GetStudentById(int id) { return await _context.Students .Include(s s.Scores) .FirstOrDefaultAsync(s s.Id id); } public async TaskListStudent GetStudents(string? keyword, int page, int pageSize) { var query _context.Students.AsNoTracking(); if (!string.IsNullOrEmpty(keyword)) { query query.Where(s s.Name.Contains(keyword) || (s.ClassName ! null s.ClassName.Contains(keyword))); } return await query .OrderByDescending(s s.CreateTime) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); } public async Taskbool UpdateStudent(int id, StudentUpdateDto dto) { var student await _context.Students.FindAsync(id); if (student null) return false; student.Name dto.Name; student.Age dto.Age; student.ClassName dto.ClassName; await _context.SaveChangesAsync(); return true; } public async Taskbool DeleteStudent(int id) { var student await _context.Students.FindAsync(id); if (student null) return false; _context.Students.Remove(student); await _context.SaveChangesAsync(); return true; } }这里面有几个重要操作细节查询分页时用AsNoTracking()告诉EF Core不用跟踪实体状态查询性能会提升不少因为EF不用额外维护状态快照。GetStudents方法里那个包含判断是用了SQL Server的Contains翻译出来就是LIKE %keyword%纯字符串包含判断挺好用。Update操作我用的FindAsync先查出实体再修改属性最后SaveChanges。EF Core的变更跟踪会自动生成UPDATE语句只更新有变化的字段。如果你想更简洁也可以定义一个新的实体实例设置Id然后Attach 修改属性状态但那样容易漏字段我宁可用先查后改的方式代码量大一点但不容易出错。删除操作要注意级联问题。Student有Scores导航属性SQL Server里Score表有外键约束直接删除学生记录会违反外键约束。解决方式有两种一种是在OnModelCreating里配置CascadeDelete另一种是删除学生前先手动删成绩。前者省事但要说清楚业务语义我这次因为业务需求是成绩独立保留所以在删除学生时先查一次成绩列表存在就一并删除逻辑更明确。3.3 数据库增删改查的EF Core原理解读EF Core的增删改查本质上是把LINQ表达式转换成SQL语句比如你写var students await _context.Students .Where(s s.Age 18) .ToListAsync();对应的SQL长这样SELECT [s].[Id], [s].[Name], [s].[Age], [s].[ClassName], [s].[CreateTime] FROM [Students] AS [s] WHERE [s].[Age] 18这里不用写SQL字符串但有两点必须搞清楚第一IQueryable是惰性查询只有调用ToListAsync、FirstOrDefaultAsync等最终执行方法时SQL才会真正发到数据库。所以你可以先用Where组装条件再决定是否ToList条件组装过程并不耗数据库资源。第二聚合操作建议在数据库端完成比如统计学生人数直接CountAsync不要先ToList再内存里Count这样SQL Server只返回一个数字而不是整表数据性能差异巨大。如果某些复杂查询必须手写SQL比如多层关联统计可以用var list await _context.Database .SqlQueryRawStudentDto(SELECT s.Id, s.Name, ... FROM Students s JOIN ...) .ToListAsync();注意返回类型必须是实体或DTO且有对应属性映射这个方式是原生SQL和ORM并存的稳妥路径。4. JWT登录认证与Token全过程落地4.1 用户表设计与密码哈希JWT认证前得先有用户数据。我建了一张Users表核心字段是Id、UserName、PasswordHash、DisplayName、Role、CreateTime。PasswordHash千万不能存明文而是存SHA256加盐后的哈希值或者直接上BCrypt。这次我用的是BCrypt.Net-Next这个NuGet包注册时可以这样var user new User { UserName dto.UserName, PasswordHash BCrypt.Net.BCrypt.HashPassword(dto.Password), DisplayName dto.DisplayName, Role User }; _context.Users.Add(user); await _context.SaveChangesAsync();BCrypt的HashPassword会自动生成随机盐并混入哈希结果每次哈希值都不一样但验证时用Verify方法能正确比对安全性远高于简单的MD5加盐拼接。登录验证代码var user await _context.Users.FirstOrDefaultAsync(u u.UserName dto.UserName); if (user null || !BCrypt.Net.BCrypt.Verify(dto.Password, user.PasswordHash)) { return null; // 登录失败 }这里有个细节值得注意为了做哈希比对你可以顺手做一个计时攻击的防护——用户不存在时也执行一次Verify让处理时间恒定。不过对于绝大多数内部系统这个优化不是必须的知道就行。4.2 Token生成参数与JWT在线解析调试登录成功后生成JWT。我封了一个方法public string GenerateToken(User user) { var claims new ListClaim { new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()), new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName), new Claim(ClaimTypes.Name, user.DisplayName), new Claim(ClaimTypes.Role, user.Role) }; var key new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtConfig.Key)); var creds new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token new JwtSecurityToken( issuer: _jwtConfig.Issuer, audience: _jwtConfig.Audience, claims: claims, expires: DateTime.Now.AddMinutes(_jwtConfig.ExpireMinutes), signingCredentials: creds ); return new JwtSecurityTokenHandler().WriteToken(token); }JWT密钥的长度有硬性要求至少32字节也就是32个字符以上因为HS256算法要求密钥长度不小于256位。你如果设置一个只有6位的密钥运行时会直接报错这是很多新手容易忽略的问题。调试Token最方便的工具是jwt.io这个在线解析网站但注意不要把真实生产环境的Token扔上去因为Token里虽然不包含密钥签名部分毕竟是敏感信息。本地调试时可以把生成好的Token贴进去解析出Header和Payload检查过期时间和角色声明是否正确。用JWT在线解析代码调试能帮你肉眼确认Header里的alg是否为HS256exp时间戳是否和预期一致Role对不对。4.3 认证中间件接入与接口鉴权在Program.cs里注册认证和授权服务var jwtSection builder.Configuration.GetSection(Jwt); var key Encoding.UTF8.GetBytes(jwtSection[Key]); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options { options.TokenValidationParameters new TokenValidationParameters { ValidateIssuer true, ValidIssuer jwtSection[Issuer], ValidateAudience true, ValidAudience jwtSection[Audience], ValidateLifetime true, IssuerSigningKey new SymmetricSecurityKey(key), ClockSkew TimeSpan.FromMinutes(1) }; }); builder.Services.AddAuthorization(); var app builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers();然后Controller或Action上面加上[Authorize]特性这个接口就受保护了不带有效Token访问会返回401。如果想限制角色比如只有管理员能删除学生就用[Authorize(Roles Admin)]。这里有个中间件顺序的坑必须把app.UseAuthentication()放在app.UseAuthorization()之前认证先于授权执行否则授权检查时拿不到用户身份信息所有请求都会401。中间件顺序在ASP.NET Core里面非常重要顺序错了表面看代码没报错实际效果完全不对。4.4 Token续签方案与Vue前端配合JWT有效期到了之后怎么处理常见做法是前端弹窗让用户重新登录但体验太差。这次我用了刷新Token方案登录接口除了返回access_token还返回一个有效期更长的refresh_token。数据库里建RefreshToken表存UserId、Token、ExpireAt、CreateAt。前端每次在请求拦截器里判断access_token是否快过期如果剩余时间小于5分钟就调用后端续期接口换取新的access_token。后端续期接口的核心逻辑var refreshToken await _context.RefreshTokens .FirstOrDefaultAsync(r r.Token dto.RefreshToken r.ExpireAt DateTime.Now); if (refreshToken null) return Unauthorized(); var user await _context.Users.FindAsync(refreshToken.UserId); var newAccessToken GenerateToken(user); var newRefreshToken GenerateRefreshToken(user.Id); // 更新数据库里的refresh_token旧的下线refresh_token存数据库的目的就是服务端可控用户主动注销时可以立即作废对应记录。不要只依赖客户端丢弃Token那只能防君子不能防小人。Vue前端配合时通常用axios拦截器service.interceptors.request.use(config { const token localStorage.getItem(access_token); if (token) { config.headers.Authorization Bearer ${token}; } return config; });响应拦截器里捕获401错误再尝试用refresh_token换新Token换成功就重新发起原请求失败则跳转登录页。这套逻辑组合下来用户全程无感知续期。5. 常见问题与排查技巧实录5.1 SQL Server连接与常规问题速查开发过程中最容易卡住的就是数据库连接。我整理了这几次实践中遇到的高频问题做成表格方便对照排查现象常见原因解决办法连接失败报“证书链不受信任”新版驱动默认校验证书连接串加TrustServerCertificateTrue提示“定位指定的服务器/实例时出错”SQL Server服务未启动打开SQL Server配置管理器启动服务登录失败sa账号密码错误密码错误或登录模式没开启改成混合模式重置sa密码连接串写错实例名默认实例与命名实例混用Server.表示默认实例命名实例写Server.\SQLEXPRESS附加MDF文件报错文件权限不足或版本不兼容给文件所在目录加当前用户完全控制权限这里单独说一下注册表相关的问题。有时候SQL Server安装或卸载不干净注册表里残留Microsoft SQL Server键值可能导致密钥打不开、服务无法启动之类的问题。我的处理方式是不去手动改注册表最稳妥的方法是卸载后用官方工具清理或者退一步如果老实例已经不使用直接备份数据文件后重装一次SQL Server时间成本往往比排查注册表问题低得多。5.2 JWT相关的坑密钥、过期时间和角色JWT这块坑也不少我挑三个最常见的说一下第一个是密钥长度不够。前面提过HS256要求至少32字节你在appsettings.json里写“Key”时别用那种几个字符的简单串直接放一个32位以上的随机字符串比如F8A2D1E4B9C7A6F3E1D2C4B5A9F8E7D6C3B2A1F4E5D6C7B8A9F0E1D2C3B4。配置文件里看起来长但这是安全的底线。第二个是ClockSkew参数。JWTBearerHandler默认允许5分钟的时间偏移也就是说Token明明过期了服务器可能还会多放行5分钟。如果业务对安全要求严格在TokenValidationParameters里显式设置ClockSkew TimeSpan.Zero或者像我一样设1分钟不要让默认5分钟成为隐患。第三个是角色Claim的类型问题。.NET里字符串常量ClaimTypes.Role实际是一个URI比如“http://schemas.microsoft.com/ws/2008/06/identity/claims/role”而JWT标准里角色一般是“role”。如果不做映射代码里写[Authorize(Roles Admin)]可能无效因为中间件没找到对应的Claim。解决办法是在AddJwtBearer配置里加options.MapInboundClaims false;或者在生成Token时把ClaimTypes.Role统一换成JwtRegisteredClaimNames的对应格式。这个问题排查起来很隐蔽不是你代码逻辑错了而是Claim名称映射不一致。5.3 文件下载与中文文件名保持不变的实现项目里还涉及一个Vue前端下载附件的需求这里重点说下如何保持文件名不变。WebAPI做文件下载返回FileStreamResult时需要在响应头里设置Content-Disposition格式为attachment; filenamexxx。但这里面藏着一个编码坑如果文件名是中文直接放在filename里某些浏览器或请求库会乱码。我用的标准姿势是同时写两个字段var file await _fileService.GetFileAsync(id); var encodedFileName Uri.EscapeDataString(file.FileName); Response.Headers.Add(Content-Disposition, $attachment; filename\{encodedFileName}\; filename*UTF-8{encodedFileName});filename是给老浏览器用的filename是RFC 5987标准的写法支持UTF-8编码现代浏览器和axios会用filename。前端用blob接收文件时从响应头里拿文件名const disposition response.headers[content-disposition]; const filenameMatch disposition.match(/filename\*UTF-8([^;])/) || disposition.match(/filename?([^;])?/); const filename filenameMatch ? decodeURIComponent(filenameMatch[1]) : download;这样Vue前端下载的文件名就不会变成乱码。说实话在做文件下载这个功能之前我一直以为文件名乱码是前端的问题后来发现根源在服务端响应头设置不规范这个坑值得记一下。5.4 SQL Server字符串转数字与特殊查询场景最后补充一个实际开发里很常见的场景SQL Server里用varchar存储了数据但关联或排序时需要当成数字处理。比如成绩单号字段是“2024001”这种字符串想按数字排序的时候就出问题了因为字符串排序是按字典序排的100会排在99前面。解决办法是用转换函数在SQL查询里可以这样写SELECT * FROM Scores ORDER BY CAST(ScoreNo AS INT)EF Core里对应写法是var list await _context.Scores .OrderBy(s EF.Functions.Columnint(s.ScoreNo)) // 前提是EF版本支持 .ToListAsync();更保险的方式是直接用EF.Functions里的Convert方法或者干脆写原生SQLvar list await _context.Scores .FromSqlRaw(SELECT * FROM Scores ORDER BY CAST(ScoreNo AS INT)) .ToListAsync();这里要小心如果ScoreNo里存在非数字字符CAST会直接报错。所以在做转换前要么保证数据格式统一要么用ISNUMERIC先做过滤。你可以在SQL Server里写SELECT * FROM Scores WHERE ISNUMERIC(ScoreNo) 1 ORDER BY CAST(ScoreNo AS INT)ISNUMERIC有时会有误判比如1e3这种科学计数法会被当成数字所以如果是严格业务场景更推荐用TRY_CAST转换失败返回NULL而不是报错SELECT * FROM Scores WHERE TRY_CAST(ScoreNo AS INT) IS NOT NULL ORDER BY TRY_CAST(ScoreNo AS INT)这个细节对从MySQL或其他数据库迁移过来的开发者也同样重要MySQL里字符串转数字用CAST或CONVERTSQL Server稍微有点不同先了解差异再动手移植能省掉很多排查时间。6. 总结与个人实践心得整个项目从搭建骨架到完成学生成绩管理的增删改查和JWT认证大约花了两个完整工作日其中排查问题的时间占了一半以上。我个人觉得这套技术栈最大的价值在于JWT把前后端身份认证彻底解耦EF Core把数据库操作变得接近内存操作SQL Server则提供了稳定可靠的数据存储能力三者结合可以让一个小团队快速交付高可用后端服务。这套方案后续可以扩展的方向有几个一是引入Redis做验证码缓存和登录限流防止接口被暴力调用二是用.NET自带或第三方工具做接口文档增强把Swagger接入JWT认证方便前端联调时直接输入Token测试三是把文件上传下载扩展成MinIO或阿里云OSS应对非结构化数据快速增长的需求。每个方向单独拆开都是一篇完整的实战文章后面有时间我再逐个写。最后分享一个实用的小技巧开发阶段建议在Program.cs里打开DetailedErrors和UseSwagger的UI配合appsettings.Development.json里的日志配置出错时能看到详细的异常堆栈和SQL语句排查问题的速度会快很多。等上线部署时再把详细错误关掉避免泄露内部信息。这一步虽然看似简单但调试效率上的提升非常明显。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门