您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

.net Web API幂等性实现

wiki 2022/3/6 14:36:43 dotnet 字数 10552 阅读 1934

API的幂等性(Idempotent),是指调用某个方法1次或N次对资源产生的影响结果都是相同的。 GET请求默认是幂等的,因为它只是查询资源,而不会修改资源。 而POST请求默认是不幂等的,多次调用POST方法可能会产生不同的结果,并会创建多个资源。 想象一下,你在扫码支付时,输入金额后点击了2次“确定”按钮,肯定不希望扣2次款。

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的高可用性和业务连续性,实现幂等性是重要的一步。


如果您也喜欢它,动动您的小指点个赞吧

除非注明,文章均由 laddyq.com 整理发布,欢迎转载。

转载请注明:
链接:http://laddyq.com
来源:laddyq.com
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


联系我
置顶