【问题标题】:Using best practice for chain responsibility in ASP.NET Core在 ASP.NET Core 中使用链责任的最佳实践
【发布时间】:2020-04-22 15:58:39
【问题描述】:

我有一个登录用户的场景。如果验证返回成功消息,我编写此代码用于检查用户。

我正在使用链式责任模式进行此验证,但它看起来很难看,因为我需要更多 new 在此类中。

现在我想编写干净的最佳实践来使用这种模式。

我该怎么做?

public abstract class ValidateUser
{
    protected readonly ValidateUser _validateUser;

    public ValidateUser(ValidateUser validateUser)
    {
        _validateUser = validateUser;
    }

    public abstract UserContext ValidateUserLogin(UserContext request);
}

CheckIsActive:

public class CheckIsActive : ValidateUser
{
    public CheckIsActive(ValidateUser validateUser) : base(validateUser)
    {
    }

    public override UserContext ValidateUserLogin(UserContext request)
    {
        if (request.Context.IsActive)
        {
            return _validateUser.ValidateUserLogin(request);
        }

        return new UserContext
        {
            Message = "User Not Active"
        };
    }
}

CheckPhoneConfirmed:

public class CheckPhoneConfirmed : ValidateUser
{
    public CheckPhoneConfirmed(ValidateUser validateUser) : base(validateUser)
    {
    }

    public override UserContext ValidateUserLogin(UserContext request)
    {
        if (request.Context.ConfirmPhoneNumber)
        {
            return _validateUser.ValidateUserLogin(request);
        }

        return new UserContext
        {
            Message="Phone Number Not confirmed"
        };
    }
}

CheckIsLockedAccount:

public  class CheckIsLockedAccount : ValidateUser
{
    public CheckIsLockedAccount(ValidateUser validateUser) : base(validateUser)
    {
    }

    public override UserContext ValidateUserLogin(UserContext request)
    {
        if (!request.Context.IsLockedEnd)
        {
            return new UserContext
            {
                Context = request.Context
            };
        }

        return new UserContext
        {
            Message = $"Your account is deactivated from to date {request.Context.LockedEnd}"
        };
    }
}

我通过这种方式使用这个Validate

var validate = new CheckIsActive(new CheckPhoneConfirmed(new CheckIsLockedAccount(null)));
var validateUserContext = validate.ValidateUserLogin(new UserContext
                {
                    Context = findUSer.Result,
                    Message = null
                });

【问题讨论】:

  • 可以添加一系列中间件。
  • 没有更多信息,感觉这里有点过分了。您基本上是在尝试验证用户,这表明您应该创建单独的方法而不是具体的类。甚至您的具体类的名称也指向它们不是真正的类。如果您有一个链,其中您有一个 UserHandler 验证用户,然后将其传递给 RoleHandler 之类的东西,以计算出将其传递给 OrderHandler 或其他东西的权限,您可能会做这样的事情。
  • @sr28 你对这项工作有更好的想法吗?

标签: c# asp.net design-patterns


【解决方案1】:

您可以使用仅基于责任链模式的 .net 核心中间件管道。

app.Use(async (context, next) =>
{
    if (context.Request.HttpContext.User.HasClaim("IsLockedEnd", "true"))
    {
        await next();
    }                
});
app.Use(async (context, next) =>
{
    if (context.Request.HttpContext.User.HasClaim("ConfirmPhoneNumber", "true"))
    {
        await next();
    }
});
app.Use(async (context, next) =>
{
    if (context.Request.HttpContext.User.HasClaim("IsActive", "true"))
    {
        await next();
    }
});

【讨论】:

  • 哦,好吧,你可以使用你想要的方式,我刚刚向你展示了使用委托实现责任链模式的另一种方式,这里你不需要在这里“新建”。您只需要调用下一个委托或使管道短路。
【解决方案2】:

我觉得这种模式不适合在这里进行验证。

如果你用谷歌搜索你的模式,你会发现以下描述:

责任链是一种行为设计模式,允许 沿着潜在处理程序链传递请求,直到其中一个 他们处理请求。该模式允许处理多个对象 没有将发送者类耦合到具体类的请求 接收者

我认为您的解决方案中没有任何一种情况。因为我觉得你不想在不同的地方处理不同的验证?我认为您误用了这种模式作为装饰器模式。

请尝试以下方法:

你如何将你的对象分成以下几类:

首先您需要一个抽象类,以便稍后定义验证规则

 public abstract class ValidationRule
    {
        public string Property { get; set; }
        public string Error { get; set; }

        public ValidationRule(string property)
        {
            Property = property;
            Error = property + " is not valid";
        }

        public ValidationRule(string property, string error)
            : this(property)
        {
            Error = error;
        }

        // validation method. To be implemented in derived classes

        public abstract bool Validate(Validator validator);

        // gets value for given business object's property using reflection

        protected object GetPropertyValue(Validator validator)
        {
            // note: reflection is relatively slow
            return validator.GetType().GetProperty(Property).GetValue(validator, null);
        }
    }

然后你可以用这个类来做一个更具体的验证器。也许是一个完成的规则​​,或者你甚至可以重复使用更多的东西,例如:

public class ValidateRegex : ValidationRule
    {
        protected string Pattern { get; set; }

        public ValidateRegex(string propertyName, string pattern)
            : base(propertyName)
        {
            Pattern = pattern;
        }

        public ValidateRegex(string propertyName, string errorMessage, string pattern)
            : this(propertyName, pattern)
        {
            Error = errorMessage;
        }

        public override bool Validate(Validator validator)
        {
            return Regex.Match(GetPropertyValue(validator).ToString(), Pattern).Success;
        }
    }

然后制定最终规则

 public class ValidateEmail : ValidateRegex
    {
        public ValidateEmail(string propertyName) :
            base(propertyName, @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")
        {
            Error = propertyName + " is not a valid email address";
        }

        public ValidateEmail(string propertyName, string errorMessage) :
            this(propertyName)
        {
            Error = errorMessage;
        }
    }

验证器可能如下所示:

public abstract  class Validator
    {
        // list of business rules

        List<ValidationRule> rules = new List<ValidationRule>();

        // list of validation errors (following validation failure)

        List<string> errors = new List<string>();


        // gets list of validations errors

        public List<string> Errors
        {
            get { return errors; }
        }


        // adds a business rule to the business object

        protected void AddRule(ValidationRule rule)
        {
            rules.Add(rule);
        }

        // determines whether business rules are valid or not.
        // creates a list of validation errors when appropriate

        public bool IsValid()
        {
            bool valid = true;

            errors.Clear();

            foreach (var rule in rules)
            {
                if (!rule.Validate(this))
                {
                    valid = false;
                    errors.Add(rule.Error);
                }
            }
            return valid;
        }
    }

您现在可以使用以下验证器(请注意,当您实现许多不同的验证规则时,如果构造函数):

public class Person : Validator
    {
        public Person ()
        {
            AddRule(new ValidateEmail("Email"));

            AddRule(new ValidateId("MemberId"));

            AddRule(new ValidateRequired("Email"));
            AddRule(new ValidateLength("Email", 1, 100));


            AddRule(new ValidateRequired("CompanyName"));
            AddRule(new ValidateLength("CompanyName", 1, 40));

            AddRule(new ValidateRequired("City"));
            AddRule(new ValidateLength("City", 1, 15));

            AddRule(new ValidateRequired("Country"));
            AddRule(new ValidateLength("Country", 1, 15));
        }



        public int MemberId { get; set; }

        public string Email { get; set; }
        public string CompanyName { get; set; }
        public string City { get; set; }
        public string Country { get; set; }
        public int NumOrders { get; set; }
        public DateTime LastOrderDate { get; set; }
    }

如果你现在调用 IsValid() 方法,你的所有验证规则都会被执行。

我觉得这有点像你想要的。如果你不想将它绑定到一个对象,你可以尝试创建一个独立的验证器并将验证器组合到你需要它的类中,而不是从它派生。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-03
    • 2012-09-07
    • 1970-01-01
    • 2014-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多