我想让我的API的某些方法被锁定(HttpStatus.Conflict
),直到另一个没有完成相同参数的方法(比如?id = 1& key = sd6gd0f1g5ds16fh),就好像坏用户试图制作2 +一次同样的请求,只会有一个请求。
我的想法是使用Semaphore
:
public class Lock : IDisposable
{
private bool _disposed = false;
private readonly Semaphore _semaphore;
public bool IsLocked
{
get;
private set;
}
public Lock(string name)
{
this.IsLocked = false;
try
{
this._semaphore = Semaphore.OpenExisting(name);
this._semaphore.Close();
}
catch (Exception)
{
this._semaphore = new Semaphore(0, 1, name);
this.IsLocked = true;
}
}
~Lock()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
this._semaphore.Release();
this._semaphore.Dispose();
}
this._disposed = true;
}
}
}
我正在使用它:
[ActionName("Ping")]
[HttpGet]
public IHttpActionResult Ping([FromUri]int? id = null, [FromUri]string key = null)
{
if (id == null)
{
//ProcessException is some wrap for api error answer
throw new ProcessException(HttpStatusCode.BadRequest, "Service ID is required");
}
if (key == null)
{
throw new ProcessException(HttpStatusCode.BadRequest, "Service Key is required");
}
Lock serviceLock = new Lock("service." + id + "." + key);
if (!serviceLock.IsLocked)
{
throw new ProcessException(HttpStatusCode.Conflict, "Other Service operation already in progress");
}
var service = Service.Get((int)id, key);
if (service == null) // Right hereino
{
throw new ProcessException(HttpStatusCode.Forbidden, "Service ID and/or Key is invalid");
}
Service.Touch((int)id);
serviceLock.Dispose();
//JResponse is some wrap for Dictionary<string, object>
return Ok(new JResponse(true));
}
但我对它很陌生并且有一些问题:
Dispose
时,Semaphore
仍然存在于下一个请求中。怎么了?Semaphore
释放)吗? (如上所述,如果service == null
)答案 0 :(得分:-1)
这并不完美,还有改进的余地,但我认为这可能会让你偏离另一个方向或思考方式。
使用您的信号量来锁定静态字典
//ToDo: You would have to make this ThreadSafe
public static class Helper
{
public static Dictionary<string,ClientDto> ClientDtos
= new Dictionary<string, ClientDto>();
}
public class ClientDto
{
public int ClientKey { get; set; }
public string Key { get; set; }
public DateTime CreatedOn { get; set; }
}
in your Global.asax add.
protected void Application_EndRequest()
{
Helper.ClientDtos.Remove(SeesionId);
}
//if this is called twice by the same client and the request is
//not finished processing the first request the second one will go into
//RequestBeingHandled and just return preventing the code from preforming
//the same action until the first/current is complete.
public IHttpActionResult Ping([FromUri]int? id = null, [FromUri]string key = null)
{
if(RequestBeingHandled(id, key))
{
//
Return .....
}
else
{
//if not add
ClientDto client = new ClientDto();
client.ClientKey = id;
client.Key = key;
client.CreatedOn = DateTime.Now;
Helper.ClientDtos.Add(SeesionId, client);
}
//call some code to do stuff...
}
private bool RequestBeingHandled(int id, string key)
{
//ToDo: write this code.
//check if its already in the dic
return bool;
}