65.9K
CodeProject 正在变化。 阅读更多。
Home

UrlValidator,Revalee 使用的一个项目小部件

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2014年5月23日

MIT

4分钟阅读

viewsIcon

10820

downloadIcon

83

在 Web 项目中创建和实现自定义 URL 验证器。

引言

您正在设置 ASP.NET MVC 项目,并且意识到 `web.config` 文件中的一个参数是 URL。如果您的应用程序可以直接将该参数验证为 URL,那不是很棒吗?是的,确实如此,方法如下。

背景

当我们编写(并继续编写)Revalee(一个用于调度 Web 应用程序回调的开源项目)时,我们需要编写许多内部工具或“小部件”来支持该项目。其中之一是 `UrlValidator`。这个验证器绝不是项目核心组件,但它的加入确实让我们的开发和部署过程更加轻松。

免责声明:Revalee 是我所在开发团队编写的免费开源项目。它可以在GitHub 上免费获取,并受MIT 许可证的保护。如果您有兴趣,请下载并查看。

UrlValidator

让我们看看为什么我们需要这样一个工具。例如,假设您的 `web.config` 包含一个自定义配置节,如下所示:

<revalee>
    <clientSettings serviceBaseUri="https://:46200" authorizationKey="YOUR_SECRET_KEY" />
    <recurringTasks callbackBaseUri="https://:64646">
        <task periodicity="daily" hour="00" minute="00" url="/RevaleeTest/DailyRecurring" />
        <task periodicity="hourly" minute="30" url="/RevaleeTest/HourlyRecurring" />
    </recurringTasks>
</revalee>

现在,让我们关注 `` 元素,特别是 `serviceBaseUri` 属性。仅此而已:不多也不少。

您可能会问,为什么我们要关心验证(或不验证)一个微不足道的属性呢?嗯,请考虑以下几点:作为一个不受您控制的已安装应用程序,您无法预测会为 `serviceBaseUri` 属性输入什么值。因此,现在就搞清楚细节,就不会在以后给您带来麻烦……呃,困扰您。

(需要明确的是,上面 `web.config` 的代码片段在四 (4) 个地方使用了 `UrlValidator`:一次用于 `` 元素的 `serviceBaseUri` 属性,一次用于 `` 元素的 `callbackBaseUri` 属性,以及两次定义的 `` 元素的 `url` 属性。我之所以提起这一点,只是为了强调代码重用的重要性,不过我跑题了……)

Web.config

让我们再次看看 `` 元素中的 `serviceBaseUri` 属性。

serviceBaseUri="https://:46200"

它显然应该是一个 URL:它有协议、主机,甚至在这个示例中还有端口号。我们可以将其导入为 `string`,但为什么要在两个步骤中完成(即将其导入为 `string`,然后在代码的其他地方将其转换为 `Uri`),而不是一步完成呢?我们希望在应用程序加载时,该属性就能验证为 URL,更具体地说,是作为 `Uri`。如果该属性的值不是有效的 URL,那么我们的应用程序配置错误,无法运行。就这么简单。

ConfigurationElement

既然我们从 `web.config` 文件开始,我们就从那里往里工作,这样我们最后才会处理 `UrlValidator`。这意味着我们需要定义一个自定义 `ConfigurationElement`,在此示例中称为 `ClientSettingsConfigurationElement`。这个 `class` 如下所示:

using System;
using System.ComponentModel;
using System.Configuration;

namespace Revalee.Client.Configuration
{
    internal class ClientSettingsConfigurationElement : ConfigurationElement
    {
        [ConfigurationProperty("serviceBaseUri", IsKey = false, IsRequired = false)]
        [UrlValidator(AllowAbsolute = true, AllowRelative = false)]
        public Uri ServiceBaseUri
        {
            get
            {
                return (Uri)this["serviceBaseUri"];
            }
        }

        // Other properties omitted for brevity.
        // ...
    }
}

您会注意到两个细节。第一,`property`(即 `ServiceBaseUri`)被定义为 `Uri`(并且 `this["serviceBaseUri"]` 被转换为 `Uri`)。第二,`ServiceBaseUri` 被标记了以下自定义属性(稍后详细介绍):

[UrlValidator(AllowAbsolute = true, AllowRelative = false)]

现在我们看到了如何使用这样一个自定义属性。那么,我们如何定义它呢?嗯……

ConfigurationValidatorAttribute

自定义 `UrlValidator` 属性(如上所示)包含两个属性:`AllowAbsolute` 和 `AllowRelative`。在创建自定义 `ConfigurationValidatorAttribute` 时,我们将定义这两个 `bool` 属性。

属性 类型 定义 示例
AllowAbsolute bool 允许(或禁止)使用绝对 `Uri`。 https://:46200/Absolute/Path
AllowRelative bool 允许(或禁止)使用相对 `Uri`。 /Relative/Path

由于此自定义属性的属性都是可选的(请注意,`constructor` 的签名没有参数:`public UrlValidatorAttribute()`),我们将为每个属性分配默认值 `true`。这个新的自定义 `ConfigurationValidatorAttribute` 的最终代码如下所示:

using System;
using System.Configuration;

namespace Revalee.Client.Configuration
{
    internal sealed class UrlValidatorAttribute : ConfigurationValidatorAttribute
    {
        private bool _AllowAbsolute = true;

        private bool _AllowRelative = true;

        public UrlValidatorAttribute()
        {
        }

        public bool AllowAbsolute
        {
            get
            {
                return _AllowAbsolute;
            }
            set
            {
                _AllowAbsolute = value;
            }
        }

        public bool AllowRelative
        {
            get
            {
                return _AllowRelative;
            }
            set
            {
                _AllowRelative = value;
            }
        }

        public override ConfigurationValidatorBase ValidatorInstance
        {
            get
            {
                return new UrlValidator(_AllowAbsolute, _AllowRelative);
            }
        }

        // The code for the 'private class UrlValidator' goes here (see below for complete class)
    }
}

上面,我们看到 `ConfigurationValidatorAttribute` 基类的 `ValidatorInstance` 属性已被重写,并返回一个 `type` 为 `ConfigurationValidatorBase` 的 `new UrlValidator()`。那个 `private class` 将会在注释所在的位置(上方)内联定义。相反,我们现在将单独回顾该代码。

ConfigurationValidatorBase

`UrlValidator` 中还定义了其他细节,但 `ConfigurationValidatorBase` 类的 `Validate()` 方法是所有自定义验证工作发生的地方。既然这是本次工作的核心,我们就重点关注这一点。

正如您可能已经根据本文前面奠定的基础所猜测的,我们有兴趣支持绝对和相对 URL(或仅支持其中一个)。此外,在此特定实现中,我们只对采用 `http` 或 `https` 协议的绝对 `Uri` 感兴趣;如果 `Uri` 是相对的,那么我们不必担心它的协议。所以,废话不多说,代码如下:

private class UrlValidator : ConfigurationValidatorBase
{
    private bool _AllowAbsolute = true;
    private bool _AllowRelative = true;

    public UrlValidator(bool allowAbsolute, bool allowRelative)
    {
        _AllowAbsolute = allowAbsolute;
        _AllowRelative = allowRelative;
    }

    public override bool CanValidate(Type type)
    {
        return type == typeof(Uri);
    }

    public override void Validate(object value)
    {
        if (value == null)
        {
            return;
        }

        if (value.GetType() != typeof(Uri))
        {
            throw new ArgumentException("The URL attribute is invalid.");
        }

        Uri url = value as Uri;

        if (!_AllowAbsolute && url.IsAbsoluteUri)
        {
            throw new ArgumentException("The URL attribute cannot contain an absolute URL.");
        }

        if (!_AllowRelative && !url.IsAbsoluteUri)
        {
            throw new ArgumentException("The URL attribute cannot contain a relative URL.");
        }

        if (url.IsAbsoluteUri && url.Scheme != Uri.UriSchemeHttp && url.Scheme != Uri.UriSchemeHttps)
        {
            throw new ArgumentException(string.Format("The URL attribute only supports {0} and {1}.",
                                                      Uri.UriSchemeHttp,
                                                      Uri.UriSchemeHttps)
                );
        }
    }
}

……就这样。几行简单的 `if` 语句,实际上是 `Validate()` 方法中的最后三行,这就是这个特定的 `UrlValidator` 的工作原理。也许您的会更复杂。

结论

本文回顾了如何实现 `UrlValidator` 的具体细节。在 Revalee 中,使用这个小部件使得项目设置和配置更加容易。希望它也能为您的项目带来同样的便利。

延伸阅读

历史

  • [2014.05.22] 初始发布。
© . All rights reserved.