zoukankan      html  css  js  c++  java
  • [译]基于请求的依赖注入

    原文

    最近有读者问我有没有可能创建一个基于请求的依赖注入。实际上,他想基于不同的登陆用户提供不同的依赖注入。

    这是个好问题 - 事实上这是一个特别好的依赖注入的使用用例。依赖注入能让我们在整个应用使用某个对象,而不需要关心它是怎么被创建的。

    例子

    让我们来看看一个简单的例子。想象下我要为我的网站创建一个后台。其中提供一个简单功能:根据name来删除一个用户。 接口如下:

    public interface IControlPanel
    {
        string DeleteUser(string name);
    }
    

    在实现这个接口前,我们来说说删除一个用户的业务规则:

    • 游客不能删除任何用户
    • 普通用户只能删除自己
    • 管理员可以删除任何用户

    实现

    来看看怎么实现上面的业务规则。为了简化, 我们决定根据请求头中的loggedInUser来判断登陆用户。

    public class ControlPanel : IControlPanel
    {
        private readonly IHttpContextAccessor _contextAccessor;
     
        public ControlPanel(IHttpContextAccessor contextAccessor)
        {
            _contextAccessor = contextAccessor;
        }
     
        public string DeleteUser(string name)
        {
            var currentUser = _contextAccessor.HttpContext.Request.Headers["loggedInUser"];
            // No user logged in: guest
            if (string.IsNullOrEmpty(currentUser))
            {
                return "Guests cannot delete users.";
            }
            // Administrator logged in
            if ("admin" == currentUser)
            {
                return $"Deleted the user {name} as Administrator";
            }
            // Normal user logged in
            if (name == currentUser)
            {
                return $"Deleted user {name} as {currentUser}";
            }
            return $"Cannot delete {name} because {currentUser} is a regular user";
        }
    }
    

    配置服务

    现在我们有了一个服务实现了,现在将它添加到service collection中去,并且创建一个删除的用户的接口:

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IControlPanel, ControlPanel>();
        }
     
        public void Configure(IApplicationBuilder app)
        {
            app.UseIISPlatformHandler();
     
            app.Map("/panel/delete", DeleteUserApi);
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Welcome to the demo.");
            });
        }
     
        public void DeleteUserApi(IApplicationBuilder app)
        {
            app.Run(async (context) =>
            {
                var controlPanel = context.RequestServices.GetRequiredService<IControlPanel>();
                var userToDelete = context.Request.Query["user"];
                await context.Response.WriteAsync(controlPanel.DeleteUser(userToDelete));
            });
        }
     
        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }
    

    测试

    现在使用curl来测试:

    $ curl -s localhost:49480/panel/delete?user=joe
    Guests cannot delete users.
    $ curl -s -H "loggedInUser: admin" localhost:49480/panel/delete?user=joe
    Deleted the user joe as Administrator
    $ curl -s -H "loggedInUser: bob" localhost:49480/panel/delete?user=joe
    Cannot delete joe because bob is a regular user
    $ curl -s -H "loggedInUser: joe" localhost:49480/panel/delete?user=joe
    Deleted user joe as joe
    

    结果和我们预期的一样。

    现在不同的用户类型的不同业务规则都包含在ControlPanel类中。 我们可以想象下如果又多了些不同的用户类型, ControlPanel类会变的越来越大,越来越复杂。

    实现多个IControlPanel

    另外一个更好的解决方案是根据请求提供不同的IControlPanel实现。 首先, 我们可以将上面的ControlPanel分割成3个不同的类 - AdminControlPanel,NormalUserControlPanel,GuestControlPanel:

    public class AdminControlPanel : IControlPanel
    {
        public string DeleteUser(string name)
        {
            return $"Deleted the user {name} as Administrator";
        }
    }
     
    public class NormalUserControlPanel : IControlPanel
    {
        private readonly string _name;
     
        public NormalUserControlPanel(string name)
        {
            _name = name;
        }
     
        public string DeleteUser(string name)
        {
            // Regular users can only delete themselves
            if (name == _name)
            {
                return $"Deleted user {name} as {_name}";
            }
            return $"Cannot delete {name} because {_name} is a regular user";
        }
    }
     
    public class GuestControlPanel : IControlPanel
    {
        public string DeleteUser(string name)
        {
            return "Guests cannot delete users.";
        }
    }
    

    可以看到,每一个IControlPanel的实现都仅包含某一个用户类型的业务逻辑。 每个类都比之前的ControlPanel简单。

    实时依赖注入

    现在到了最核心最重要的部分了。我们更新ConfigureServices方法根据request提供不同的IControlPanel实现:

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
                services.AddTransient<IControlPanel>(serviceProvider =>
                {
                    var context = serviceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext;
                    var userHeader = context.Request.Headers["loggedInUser"];
                    if (string.IsNullOrEmpty(userHeader)) return new GuestControlPanel();
                    if ("admin" == userHeader) return new AdminControlPanel();
                    return new NormalUserControlPanel(userHeader);
                });
            }
     
        public void Configure(IApplicationBuilder app)
        {
            app.UseIISPlatformHandler();
     
            app.Map("/panel/delete", DeleteUserApi);
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Welcome to the demo.");
            });
        }
     
        public void DeleteUserApi(IApplicationBuilder app)
        {
            app.Run(async (context) =>
            {
                var controlPanel = context.RequestServices.GetRequiredService<IControlPanel>();
                var userToDelete = context.Request.Query["user"];
                await context.Response.WriteAsync(controlPanel.DeleteUser(userToDelete));
            });
        }
     
        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }
    

    再次运行程序,结果和之前的结果一样:

    $ curl -s localhost:49480/panel/delete?user=joe
    Guests cannot delete users.
    $ curl -s -H "loggedInUser: admin" localhost:49480/panel/delete?user=joe
    Deleted the user joe as Administrator
    $ curl -s -H "loggedInUser: bob" localhost:49480/panel/delete?user=joe
    Cannot delete joe because bob is a regular user
    $ curl -s -H "loggedInUser: joe" localhost:49480/panel/delete?user=joe
    Deleted user joe as joe
    
  • 相关阅读:
    mysql安装脚本
    vim常用命令
    CentOS 6.5使用国内网易163的源
    053(七十五)
    053(七十四)
    053(七十三)
    053(七十二)
    053(七十一)
    053(七十)
    053(六十九)
  • 原文地址:https://www.cnblogs.com/irocker/p/asp-net-core-custom-service-based-on-request.html
Copyright © 2011-2022 走看看