当前位置:网站首页>ASP.NET Core Web API 幂等性
ASP.NET Core Web API 幂等性
2022-08-02 05:35:00 【KingCruel】
API的幂等性(Idempotent),是指调用某个方法1次或N次对资源产生的影响结果都是相同的。
GET请求默认是幂等的,因为它只是查询资源,而不会修改资源。
而POST请求默认是不幂等的,多次调用POST方法可能会产生不同的结果,并会创建多个资源。
想象一下,你在扫码支付时,输入金额后点击了2次“确定”按钮,肯定不希望扣2次款。
幂等性保证了操作只会执行一次。
1、思路
使用ASP.NET Core过滤器来处理POST请求,检查请求头【Headers】中的幂等键(IdempotencyKey)。
如果在缓存中未检查到IdempotencyKey,则真实执行操作并缓存响应数据,否则直接返回缓存的响应数据。
这样,操作只能对资源产生一次影响。
2、IdempotentAttributeFilter
创建自定义Filter,使用OnActionExecuting方法在执行操作前检查缓存,如有缓存直接返回context.Result;使用OnResultExecuted方法在执行操作后缓存响应。
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace WebApi
{
/// <summary>
///
/// </summary>
public class IdempotentAttributeFilter : IActionFilter, IResultFilter
{
private readonly IDistributedCache _distributedCache;
private bool _isIdempotencyCache = false;
const string IdempotencyKeyHeaderName = "IdempotencyKey";
private string _idempotencyKey;
/// <summary>
///
/// </summary>
/// <param name="distributedCache"></param>
public IdempotentAttributeFilter(IDistributedCache distributedCache)
{
_distributedCache = distributedCache;
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
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;
}
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void OnResultExecuted(ResultExecutedContext context)
{
//已缓存
if (_isIdempotencyCache)
{
return;
}
var contextResult = context.Result;
DistributedCacheEntryOptions cacheOptions = new DistributedCacheEntryOptions();
//相对过期时间
//cacheOptions.SlidingExpiration = TimeSpan.FromSeconds(10);
//绝对过期时间
cacheOptions.AbsoluteExpirationRelativeToNow = new TimeSpan(24, 0, 0);
//缓存:
_distributedCache.SetString(GetDistributedCacheKey(), JsonConvert.SerializeObject(contextResult), cacheOptions);
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void OnActionExecuted(ActionExecutedContext context)
{
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void OnResultExecuting(ResultExecutingContext context)
{
}
private string GetDistributedCacheKey()
{
return "Idempotency:" + _idempotencyKey;
}
}
}
3、创建自定义Attribute
声明了IdempotentAttribute的Class或者Method,在运行时会创建IdempotentAttributeFilter。
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace WebApi
{
/// <summary>
///
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class IdempotentAttribute : Attribute, IFilterFactory
{
/// <summary>
///
/// </summary>
public bool IsReusable => false;
/// <summary>
///
/// </summary>
/// <param name="serviceProvider"></param>
/// <returns></returns>
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var distributedCache = (IDistributedCache)serviceProvider.GetService(typeof(IDistributedCache));
var filter = new IdempotentAttributeFilter(distributedCache);
return filter;
}
}
}
4、新建ASP.NET Core Web API项目
创建 WeatherForecastController 控制器,为Post方法加上【Idempotent】
这里用一个静态变量模拟数据库,POST请求写入数据,GET请求读取数据
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace WebApi.Controllers
{
/// <summary>
///
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class WeatherForecastController : ControllerBase
{
private static List<WeatherForecast> _db = new List<WeatherForecast>();
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
/// <summary>
///
/// </summary>
public WeatherForecastController()
{
}
/// <summary>
///
/// </summary>
/// <param name="temperature"></param>
/// <returns></returns>
[Idempotent]
[HttpPost]
public WeatherForecast Post(int temperature)
{
var data = new WeatherForecast { TemperatureC = temperature };
_db.Add(data);
return data;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet()]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return _db.Select(p => new WeatherForecast
{
TemperatureC = p.TemperatureC,
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}
5、注册分布式缓存
必须增加分布式缓存,用于保存幂等键的值和响应数据。
管理 NuGet 程序包(N)...
Microsoft.Extensions.Caching.SqlServer
Microsoft.Extensions.Caching.Redis
Microsoft.Extensions.Caching.StackExchangeRedis
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//分布式 SQL Server 缓存
services.AddDistributedSqlServerCache(opt =>
{
opt.ConnectionString = Configuration.GetConnectionString("DefaultConnection");
opt.SchemaName = "dbo";
opt.TableName = "sys_distributed_cache";
opt.DefaultSlidingExpiration = TimeSpan.FromMinutes(10);
opt.ExpiredItemsDeletionInterval = TimeSpan.FromMinutes(5);
});
//分布式 Redis 缓存
services.AddDistributedRedisCache(cfg =>
{
cfg.Configuration = Configuration.GetConnectionString("RedisConnection");
});
//分布式 StackExchangeRedis 缓存
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost";
options.InstanceName = "SampleInstance";
});
//分布式缓存
services.AddDistributedMemoryCache();
}
在数据库中新建一个名叫“CacheDB”的数据库,然后以管理员身份cmd运行下面指令,会创建一张名叫“CacheTable”表,相应的缓存信息都存在于这张表中。
dotnet sql-cache create <connection string> <schema> <table>
dotnet sql-cache create "Server=localhost;User=sa;Password=000000;Database=CacheDB" dbo CacheTable
成功后会提示【Table and index were created successfully】
表结构
CREATE TABLE [dbo].[CacheTable](
[Id] [nvarchar](449) NOT NULL,
[Value] [varbinary](max) NOT NULL,
[ExpiresAtTime] [datetimeoffset](7) NOT NULL,
[SlidingExpirationInSeconds] [bigint] NULL,
[AbsoluteExpiration] [datetimeoffset](7) NULL,
CONSTRAINT [pk_Id] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
CREATE NONCLUSTERED INDEX [Index_ExpiresAtTime] ON [dbo].[CacheTable]
(
[ExpiresAtTime] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
6、测试
运行Web API,使用不同IdempotencyKey执行POST请求,然后获取数据
code
POST /api/WeatherForecast?temperature=1000 HTTP/1.1
Host: localhost:8001
IdempotencyKey: 1000
*
*
边栏推荐
- odoo field 设置匿名函数domain
- npm ---- install yarn
- MySql - there is no insert, there is update or ignored
- MySql -- 不存在则插入,存在则更新或忽略
- Redis(十一) - 异步优化秒杀
- 聪明人的游戏提高篇:第三章第二课:“桐桐数”(number)
- Node installation and environment variable configuration
- Nacos安装详细过程
- [OpenCV from entry to practice] image processing technology [pixel] (the most detailed in the whole network)
- 洛谷小游戏大全(用洛谷的人都得知道)
猜你喜欢
The stock price has repeatedly hit new lows, and the real estate SaaS giant is in trouble. How should Mingyuan Cloud transform and save itself?
Write implicit join on view with jOOQ 3.14 synthetic foreign key
node安装及环境变量配置
人工神经网络
mysql高阶语句(一)
Deep learning - CNN realizes the recognition of MNIST handwritten digits
Not annotated parameter overrides @NonNullApi parameter
目标检测重要概念——IOU、感受野、空洞卷积、mAP
HCIP第十七天
oracle 远程连接数据库
随机推荐
Nacos安装详细过程
机器学习——支持向量机原理
MySQL classic 50 practice questions and the most detailed analysis of the whole network
Nacos database configuration
rhce作业
Xgboost报错ValueError:无效的形状:标签(1650 2)
MySQL高级语句(一)
zabbix邮件报警和微信报警
HCIP BGP Comprehensive Experiment Establishing peers, route reflectors, federation, route announcement and aggregation
MySQL (3)
selenium + robotframework的运行原理
Py之mlxtend:mlxtend库的简介、安装、使用方法之详细攻略
MySql 5.7.38 download and installation tutorial, and realize the operation of MySql in Navicat
Dataset:机器学习中常用数据集下载链接集合之详细攻略
NPM 安装指定版本包的方法及版本号查看
反向解析dns服务器
Node installation and environment configuration
npm 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
【OpenCV从入门到实践】图像处理技术[像素](全网最详细)
MySQL 5.7 安装教程(全步骤、保姆级教程)