【问题标题】:How do I implement AOP in an Azure Mobile App Services client?如何在 Azure 移动应用服务客户端中实现 AOP?
【发布时间】:2016-07-17 16:24:16
【问题描述】:

在使用 MVC 5、Web API 2.0 和 EF Core 1.0 的 Azure 移动应用服务服务器端应用上,可以像这样装饰控制器以实现基于令牌的身份验证:

// Server-side EF Core 1.0 / Web API 2 REST API
[Authorize]
public class TodoItemController : TableController<TodoItem>
{
  protected override void Initialize(HttpControllerContext controllerContext)
  {
     base.Initialize(controllerContext);

     DomainManager = new EntityDomainManager<TodoItem>(context, Request);
  }

  // GET tables/TodoItem
  public IQueryable<TodoItem> GetAllTodoItems()
  {
     return Query();
  }
  ...
}

我希望能够在客户端做类似的事情,我从上面用 [Authorize] 之类的东西装饰一个方法,也许用下面的 [Secured] 装饰:

public class TodoItem
{
    string id;
    string name;
    bool done;

    [JsonProperty(PropertyName = "id")]
    public string Id
    {
        get { return id; }
        set { id = value;}
    }

    [JsonProperty(PropertyName = "text")]
    public string Name
    {
        get { return name; }
        set { name = value;}
    }

    [JsonProperty(PropertyName = "complete")]
    public bool Done
    {
        get { return done; }
        set { done = value;}
    }

    [Version]
    public string Version { get; set; }
}

// Client side code calling GetAllTodoItems from above
[Secured]
public async Task<ObservableCollection<TodoItem>> GetTodoItemsAsync()
{
    try
    {
        IEnumerable<TodoItem> items = await todoTable
            .Where(todoItem => !todoItem.Done)
            .ToEnumerableAsync();

        return new ObservableCollection<TodoItem>(items);
    }
    catch (MobileServiceInvalidOperationException msioe)
    {
        Debug.WriteLine(@"Invalid sync operation: {0}", msioe. 
    }
    catch (Exception e)
    {
        Debug.WriteLine(@"Sync error: {0}", e.Message);
    }

    return null;
}

[Secured] 的定义可能如下所示:

public class SecuredFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // Check if user is logged in, if not, redirect to the login page.
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // Check some globally accessible member to see if user is logged out.
    }
}

不幸的是,根据微软关于“创建自定义操作过滤器”的文章:https://msdn.microsoft.com/en-us/library/dd381609(v=vs.100).aspx

如何实现类似于“自定义操作过滤器”的功能,允许我在移动应用服务客户端而不是服务器中使用“[Secured]”装饰?答案将帮助我从客户端创建自定义身份验证并将代码保存在一个位置,而不会使实现复杂化,即,它是一个横切关注点,如性能指标、重复尝试的自定义执行计划、日志记录等。

使场景复杂化的是,客户端还实现了适用于 iOS 的 Xamarin.Forms,并且由于 iOS 对本机代码的要求,它必须是功能性的 Ahead of Time 模式,JIT 尚不可行。

【问题讨论】:

  • 您是否考虑过根据自己的需要扩展自定义授权???
  • 我不知道该怎么做。 “扩展自定义授权”是什么意思?这是客户端,所以我假设您的意思是 LoginAsync。

标签: c# xamarin aop custom-attributes azure-app-service-envrmnt


【解决方案1】:

属性在您描述的场景中起作用的原因是因为其他代码负责实际调用方法或读取属性,而其他代码将查找属性并相应地修改行为。当您只是在运行 C# 代码时,通常不会得到它;没有一种本地方法可以在执行方法之前执行属性中的代码。

从您所描述的情况来看,听起来您正在追求面向方面的编程。有关框架列表,请参阅 What is the best implementation for AOP in .Net?

本质上,使用适当的 AOP 框架,您可以添加属性或其他标记,并在编译时执行或插入代码。有很多方法可以解决它,因此为什么我不是很具体,抱歉。 您确实需要了解 AOP 方法与 ASP.Net MVC 之类的工作方式不同,因为 AOP 通常会修改您的运行时代码(无论如何,在我的理解中,我确信这方面也会有变化)。

至于 AOP 是否真的可行取决于您的要求,但我会谨慎行事 - 这不适合胆小的人。

这个问题的一个完全替代解决方案是查看Mediatr 或类似的东西,将您的逻辑分解为一组命令,您可以通过消息总线调用这些命令。有帮助的原因是您可以使用各种类型的逻辑(包括授权逻辑)来装饰您的消息总线(或管道)。该解决方案与您要求的解决方案非常不同 - 但无论如何可能更可取。

或者只是在每个方法的第一行添加一个单行授权调用,而不是作为一个属性......

【讨论】:

  • 我希望将客户端方法包装在一个 有点 通用 try / catch 中,它将捕获 MobileServiceInvalidOperationException,然后在自定义执行计划中重试执行(即 3 次尝试在 UI 中显示错误之前点击 Azure)。该解决方案使用 Xamarin.Forms 来实现跨平台兼容性,因此我也仅限于此。我会考虑你的建议,谢谢。
  • 我对这个问题采取的一种方法是静态方法,它接受一个 lambda,然后尝试执行该 lambda,重试和捕获异常等。你仍然必须实际使用该方法,但它节拍重复它。但是,mediatr 实际上可能是一个不错的选择,因为您可以使用管道上的装饰器来处理它。
  • 看起来 PostSharp 支持 Xamarin(和 IOS),这里有一个链接指向某人正在寻找我正在尝试做的事情:forums.xamarin.com/discussion/34976/…
  • 不幸的是 PostSharp 是一个商业产品
【解决方案2】:

您通过几个不同的名称/术语来更广泛地描述的内容。首先想到的是“面向方面的编程”(或简称 AOP)。它处理所谓的cross cutting concerns。我敢打赌你想做两件事中的一件

  1. 以标准化的有意义的方式记录异常/消息
  2. 记录系统区域的时间/性能

在一般意义上,是的,C# 能够做这样的事情。网上会有无数关于如何做到这一点的教程,以这种方式回答太宽泛了。

然而,asp.net MVC 的作者对这些东西有很多思考,并为您提供了许多属性,正如您所描述的那样,可以随意扩展,并提供对管道的轻松访问,为开发人员提供他们需要的所有信息(例如当前路由、任何参数、任何异常、任何授权/身份验证请求等)

这是一个很好的起点:http://www.strathweb.com/2015/06/action-filters-service-filters-type-filters-asp-net-5-mvc-6/

这看起来也不错:http://www.dotnetcurry.com/aspnet-mvc/976/aspnet-mvc-custom-action-filter

【讨论】:

  • 这些想法很棒,我可以将其用于日志和性能指标。目前,我的目的是用于客户端自定义身份验证(尝试/捕获并检查 HTTP 未经授权,然后在必要时显示登录模式)。不幸的是,Azure 移动应用服务中用于授权的 AOP 内容似乎适用于服务器端的 MVC 5+,并且仅内置于客户端的 LoginAsync(不是自定义身份验证)中。我正在尝试在未使用 MVC 5+ 的客户端构建此包装器,因此我没有对 LoginAsync 的“未知”和移动目标依赖。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-03
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多