Я пытаюсь заблокировать дубликат запроса от клиента на внутренней стороне.(Этот вопрос касается игнорирования действий поддержки на стороне клиента, таких как: отключение кнопок, отображение экрана загрузки, Google re-captcha, ...)
Вот моя идея:
- Объявите
ConcurrentDictionary<string, bool>
как синглтон. - Создайте один API [GET]
api/survey/token
для создания уникального токена на внутреннем сайте и возврата клиенту. - Сохраните вновь сгенерированный уникальныйтокен для
ConcurrentDictionary<string, bool>
.
Когда пользователь отправляет информацию
Вот моя реализация:
[Route("api/[controller]")]
public class SurveyController : ControllerBase
{
#region Properties
private readonly ConcurrentDictionary<string, DateTime> _concurrentDictionary;
#endregion
#region Constructor
public SurveyController(ConcurrentDictionary<string, DateTime> concurrentDictionary)
{
_concurrentDictionary = concurrentDictionary;
}
#endregion
#region Methods
/// <summary>
/// Generate token asynchronously.
/// </summary>
/// <returns></returns>
[HttpGet("token")]
public virtual IActionResult GetSubmissionTokenAsync()
{
var uniqueToken = Guid.NewGuid().ToString("D");
var generateUniqueTokenModel = new GenerateSurveyTokenResultViewModel();
generateUniqueTokenModel.UniqueToken = uniqueToken;
var dateTime = DateTime.UtcNow.Subtract(TimeSpan.FromMilliseconds(500));
_concurrentDictionary.TryAdd(uniqueToken, dateTime);
return Ok(generateUniqueTokenModel);
}
/// <summary>
/// Add survey asynchronously.
/// </summary>
/// <returns></returns>
[HttpPost("")]
public virtual async Task<IActionResult> AddSurveyAsync([FromBody] AddSurveyViewModel model)
{
// Validate model.
if (model == null)
{
model = new AddSurveyViewModel();
TryValidateModel(model);
}
if (!ModelState.IsValid)
return BadRequest(ModelState);
var apiMessage = new ApiMessageViewModel();
lock (_concurrentDictionary)
{
// Check whether token is registered before or not.
if (!_concurrentDictionary.TryGetValue(model.UniqueToken, out var dateTime))
{
apiMessage.Message = "INVALID_SUBMISSION_TOKEN";
return NotFound(apiMessage);
}
// Remove the registered unique token.
_concurrentDictionary.TryRemove(model.UniqueToken, out var expiredTime);
}
await Task.Run(() =>
{
Thread.Sleep(2000);
});
return Ok();
}
#endregion
}
В моей реализации я блокирую ConcurrentDictionary , когда хочу добавить уникальный токен в словарь, а также блокирую ConcurrentDictionary при удаленииуникальный токен.
Мой вопрос:
Должна ли блокировка влиять на производительность системы, когда тысячи запросов поступают на мой сервер API?
Существуют ли более эффективные способы архивации цели, которые предотвращают повторное представление формы?
**** ОБНОВЛЕНИЕ *****
У меня естьреализован атрибут с именем RequestCacheAttribute
public class RequestCacheAttribute : ActionFilterAttribute
{
#region Properties
//private readonly ConcurrentDictionary<string, DateTime> _concurrentDictionary;
#endregion
#region Constructor
#endregion
#region Methods
public override void OnActionExecuting(ActionExecutingContext context)
{
var availableTokens = (ConcurrentDictionary<string, DateTime>)context.HttpContext.RequestServices.GetService(typeof(ConcurrentDictionary<string, DateTime>));
var eTag = context.HttpContext.Request.Headers[HeaderNames.ETag];
Debug.WriteLine($"ETag = {eTag}");
if (string.IsNullOrEmpty(eTag))
{
context.Result = new NotFoundResult();
return;
}
lock (availableTokens)
{
if (!availableTokens.TryGetValue(eTag, out var dateTime))
{
context.Result = new NotFoundResult();
return;
}
availableTokens.TryRemove(eTag, out var date);
}
base.OnActionExecuting(context);
}
В этом атрибуте, прежде чем перейти к API [POST] api/survey
для создания информации опроса, я блокирую ConcurrentDictionary
, проверяю уникальный токен доступен или нет.Если доступно, разрешите пользователю войти в API и удалите уникальный токен из ConcurrentDictionary
, в противном случае запретите пользователю доступ к API.
Это плохо для производительности?
Спасибо