在开发Web应用程序时,经常需要在配置文件中设置一些参数,比如URL。如果这些参数的值不正确,可能会导致应用程序运行出错。因此,实现一个URL验证器来确保这些参数是有效的URL,就显得尤为重要。本文将介绍如何在.NET应用程序中实现一个URL验证器,并展示其在实际项目中的应用。
Revalee是一个开源项目,用于为Web应用程序安排回调。在开发过程中,编写了一些内部工具或“小部件”,其中一个就是URL验证器。虽然这个验证器不是项目的核心组件,但它的加入确实让在开发和部署过程中更加轻松。
Revalee是一个免费的开源项目,由所在的开发团队编写。它在GitHub上可以免费获取,并且受到MIT许可的保护。如果感兴趣,可以下载并查看。
假设web.config文件中包含了一个自定义配置节,如下所示:
<revalee>
<clientSettings serviceBaseUri="http://localhost:46200" authorizationKey="YOUR_SECRET_KEY" />
<recurringTasks callbackBaseUri="http://localhost:64646">
<task periodicity="daily" hour="00" minute="00" url="/RevaleeTest/DailyRecurring" />
<task periodicity="hourly" minute="30" url="/RevaleeTest/HourlyRecurring" />
</recurringTasks>
</revalee>
现在让关注<clientSettings>元素,特别是serviceBaseUri属性。这个属性显然是一个URL:它有方案、主机,甚至在这个例子中还有一个端口号。可以直接将其作为Uri导入,而不是先作为字符串导入,然后在代码的其他地方将其转换为Uri。希望在应用程序加载时,该属性的值能够作为一个有效的URL进行验证,更具体地说,作为一个Uri。如果该属性的值不是一个有效的URL,那么应用程序就配置错误,无法运行。
既然从web.config文件开始,让从那里开始工作,最后再介绍UrlValidator。这意味着需要定义一个自定义的ConfigurationElement,在这个例子中叫做ClientSettingsConfigurationElement。这个类看起来像这样:
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"];
}
}
// 其他属性省略。
}
}
会注意到两个细节。首先,属性(即ServiceBaseUri)被定义为Uri(并且this["serviceBaseUri"]被转换为Uri)。其次,ServiceBaseUri被标记了以下自定义属性(稍后会详细介绍):
[UrlValidator(AllowAbsolute = true, AllowRelative = false)]
现在看到了如何使用这样的自定义属性。如何定义它呢?好吧...
自定义UrlValidator属性(如上所示)包括两个属性:AllowAbsolute和AllowRelative。现在定义这两个bool属性,当创建自定义ConfigurationValidatorAttribute时。
属性类型定义示例
属性 | 类型 | 定义 | 示例 |
---|---|---|---|
AllowAbsolute | bool | 允许(或禁止)使用绝对Uri | http://localhost:46200/Absolute/Path |
AllowRelative | bool | 允许(或禁止)使用相对Uri | /Relative/Path |
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);
}
}
// 'private class UrlValidator'的代码在这里定义(见下文完整类)
}
}
在上面的代码中,看到了ConfigurationValidatorAttribute基类的ValidatorInstance属性被覆盖,并返回了一个新的UrlValidator()类型: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的精髓。也许会更复杂。