当前位置: 首页 > news >正文

C# .NET 6 WebAPI 对接微信小程序:3类接口设计与JWT鉴权实战

C# .NET 6 WebAPI 对接微信小程序:3类接口设计与JWT鉴权实战

当开发者需要为微信小程序构建稳定可靠的后端服务时,C# .NET 6 WebAPI凭借其高性能和丰富的生态成为理想选择。本文将深入探讨三种典型接口的设计实现,并构建完整的JWT鉴权体系,为缺乏小程序联调经验的.NET开发者提供可直接复用的工程化解决方案。

1. 环境准备与基础配置

在开始接口开发前,需要完成基础环境的搭建。首先确保已安装.NET 6 SDK和Visual Studio 2022(或Rider等IDE)。创建新项目时选择"ASP.NET Core Web API"模板,并勾选启用OpenAPI支持:

dotnet new webapi -n WeChatMiniAppApi --framework net6.0

微信小程序要求所有网络请求必须使用HTTPS协议,因此在Program.cs中需强制启用HTTPS重定向:

app.UseHttpsRedirection();

小程序后端接口的典型配置参数如下表所示:

配置项示例值说明
BaseUrlhttps://api.yourdomain.com已备案的HTTPS域名
RequestTimeout30000微信请求超时(毫秒)
MaxRequestBodySize20971520文件上传最大支持20MB
CORS PolicyWeChatPolicy跨域策略名称

配置微信小程序合法域名时,需在微信公众平台将API域名加入request合法域名列表。同时配置开发环境的appsettings.Development.json

{ "WeChatSettings": { "AppId": "wx你的小程序appid", "AppSecret": "你的小程序appsecret" } }

2. JWT鉴权体系实现

微信小程序推荐使用自定义登录态维护用户身份,JWT(JSON Web Token)因其无状态特性成为首选方案。首先安装必要的NuGet包:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer dotnet add package System.IdentityModel.Tokens.Jwt

Program.cs中配置JWT认证服务:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidateAudience = true, ValidAudience = builder.Configuration["Jwt:Audience"], ValidateLifetime = true, IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"])), ValidateIssuerSigningKey = true, ClockSkew = TimeSpan.Zero // 严格校验过期时间 }; });

创建JwtHelper工具类处理token生成:

public static class JwtHelper { public static string GenerateToken(string openId, IConfiguration config) { var claims = new[] { new Claim(JwtRegisteredClaimNames.Sub, openId), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) }; var key = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(config["Jwt:SecretKey"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: config["Jwt:Issuer"], audience: config["Jwt:Audience"], claims: claims, expires: DateTime.Now.AddDays(7), signingCredentials: creds); return new JwtSecurityTokenHandler().WriteToken(token); } }

注意:实际生产环境中应考虑使用分布式缓存存储token黑名单,实现更灵活的身份控制

3. 用户登录接口设计

微信小程序登录流程涉及三方交互:小程序端、开发者服务器和微信接口服务。典型登录接口实现如下:

[ApiController] [Route("api/[controller]")] public class AuthController : ControllerBase { private readonly IHttpClientFactory _httpClient; private readonly IConfiguration _config; public AuthController(IHttpClientFactory httpClient, IConfiguration config) { _httpClient = httpClient; _config = config; } [HttpPost("login")] public async Task<IActionResult> Login([FromBody] LoginRequest request) { // 1. 校验code有效性 var client = _httpClient.CreateClient(); var url = $"https://api.weixin.qq.com/sns/jscode2session?" + $"appid={_config["WeChatSettings:AppId"]}" + $"&secret={_config["WeChatSettings:AppSecret"]}" + $"&js_code={request.Code}" + "&grant_type=authorization_code"; var response = await client.GetFromJsonAsync<WeChatSessionResponse>(url); if (string.IsNullOrEmpty(response.OpenId)) return BadRequest("微信登录失败"); // 2. 创建或更新用户记录 var user = await _userRepository.FindOrCreateAsync(response.OpenId); // 3. 生成JWT var token = JwtHelper.GenerateToken(response.OpenId, _config); return Ok(new LoginResponse { Token = token, UserInfo = new UserDto(user) }); } }

登录流程中涉及的关键DTO定义:

public record LoginRequest(string Code); public record WeChatSessionResponse(string OpenId, string SessionKey, string UnionId); public record LoginResponse(string Token, UserDto UserInfo);

4. 数据获取接口实现

数据接口需要同时考虑安全性和性能优化。以下是带分页的商品列表接口示例:

[Authorize] [HttpGet("products")] public async Task<IActionResult> GetProducts([FromQuery] ProductQuery query) { // 1. 参数校验 if (query.PageSize > 100) return BadRequest("单页数量不能超过100"); // 2. 从JWT中获取用户身份 var openId = User.FindFirstValue(JwtRegisteredClaimNames.Sub); // 3. 构建查询条件 var filter = new ProductFilter { CategoryId = query.CategoryId, MinPrice = query.MinPrice, MaxPrice = query.MaxPrice, Keywords = query.Keywords }; // 4. 分页查询 var (products, total) = await _productService.GetPagedListAsync( filter, query.PageIndex, query.PageSize); // 5. 返回标准化响应 return Ok(new PagedResult<ProductDto>( products.Select(p => new ProductDto(p)), query.PageIndex, query.PageSize, total)); }

为提高接口安全性,建议实现以下防护措施:

  • 参数签名验证:防止参数篡改
  • 请求频率限制:防止暴力请求
  • SQL注入防护:使用参数化查询
  • 敏感数据脱敏:如手机号中间四位替换为*

5. 文件上传接口设计

微信小程序常见的文件上传场景包括用户头像、反馈图片等。以下是支持多文件上传的接口实现:

[Authorize] [HttpPost("upload")] [RequestSizeLimit(20 * 1024 * 1024)] // 20MB限制 public async Task<IActionResult> UploadFiles(List<IFormFile> files) { var openId = User.FindFirstValue(JwtRegisteredClaimNames.Sub); var result = new List<UploadResult>(); foreach (var file in files) { // 1. 文件类型校验 var ext = Path.GetExtension(file.FileName).ToLowerInvariant(); if (!_allowedExtensions.Contains(ext)) { result.Add(new UploadResult { FileName = file.FileName, Success = false, Message = "不支持的文件类型" }); continue; } // 2. 生成唯一文件名 var newFileName = $"{Guid.NewGuid()}{ext}"; var savePath = Path.Combine(_env.WebRootPath, "uploads", newFileName); // 3. 保存文件 await using var stream = new FileStream(savePath, FileMode.Create); await file.CopyToAsync(stream); // 4. 记录上传结果 result.Add(new UploadResult { FileName = file.FileName, FileUrl = $"/uploads/{newFileName}", FileSize = file.Length, Success = true }); } return Ok(result); }

文件上传接口的关键配置参数:

参数推荐值说明
MaxRequestBodySize20971520Kestrel最大请求体
MultipartBodyLengthLimit20971520多部分表单限制
FileSizeLimit5242880单个文件最大5MB
AllowedExtensions.jpg,.png,.gif允许的扩展名

6. 小程序端请求封装

为统一处理鉴权和错误,建议在小程序端封装wx.request

const request = (url, method, data) => { return new Promise((resolve, reject) => { const token = wx.getStorageSync('token') wx.request({ url: `https://your-api-domain.com${url}`, method: method, data: data, header: { 'Content-Type': 'application/json', 'Authorization': token ? `Bearer ${token}` : '' }, success: (res) => { if (res.statusCode === 401) { // token过期处理 wx.navigateTo({ url: '/pages/login/login' }) return } if (res.data.code !== 0) { wx.showToast({ title: res.data.message, icon: 'none' }) reject(res.data) return } resolve(res.data.data) }, fail: (err) => { wx.showToast({ title: '网络错误', icon: 'none' }) reject(err) } }) }) } // 使用示例 const fetchProducts = async (params) => { return request('/api/products', 'GET', params) }

7. 异常处理与日志记录

完善的异常处理机制能显著提升接口可靠性。在.NET 6中配置全局异常处理:

// Program.cs app.UseExceptionHandler(appError => { appError.Run(async context => { context.Response.ContentType = "application/json"; var contextFeature = context.Features.Get<IExceptionHandlerFeature>(); if (contextFeature != null) { // 记录异常日志 _logger.LogError(contextFeature.Error, "全局异常捕获"); // 构造标准化错误响应 await context.Response.WriteAsync(new ErrorDetails { StatusCode = context.Response.StatusCode, Message = contextFeature.Error is BusinessException ? contextFeature.Error.Message : "系统异常" }.ToString()); } }); });

建议将日志记录到文件的同时,接入APM工具如Application Insights实现:

  • 请求链路追踪
  • 性能指标监控
  • 异常告警通知
  • 依赖调用分析

8. 性能优化实践

针对小程序场景的高并发特点,可采用以下优化策略:

缓存策略示例:

[HttpGet("categories")] [ResponseCache(Duration = 3600)] // 客户端缓存1小时 public async Task<IActionResult> GetCategories() { var cacheKey = "product_categories"; if (!_cache.TryGetValue(cacheKey, out List<Category> categories)) { categories = await _categoryService.GetAllAsync(); _cache.Set(cacheKey, categories, new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromHours(1))); } return Ok(categories); }

数据库查询优化技巧:

  1. 使用AsNoTracking()避免不必要的变更跟踪
  2. 通过Select只查询必要字段
  3. 合理配置索引,特别是高频查询条件
  4. 对大数据集使用分页查询

异步编程规范:

  • Controller方法全部使用async/await
  • 避免.Result.Wait()导致的死锁
  • 长时间运行任务使用BackgroundService
  • IO密集型操作配置合理的超时时间

9. 安全加固措施

除JWT鉴权外,还需实施以下安全防护:

防XSS攻击:

services.AddControllers(options => { options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); });

CSRF防护配置:

builder.Services.AddAntiforgery(options => { options.HeaderName = "X-CSRF-TOKEN"; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; });

敏感数据保护:

// 配置数据保护API builder.Services.AddDataProtection() .PersistKeysToFileSystem(new DirectoryInfo("/path/to/keys")) .SetApplicationName("WeChatMiniApp") .ProtectKeysWithCertificate(certificate);

10. 部署与监控

推荐使用Docker容器化部署,示例Dockerfile:

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["WeChatMiniAppApi.csproj", "."] RUN dotnet restore "WeChatMiniAppApi.csproj" COPY . . RUN dotnet build "WeChatMiniAppApi.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "WeChatMiniAppApi.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "WeChatMiniAppApi.dll"]

关键监控指标应包括:

  • 接口响应时间P99
  • 错误率与异常统计
  • JWT签发/验证性能
  • 数据库查询效率
  • 外部API调用成功率

11. 接口测试策略

采用分层测试策略确保接口质量:

单元测试示例:

[Fact] public void GenerateToken_ShouldReturnValidJwt() { // Arrange var config = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary<string, string> { ["Jwt:SecretKey"] = "test_key_1234567890", ["Jwt:Issuer"] = "test_issuer", ["Jwt:Audience"] = "test_audience" }) .Build(); // Act var token = JwtHelper.GenerateToken("test_openid", config); // Assert Assert.NotNull(token); Assert.NotEmpty(token); }

集成测试要点:

  1. 测试带鉴权的API访问
  2. 验证微信code交换流程
  3. 文件上传大小限制测试
  4. 并发请求压力测试
  5. 错误参数边界测试

12. 实际项目经验分享

在电商类小程序项目中,我们遇到了购物车高并发更新的挑战。最终解决方案是:

  1. 使用Redis分布式锁控制并发
  2. 采用乐观并发控制处理冲突
  3. 客户端实现本地缓存减少请求
  4. 重要操作添加二次确认

另一个教育类小程序中,视频上传接口经过以下优化:

  1. 分片上传支持
  2. 断点续传功能
  3. OSS直传方案
  4. 上传进度实时反馈

对于需要更高安全要求的金融类小程序,我们额外实现了:

  1. 接口调用白名单
  2. 请求参数签名
  3. 敏感操作短信验证
  4. 操作日志审计
http://www.cnnetsun.cn/news/3183056.html

相关文章:

  • Java 事件驱动编程实战:基于 ActionListener 实现游戏逻辑的 2 种状态管理
  • Java+Vue+SpringBoot+MySQL员工绩效系统毕业设计部署与核心模块解析
  • Java+Vue+SpringBoot+MySQL课程作业管理系统:毕业设计实战部署与二次开发指南
  • 古风模特ai图片生成与多平台场景应用案例解析
  • 从DOSBox到86Box:五种虚拟化技术如何解决老软件兼容性问题
  • Balena Etcher终极指南:三步轻松制作系统启动盘,告别命令行复杂操作
  • STBP-tdBN 算法实战:ResNet-50 SNN 在 CIFAR-10 上实现 93.15% 准确率(6 时间步)
  • 01.02.01.DeepSeek:环境搭建篇(数据库 PostgreSQL)
  • 高精度计时系统:CS2200-CP与STM32F415ZG的硬件设计与应用
  • Plone主题开发底层逻辑:ZCA、TAL与Viewlet分层模型解析
  • Maya动画管理终极指南:Studio Library如何提升你的工作效率300%
  • SQL触发器:数据一致性的最后一道闸门
  • Excel slicer高级筛选实战:跨表联动与数据模型原理
  • Zenko:面向多云对象存储的数据编排层与策略驱动实践
  • Python工程选型的四大核心理由:效率、集成、交付与调试
  • DeepSeek服务波动根因解析:从503/504错误到云原生AI弹性瓶颈
  • Django技术向善:用开源框架驱动社会价值落地
  • PIC32与DS28EC20在嵌入式EEPROM存储中的优化实践
  • Snowflake四层架构实战:从账户初始化到可运行数据库
  • ASM330LHH与STM32F439ZG在运动跟踪技术中的应用
  • Django项目Kubernetes+GitOps自动化部署实战
  • 日常 Kubernetes:从命令行到工作流的工程化实践
  • KannalaBrandt8 模型 OpenCV 4.8 标定实战:5步完成鱼眼相机参数精确求解
  • Excel冻结多行:解决表头丢失的信息锚定问题
  • Windows Cleaner:如何通过3个核心功能解决C盘空间不足问题
  • Plone可扩展性实战:ZEO集群与ZODB优化指南
  • AI大模型职业指南:从核心岗位到实战技能,助你成功转型
  • COCO 2017 数据集格式实战:5分钟代码解析 JSON 5大核心字段
  • AWS VPC从零搭建:CLI与控制台双轨验证实战
  • 【信息科学与工程学】【通信工程】第八十六篇 通信与网络系统几何–拓扑–代数分析01