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





5.00/5 (3投票s)
在 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` 属性输入什么值。因此,现在就搞清楚细节,就不会在以后给您带来麻烦……呃,困扰您。
(需要明确的是,上面 `web.config` 的代码片段在四 (4) 个地方使用了 `UrlValidator`:一次用于 `
Web.config
让我们再次看看 `
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] 初始发布。