一键搞定ASP.NET Core Web API幂等性
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一键搞定ASP.NET Core Web API幂等性相关的知识,希望对你有一定的参考价值。
API的幂等性(Idempotent),是指调用某个方法1次或N次对资源产生的影响结果都是相同的。
GET请求默认是幂等的,因为它只是查询资源,而不会修改资源。
而POST请求默认是不幂等的,多次调用POST方法可能会产生不同的结果,并会创建多个资源。
想象一下,你在扫码支付时,输入金额后点击了2次“确定”按钮,肯定不希望扣2次款。
幂等性保证了操作只会执行一次。
1.思路
使用ASP.NET Core过滤器来处理POST请求,检查请求头中的幂等键(IdempotencyKey)。
如果在缓存中未检查到IdempotencyKey
,则真实执行操作并缓存响应数据,否则直接返回缓存的响应数据。
这样,操作只能对资源产生一次影响。
原理示意图如下:
2.实现
2.1 IdempotentAttributeFilter
创建自定义Filter。
使用OnActionExecuting
方法在执行操作前检查缓存,如有缓存直接返回context.Result
;使用OnResultExecuted
方法在执行操作后缓存响应。
代码如下:
public class IdempotentAttributeFilter : IActionFilter, IResultFilter
{
private readonly IDistributedCache _distributedCache;
private bool _isIdempotencyCache= false;
const string IdempotencyKeyHeaderName = "IdempotencyKey";
private string _idempotencyKey;
public IdempotentAttributeFilter(IDistributedCache distributedCache)
{
_distributedCache = distributedCache;
}
public void OnActionExecuting(ActionExecutingContext context)
{
Microsoft.Extensions.Primitives.StringValues idempotencyKeys;
context.HttpContext.Request.Headers.TryGetValue(IdempotencyKeyHeaderName, out idempotencyKeys);
_idempotencyKey = idempotencyKeys.ToString();
var cacheData = _distributedCache.GetString(GetDistributedCacheKey());
if (cacheData != null)
{
context.Result = JsonConvert.DeserializeObject<ObjectResult>(cacheData);
_isIdempotencyCache = true;
return;
}
}
public void OnResultExecuted(ResultExecutedContext context)
{
//已缓存
if (_isIdempotencyCache)
{
return;
}
var contextResult = context.Result;
DistributedCacheEntryOptions cacheOptions = new DistributedCacheEntryOptions();
cacheOptions.AbsoluteExpirationRelativeToNow = new TimeSpan(24, 0, 0);
//缓存:
_distributedCache.SetString(GetDistributedCacheKey(), JsonConvert.SerializeObject(contextResult), cacheOptions);
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnResultExecuting(ResultExecutingContext context)
{
}
private string GetDistributedCacheKey()
{
return "Idempotency:" + _idempotencyKey;
}
}
2.2 IdempotentAttribute
创建自定义Attribute。
声明了IdempotentAttribute
的Class或者Method,在运行时会创建IdempotentAttributeFilter
。
代码如下:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class IdempotentAttribute : Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var distributedCache = (IDistributedCache)serviceProvider.GetService(typeof(IDistributedCache));
var filter = new IdempotentAttributeFilter(distributedCache);
return filter;
}
}
3.使用
3.1 创建项目
新建ASP.NET Core Web API项目,实现代码如下:
private static List<WeatherForecast> _db = new List<WeatherForecast>();
[HttpPost]
public WeatherForecast Post(int temperature)
{
var data = new WeatherForecast { TemperatureC = temperature };
_db.Add(data);
return data;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
return _db.Select(p => new WeatherForecast
{
TemperatureC = p.TemperatureC,
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
这里用一个静态变量模拟数据库,POST请求写入数据,GET请求读取数据。
3.2 设置幂等
为Post方法加上Idempotent
Attribute:
[Idempotent]
public WeatherForecast Post(int temperature)
3.3 注册分布式缓存
从上面的原理图我们可以看到,必须增加分布式缓存,用于保存幂等键的值和响应数据。
修改Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDistributedMemoryCache();
}
3.4 测试
运行Web API,使用不同IdempotencyKey
执行POST请求,然后获取数据:
可以看到,同一IdempotencyKey
执行了2次,但是只写入了一条数据,成功!
结论
为了确保关键Web API的高可用性和业务连续性,实现幂等性是重要的一步。
如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“,记住我
以上是关于一键搞定ASP.NET Core Web API幂等性的主要内容,如果未能解决你的问题,请参考以下文章
将文件从 ASP.NET Core Web api 发布到另一个 ASP.NET Core Web api