在传统MVC中实现异步AuthorizeAttribute

在.NET Core中,async/await 已经得到原生支持,但在.NET Framework的MVC4中,这并不是一个内置的功能。尽管如此,仍然可以通过一些技巧来实现异步代码的同步执行。本文将介绍如何在不升级到.NET Core的情况下,使用异步代码编写AuthorizeAttribute。

异步方法的同步执行

在.NET Framework中,异步方法的同步执行通常需要借助一些辅助类。例如,Chris McKee在GitHub上分享了一个名为AsyncHelpers的类,该类可以帮助实现这一功能。以下是该类的一个实现示例:

using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Web; namespace GordonBeeming.ApiHelpers { public static class AsyncHelpers { public static void RunSync(Func task) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); synch.Post(async _ => { try { await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); } public static T RunSync(Func> task) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); T ret = default(T); synch.Post(async _ => { try { ret = await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); return ret; } private class ExclusiveSynchronizationContext : SynchronizationContext { private bool done; public Exception InnerException { get; set; } readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false); readonly Queue> items = new Queue>(); public override void Send(SendOrPostCallback d, object state) { throw new NotSupportedException("We cannot send to our same thread"); } public override void Post(SendOrPostCallback d, object state) { lock (items) { items.Enqueue(Tuple.Create(d, state)); } workItemsWaiting.Set(); } public void EndMessageLoop() { Post(_ => done = true, null); } public void BeginMessageLoop() { while (!done) { Tuple task = null; lock (items) { if (items.Count > 0) { task = items.Dequeue(); } } if (task != null) { task.Item1(task.Item2); if (InnerException != null) { throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException); } } else { workItemsWaiting.WaitOne(); } } } public override SynchronizationContext CreateCopy() { return this; } } } }

通过这个辅助类,可以轻松地将异步代码包装成同步执行。例如:

AsyncHelpers.RunSync(MyMethodAsync);

接下来,将探讨本文的核心内容——创建异步AuthorizeAttribute。

创建异步AuthorizeAttribute

值得注意的是,上述代码不仅适用于AuthorizeAttribute,它在任何需要异步代码的场景中都可以使用。在标准的OnAuthorization方法中,通过调用异步的OnAuthorization方法,并将所有逻辑放在其中,以保持代码的清晰性。

using nologo.Chassis.Part.Identity; using nologo.Common.Core; using System; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http.Controllers; using System.Web.Mvc; namespace GordonBeeming.Attributes { public class AuthorizeAsyncAttribute : AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext filterContext) { AsyncHelpers.RunSync(() => OnAuthorizationAsync(filterContext)); } public async Task OnAuthorizationAsync(AuthorizationContext filterContext) { var profile = await ProfileHelper.GetFromApi(); // 根据profile执行一些操作 } public int AllowedRole { get; set; } public int[] AllowedRoles { get; set; } } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485