在MySQL中,没有单条SQL语句能直接优化和修复所有表。不过,有以下两种常用方法可以实现这个需求:
1. 使用MySQL命令行工具:
mysqlcheck -u root -p --auto-repair --optimize --all-databases这条命令会提示输入密码,然后自动修复并优化所有数据库中的所有表。
2. 如果确实需要在MySQL客户端内执行,可以生成批处理语句:
SELECT CONCAT('OPTIMIZE TABLE ', table_schema, '.', table_name, '; REPAIR TABLE ', table_schema, '.', table_name, ';')
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema','mysql','performance_schema','sys');执行此查询后,复制结果中的所有语句再执行。
注意:执行表优化和修复操作需要相应权限,且在高负载生产环境中应谨慎操作,最好在低峰期进行。
AsNoTracking 设置未追踪查询
var customers = dbContext.Customers.AsNoTracking().ToList();这对于只读查询非常有用,因为它可以减少内存使用并提高性能,因为它不需要维护实体的更改跟踪信息。
ExecuteDelete 和 ExecuteUpdate 批量操作
context.Logs.Where(c => c.Time < new DateTime(2000, 1, 1)).ExecuteDelete();从 EF Core 7 开始,ExecuteDelete 和 ExecuteUpdate 是官方原生支持的批量操作方法。直接操作数据库,不需要调用 SaveChanges():不加载实体到内存,减少内存消耗和网络往返。
若需要分页删除和大批量插入,或在高频、大规模场景,推荐使用 Zack.EFCore.Batch:
context.Logs.Where(c => c.Time < new DateTime(2000, 1, 1)).DeleteRangeAsync(batchSize: 1000);考虑用 Union 代替 OR
// Where 后行数多时(如分页前)用 OR
var q = db.dt_crm__contract.AsNoTracking();
q = q.Where(c => c.dt_crm__customer.SalesmanId == uid || myIns.Contains(c.IndustryId));
// 用于合并的 q1、q2 的行数少时用 Union
var q1 = db.dt_crm__contract.AsNoTracking().Where(c => c.dt_crm__customer.SalesmanId == uid);
var q2 = db.dt_crm__contract.AsNoTracking().Where(c => myIns.Contains(c.IndustryId));
var q = q1.Union(q2);“ToDictionary + Count”之前先 Select
// 不推荐
var dic = q.GroupBy(c => c.Date)
.ToDictionary(k => k.Key, v => v.Count());
// 推荐
var dic = q.GroupBy(c => c.Date)
.Select(g => new { g.Key, Count = g.Count() })
.ToDictionary(k => k.Key, v => v.Count);临界区与 lock 关键字
核心作用:
通过将多线程访问串行化,保护共享资源或代码段。lock 关键字是 Monitor 类的语法糖,提供异常安全的临界区实现。
实现示例:
// 创建私有静态只读对象 // private static readonly object _lockObj = new object(); private static readonly System.Threading.Lock _locker = new(); // .NET 9+ 推荐使用 Lock 类型,避免传统 object 的性能损耗 public void ThreadSafeMethod() { lock (_lockObj) { // 临界区代码(每次仅一个线程可进入) } }超时机制:
高并发场景可结合 Monitor.TryEnter 设置超时,避免无限等待:
if (Monitor.TryEnter(lockObject, TimeSpan.FromSeconds(1))) { try { /* 操作 */ } finally { Monitor.Exit(lockObject); } }关键特性:
用户态锁(无内核切换开销)
自动调用Monitor.Enter和Monitor.Exit
必须使用专用私有对象作为锁标识
注意事项:
❌ 避免锁定this、Type实例或字符串(易引发死锁)
❌ 避免嵌套锁(需严格按顺序释放)
✅ 推荐readonly修饰锁对象
❌ lock 不适用于异步代码(async/await),需使用 SemaphoreSlim 实现异步锁
互斥锁(Mutex)
核心作用:
系统级内核锁,支持跨进程同步,但性能开销较高(用户态/内核态切换)。
实现示例:
using var mutex = new Mutex(false, "Global\\MyAppMutex"); try { // 等待锁(最大等待时间500ms) if (mutex.WaitOne(500)) { // 临界区代码 } } finally { if (mutex != null) { mutex.ReleaseMutex(); } }关键特性:
支持跨应用程序域同步
线程终止时自动释放锁
支持命名互斥体(系统全局可见)
适用场景:
单实例应用程序控制
进程间共享文件访问
硬件设备独占访问
信号量(Semaphore)
核心作用:
通过许可计数器控制并发线程数,SemaphoreSlim为轻量级版本(用户态实现)。
实现对比:
类型 跨进程 性能 最大许可数 Semaphore ✔️ 低 系统限制 SemaphoreSlim ❌ 高 Int32.Max 代码示例:
// 创建初始3许可、最大5许可的信号量 var semaphore = new SemaphoreSlim(3, 5); semaphore.Wait(); // 获取许可 try { // 资源访问代码 } finally { semaphore.Release(); }异步编程
private readonly SemaphoreSlim _asyncLock = new(1, 1); public async Task UpdateAsync() { await _asyncLock.WaitAsync(); try { /* 异步操作 */ } finally { _asyncLock.Release(); } }典型应用:
数据库连接池(限制最大连接数)
API 请求限流
批量任务并发控制
事件(Event)
核心机制:
通过信号机制实现线程间通知,分为两种类型:
类型 信号重置方式 唤醒线程数 AutoResetEvent 自动 单个 ManualResetEvent 手动 所有 使用示例:
var autoEvent = new AutoResetEvent(false); // 等待线程 Task.Run(() => { autoEvent.WaitOne(); // 收到信号后执行 }); // 信号发送线程 autoEvent.Set(); // 唤醒一个等待线程高级用法:
配合WaitHandle.WaitAll实现多事件等待
使用ManualResetEventSlim提升性能
读写锁(ReaderWriterLockSlim)
核心优势:
实现读写分离的并发策略,适合读多写少场景(如缓存系统)。
锁模式对比:
模式 并发性 升级支持 读模式(EnterReadLock) 多线程并发读 ❌ 写模式(EnterWriteLock) 独占访问 ❌ 可升级模式 单线程读→写 ✔️ 代码示例:
var rwLock = new ReaderWriterLockSlim(); // 读操作 rwLock.EnterReadLock(); try { // 只读访问 } finally { rwLock.ExitReadLock(); } // 写操作 rwLock.EnterWriteLock(); try { // 排他写入 } finally { rwLock.ExitWriteLock(); }最佳实践:
优先使用ReaderWriterLockSlim(旧版有死锁风险)
避免长时间持有读锁(可能饿死写线程)
原子操作(Interlocked)
原理:
通过CPU指令实现无锁线程安全操作。
常用方法:
int counter = 0; Interlocked.Increment(ref counter); // 原子递增 Interlocked.Decrement(ref counter); // 原子递减 Interlocked.CompareExchange(ref value, newVal, oldVal); // CAS操作适用场景:
简单计数器
标志位状态切换
无锁数据结构实现
自旋锁(SpinLock)
核心特点:
通过忙等待(busy-wait)避免上下文切换,适用极短临界区(<1微秒)。
实现示例:
private SpinLock _spinLock = new SpinLock(); public void CriticalOperation() { bool lockTaken = false; try { _spinLock.Enter(ref lockTaken); // 极短临界区代码 } finally { if (lockTaken) _spinLock.Exit(); } }优化技巧:
单核CPU需调用Thread.SpinWait或Thread.Yield
配合SpinWait结构实现自适应等待
同步机制对比指南
机制 跨进程 开销级别 最佳适用场景 lock ❌ 低 通用临界区保护 Mutex ✔️ 高 进程间资源独占 Semaphore ✔️ 中 并发数限制(跨进程) SemaphoreSlim ❌ 低 并发数限制(进程内) ReaderWriterLockSlim ❌ 中 读多写少场景 SpinLock ❌ 极低 纳秒级临界区 Interlocked - 无锁 简单原子操作
选择原则:
优先考虑用户态锁(lock/SpinLock/SemaphoreSlim)
跨进程需求必须使用内核对象(Mutex/Semaphore)
读写比例超过10:1时考虑读写锁
自旋锁仅用于高频短操作(如链表指针修改)
通过以上结构化的分类和对比,开发者可以更精准地选择适合特定场景的线程同步方案。建议在实际使用中配合性能分析工具(如BenchmarkDotNet)进行量化验证。
💡 ASP.NET 的异步编程(async/await)本质是单进程内的线程调度,不算“跨进程”。每个 IIS 应用程序池对应一个独立的工作进程(w3wp.exe),不同用户访问同一应用程序池下的 ASP.NET 网站,两者的请求均由同一个 w3wp.exe 进程处理。可能跨进程的场景有:Web Garden 配置、多应用程序池部署等。
在 C# 中,除了常规锁机制(如 lock、Mutex、Semaphore 等),还有一些内置类型通过内部锁或无锁设计实现线程安全。以下是常见的几类:
线程安全集合(System.Collections.Concurrent)这些集合通过细粒度锁或无锁算法(如 CAS)实现线程安全,适合高并发场景。
ConcurrentDictionary:分段锁机制,将数据分片存储,每个分片独立加锁,减少锁竞争。
ConcurrentQueue / ConcurrentStack:基于原子操作(Interlocked)保证线程安全。
ConcurrentBag:每个线程维护本地存储,减少争用,适合频繁添加和移除的场景。
BlockingCollection:基于 ConcurrentQueue 和信号量(SemaphoreSlim)实现生产-消费者模式,支持阻塞和超时。
不可变集合(System.Collections.Immutable) 通过数据不可变性实现线程安全(无需锁),每次修改返回新对象。
Lazy 的线程安全初始化(Lazy<T>) 通过锁或 Interlocked 确保延迟初始化的线程安全。
通道(System.Threading.Channels)用于异步生产-消费者模型,内部通过锁和信号量管理容量限制。
内存缓存(System.Runtime.Caching.MemoryCache)内部使用锁保护共享状态,确保线程安全。
原子操作类型(Interlocked 类、Volatile 关键字、Unsafe 类)通过 CPU 指令实现无锁线程安全。
其他同步工具(Barrier、CountdownEvent)虽然不是严格意义上的锁,但用于协调线程。
以下列出本人所遇到的情况及处理方法,肯定不全,但都有用。
使用系统自带清理工具进行清理
Windows 7 / 8 / 8.1 / 10:在 C 盘上点击右键属性 - 磁盘清理 - 清理系统文件 - 视情况勾选 - 确定
Windows 11:在 C 盘上点击右键属性 - 详细信息 - 临时文件 - 视情况勾选 - 确定
关闭“传递优化”
设置 - Windowx 更新 - 高级选项 - 传递优化,关闭“允许从其他设备下载”
其实前面说到的清理临时文件中已经包含了“传递优化文件”,所以这里按个人喜好选择是否关闭。
更改虚拟内存路径
可以将虚拟内存路径更改为非系统盘,但建议是固态硬盘。
关闭系统还原
当遇到系统问题时,如果你喜欢重装系统,而不是系统还原,那么可以关闭它。
更改桌面、文档、下载等用户文件夹的位置
将这些目录路径更改到非系统,但仍然建议是固态硬盘。以 Windows 11 的桌面目录为例:
打开资源管理器 - 主文件夹,右键点击“桌面”属性,切换到“位置”,移动。
将软件安装到其它盘
有些电脑管理软件有软件迁移功能,但我还是建议先卸载软件,再安装到其它盘符。
将软件文档路径更改到其它盘
如果不想把软件安装到其它盘(譬如只有C盘是固态硬盘),那么可以将文档路径更改到其它盘,譬如:
微信:☰ - 设置 - 文件管理 - 更改
QQ:☰ - 设置 - 存储管理 - 更改存储路径(注意是聊天消息那个)
企业微信:头像 - 设置 - 存储管理
钉钉:头像 - 设置与隐私 - 通用 - 缓存目录
千牛:设置 - 数据存储文件夹
清理浏览器缓存
Chrome:┇ - 设置 - 隐私与安全 - 删除浏览数据
Edge:… - 设置 - 隐私、搜索和服务 - 删除浏览记录 - 选择要清除的内容
Firefox:☰ - 设置 - 隐私与安全 - 历史记录 - 清除历史记录
VMware 虚拟机
在已安装的镜像上点击右键 - 管理 - 清理磁盘
把已安装的镜像复制到其它磁盘,再添加到 VMware 中,删除原镜像文件。
更改 Navicat 数据库备份目录
如果你的 Navicat 启用了自动运行的备份任务,那么可以更改备份路径。
在连接上点击右键编辑连接,切换到高级,更改设置位置。
SQL Server 数据库文件瘦身
若 SQL Server 数据目录(C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\DATA)中有很多较大的数据库日志文件(.ldf),可以按需采取以下措施:
使用 SSMS 连接到你的 SQL Server 实例;数据库恢复模式设置为“简单”;右键点击要压缩的数据库,选择“任务”->“收缩”->“文件”,选择“日志”,在“释放未使用的空间前重新组织页”一项中设置为 0MB,然后点击“确定”按钮。
最后推荐一款免费软件 TreeSize Free,可以查看磁盘中各目录和文件占用空间大小,小白不要乱删文件哦,删错了可就得重装系统了。
本文图片基于网络图片更新,如有侵权,请通过网站底部联系站长删除。
本文内容来源于网络,如有错误欢迎批评指正。
USB 接口类型
USB 接口有 Mini、Micro、Type 三种外观。

通常说的 USB-C 一般指 USB Type-C,因为没有 Mini-C 和 Micro-C。而 USB-A / USB-B 一般也特指 Type-A / Type-B。
数据传输能力
USB 标准(附 Thunderbolt)
| 规格 | 别名 | 接口类型 | 理论带宽 | 电力规格 | 备注 |
| USB 1.0 | Low-Speed | Mini A / B Micro A / B 2.0 Type A / B 2.0 | 1.5 Mbps | 5V/0.5A | |
| USB 1.1 | Full-Speed | 12 Mbps | 5V/0.5A | ||
| USB 2.0 | Hi-Speed High Speed | Mini A / B Micro A / B 2.0 Type A 2.0 / B 2.0 / C | 480 Mbps | 5V/0.5A | |
USB 3.0 USB 3.1 Gen 1 USB 3.2 Gen 1 | SuperSpeed | Micro B 3.0 Type A 3.x / B 3.x / C | 5 Gbps | 5V/0.9A PD2.0 100W | |
USB 3.1 USB 3.1 Gen 2 USB 3.2 Gen 2 | SuperSpeed+ | Micro B 3.0 Type A 3.x / B 3.x / C | 10 Gbps | 5V/0.9A PD2.0 100W | |
USB 3.2 USB 3.2 Gen 2×2 | SuperSpeed 20Gbps | Type-C | 20 Gbps | PD2.0 100W | |
USB4 Gen 2×2 | USB4 20Gbps | Type-C | 20 Gbps | PD3.0 100W PD3.1 240W | 兼容 Thunderbolt 3 |
| USB4 Gen 3×2 | USB4 40Gbps | Type-C | 40 Gbps | ||
| USB4 2.0 | USB4 80Gbps | Type-C | 80 Gbps | ||
| Thunderbolt 1 | 雷雳1、雷电1 | Mini DisplayPort | 10 Gbps | ||
| Thunderbolt 2 | 雷雳2、雷电2 | Mini DisplayPort | 20 Gbps | ||
| Thunderbolt 3 | 雷雳3、雷电3 | Type-C | 40 Gbps | 配件供电 15W PD 100W | 兼容 USB4 |
| Thunderbolt 4 | 雷雳4、雷电4 | Type-C | 40 Gbps | 配件供电 15W PD 100W | 兼容 Thunderbolt 3 |
| Thunderbolt 5 | 雷雳5、雷电5 | Type-C | 80Gpb (带宽增强 120Gpbs) | 240W | USB4 V2、DisplayPort 2.1 和 PCI Express Gen 4 |
Thunderbolt / 雷雳 / 雷电 是同一个意思,而闪电接口指的是 Lightning,都是区别于 USB 的传输技术
Thunderbolt 本身是数据传输和显示协议,Thunderbolt 3 起采用 USB-C 外观,支持 PD 协议后可进行快速充电
识别不同标准的接口,比如若 Type-C 接口旁边写着 ss 和 5 表示 SuperSpeed 和 5Gbps,即 USB 3.0;插口旁边带有⚡️标志表示雷电接口。
电力传输能力
常见快充协议
| 协议 | 版本 | 发布年份 | 区别 | 接口形态 |
BC (Battery Charging Specification) | 1.2 | 2010 年 | 7.5W(5V/1.5A) | |
高通 QC (Qualcomm Quick Charge) | 1.0 | 2013 年 | 10W(5V/2A) | Micro-USB |
| 2.0 | 2014 年 | Class A:5V、9V、12V 电压可选,最大输出功率为 18W? Class B:5V、9V、12V、20V 电压可选,最大输出功率为 60W | Micro-USB / Type-A | |
| 3.0 | 2015 年 | Class A:3.6-12V,Class B:3.6-20V,支持动态调整电压和电流,最大输出功率为 18W? | Micro-USB / Type-A / Type-C | |
| 4.0 | 2017 年 | 5V/5.6A 或 9V/3A,最大输出功率为 28W?,兼容了 USB PD 2.0 / PD 3.0 PPS? | Type-C | |
| 4+ | 2019 年 | 最大输出功率为 100W?,支持 PPS 协议?兼容 QC2.0~QC3.0 | Type-C | |
| 5.0 | 2020 年 | 最大输出功率 > 100W,兼容 QC2.0~QC4+ | Type-C | |
USB-PD (USB Power Delivery) | 1.0 | 2012 年 | 5V、12V、20V 电压可选,最大输出功率为 10W | Micro-USB / Type-A / Type-B |
| 2.0 | 2014 年 | 5V3A、9V3A、15V3A、20V3A、20V5A(E-Marked),最大输出功率为 100W | Type-C | |
| 3.0 | 2015 年 | 5V3A、9V3A、15V3A、20V3A、20V5A(E-Marked),最大输出功率为 100W | Type-C | |
| 3.0 PPS | 2017 年 | 支持 PPS 协议,3.3V-21V 3A-5A | Type-C | |
| 3.1 | 2021 年 | Type-C | ||
| 3.2 | 2023年 | 强制 AVS,优化 EPR | Type-C |
华为 SuperCharge(FCP / SCP)、小米 Mi Turbo Charge、OPPO VOOC、vivo FlashCharge、三星 AFC 等私有协议不在此列出。
Magsafe 电源适配器 是磁吸式有线充电接口,最大输出功率为 85W(截止2023年),用于 Macbook;
Magsafe 充电器 是磁吸式无线充电接口,最大输出功率为 15W(截止2023年),用于 iPhone,不属于快充。
Qi 是无线充电协议,Qi1 最大输出功率为 7.5W,Qi2 最大输出功率为 15W。
功率决定充电速度,功率与电压电流的关系:P=U*I,如 5V1A=5W
一条 USB 线由插座/母口(Receptacle)、插头/公口(Plug)和线缆(Cable)三部分组成,要实现某个协议,需要插座和插头都支持该协议。
PPS:Programmable Power Supply 可编程电源
SPR:Standard Power Range 标准功率范围
EPR:Extended Power Range 扩展功率范围
FCP:Fast Charge Protocol 华为早期快充协议,支持 18W
SCP:Super Charge Protocol 华为超级快充协议,支持 22.5W、40W
论坛出现动态页面打开或刷新非常慢(超过半分钟),甚至打不开,该页无法显示,502 Bad Gateway 等情况,TTFB 计时需要好几秒。

切换 nginx 版本、php 版本 等均无效果,甚至购买新的 ECS 重新搭建环境也是效果甚微。
最终在阿里云 RDS 管理中,手动主备库切换,问题得以解决。

因此大概率是原主库有问题(有损坏的表需要修复或锁等情况)。
查询故障时间段的慢 SQL,发现某表的锁等待时间特别长,不知道是不是这个表的原因导致的。

次日,提交工单得到回复是原主库(现备库)会自动修复,经再次主备库切换测试得到了肯定的结果。
再次日,又遇到同样的打开慢的情况,这次直接找到慢 SQL 中锁等待时间较长的表,优化(OPTIMIZE TABLE)(虽然是 InnoDB),立刻解决了问题。
Microsoft.Net.Compilers is only supported on MSBuild v16.3 and above
解决办法:菜单 - 生成 - ASP.NET 编译 - 优化解决方案的生成性能

如果没有该项,在解决方案资源管理器中选中解决方案再试。
数据表所占用的空间(简称“表空间”)一般会大于其数据空间和索引空间的和。
当数据被删除时,其所占空间并不会立即释放,而是等待新数据写入,这会导致出现许多磁盘碎片。使用 OPTIMIZE TABLE 或 ALTER TABLE 可以回收碎片,重组文件。优化表的过程类似于 Windows 碎片整理。
操作过程会导致该表上的写操作无法执行。
一般在删除了大批量数据或更改了许多可变长度字段后执行优化表。
碎片率 = 100% - (数据空间 + 索引空间) / 表空间
优化后碎片率接近于 0%,数据空间和索引空间也会变小,此时 表空间 接近于“数据空间 + 索引空间”
在 MyISAM 引擎上遇到优化后导致获取行数为 0,SELECT 数据只有 1 条的情况,需要执行修复表(REPAIR TABLE),使数据恢复正常。执行后结果显示:Number of rows changed from 0 to xxxxxx
于 2024 年 8 月:
查看空间占用
主菜单“云数据库 RDS” - 选择相应实例 - 自治服务 - 一键诊断 - 空间分析 - 立即分析 - 几分钟后就能看到报表(经济版 / 企业版可以开启自动分析)
如果没有可清理的空间,可以直接扩容(有条件):实例 - 基本信息 - 变更配置 - 立即升配
慢查询(索引优化建议)
主菜单“云数据库 RDS” - 选择相应实例 - 自治服务 - 慢SQL
2019 年 5 月前:
查看空间占用
主菜单“云数据库 RDS 版” - 选择相应实例 - 二级菜单“CloudDBA” - 空间管理
主菜单“混合云数据库管理 HDM” - 选择相应实例 - 二级菜单“库表空间” - 数据空间
注意“数据空间”只是数据的大小,跟磁盘占用有直接关系的是“表空间”,某些表(如日志表)记录频繁插入删除的,表空间会比较大,应执行“优化表(OPTIMIZE TABLE)”,优化过程中会影响表的写操作
慢查询(索引优化建议)
主菜单“云数据库 RDS 版” - 选择相应实例 - 二级菜单“CloudDBA” - 性能优化
主菜单“混合云数据库管理 HDM” - 选择相应实例 - 二级菜单“请求分析” - 慢日志
前提:
若需获取用户 unionid,则小程序必须已绑定到微信开放平台。
小程序调用 wx.login(),将 code 发送到开发者服务器
开发者服务器请求微信接口 code2Session,获得 session_key、openid,有机会获得 unionid(参 UnionID 机制说明)。因此无需任何用户授权即可获取 openid
访问须要已获取用户信息的页面时,可通过小程序端使用 button + getUserInfo 的方式请求用户授权
访问须要已获取手机号码的页面时,可通过小程序端使用 button + getPhoneNumber 的方式请求用户授权
code2session、getUserInfo、getPhoneNumber 等接口返回数据存入数据库 dt_weapp_user
服务端的任何接口均返回口令状态包(含自定义会话口令等信息),小程序端应保存于 storage
请求 getUserInfo 后应判断 detail.errMsg 是否为 getUserInfo:ok,请求 getPhoneNumber 后应判断 detail.errMsg 是否为 getPhoneNumber:ok
授权窗口的弹出情况参此文
口令状态包是用来传递到客户端用来标识用户唯一凭证的,包含字段:会话口令(自定义用户凭证)、会话口令有效时间(秒)、是否已获取 unionid 或昵称(视业务需求)、是否已获取手机号码、uid(可选),以及其他用来标识业务身份字段。服务端可根据会话口令从数据库中获取该用户的所有已知信息。
一般情况下,当已获取手机号码时,uid 必有值,否则,uid 必为空。因此 uid 不是必须传递到客户端的。
通过 code2Session 获得的 session_key、以及 openid、unionid、手机号码等机密信息不允许包含在口令状态包中并缓存在客户端,客户端需要用到手机号码时单独调用接口获取。
若业务不要求以手机号码作为用户唯一标识,那么口令状态包中不需要包含是否已获取手机号码,仅 uid 即可。在客户端访问须要业务用户登录的页面时,也仅判断 uid 有值即可。
流程图:

为更好地适应 2017.7.19 发布的《小程序内用户帐号登录规范调整和优化建议》,开发者服务器提供的所有业务逻辑接口的返回值中都应包含口令状态包,约定有以下相关的返回值和处理动作(返回值按个人喜好来定):
| 口令状态 | 接口返回 | ASP.NET Web API 语句 | 处理动作 |
| access_token 空或失效 | 401 状态码 | return Unauthorized(); | login() + code2Session,获取 openid,生成 access_token |
| access_token 有效,但未获取 uniond 或用户信息 | 401 状态码,http 头 WWW-Authenticate 中标注 getUserInfo | return Unauthorized(new AuthenticationHeaderValue("getUserInfo")); | getUserInfo() |
| access_token 有效,但未获取手机号码(即未找到业务用户) | 401 状态码,http 头 WWW-Authenticate 中标注 getPhoneNumber | return Unauthorized(new AuthenticationHeaderValue("getPhoneNumber")); | getPhoneNumber() |
WWW-Authenticate 有大小写的问题,千万别入坑,参:https://xoyozo.net/Blog/Details/header-www-authenticate
返回内容是根据业务逻辑在服务端决定的,客户端在处理每个接口返回值时对返回内容作相应的处理即可。