在C#中验证URL的更好方法比try-catch更好吗?

时间:2010-07-12 13:29:36

标签: c# image url try-catch

我正在构建一个从互联网上检索图像的应用程序。即使它工作正常,但在应用程序中使用try-catch语句时速度很慢(在错误的给定URL上)。

(1)这是验证URL和处理错误输入的最佳方法 - 还是应该使用正则表达式(或其他方法)?

(2)如果我没有在textBox中指定http://,为什么应用程序会尝试在本地查找图像?

private void btnGetImage_Click(object sender, EventArgs e)
{
    String url = tbxImageURL.Text;
    byte[] imageData = new byte[1];

    using (WebClient client = new WebClient())
    {
        try
        {
            imageData = client.DownloadData(url);
            using (MemoryStream ms = new MemoryStream(imageData))
            {
                try
                {
                    Image image = Image.FromStream(ms);
                    pbxUrlImage.Image = image;
                }
                catch (ArgumentException)
                {
                    MessageBox.Show("Specified image URL had no match", 
                        "Image Not Found", MessageBoxButtons.OK, 
                        MessageBoxIcon.Error);
                }
            }
        }
        catch (ArgumentException)
        {
            MessageBox.Show("Image URL can not be an empty string", 
                "Empty Field", MessageBoxButtons.OK, 
                MessageBoxIcon.Information);
        }
        catch (WebException)
        {
            MessageBox.Show("Image URL is invalid.\nStart with http:// " +
                "and end with\na proper image extension", "Not a valid URL",
                MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    } // end of outer using statement
} // end of btnGetImage_Click

修改 我尝试了Panagiotis Kanavos建议的解决方案(谢谢你的努力!),但只有当用户输入http://时才会被if-else语句捕获。更改为UriKind.Absolute也会捕获空字符串!越来越近 :) 截至目前的代码:

private void btnGetImage_Click(object sender, EventArgs e)
{
    String url = tbxImageURL.Text;
    byte[] imageData = new byte[1];
    Uri myUri;

    // changed to UriKind.Absolute to catch empty string
    if (Uri.TryCreate(url, UriKind.Absolute, out myUri))
    {
        using (WebClient client = new WebClient())
        {
            try
            {
                imageData = client.DownloadData(myUri);
                using (MemoryStream ms = new MemoryStream(imageData))
                {
                    imageData = client.DownloadData(myUri);
                    Image image = Image.FromStream(ms);
                    pbxUrlImage.Image = image;
                }
            }
            catch (ArgumentException)
            {
                MessageBox.Show("Specified image URL had no match",
                    "Image Not Found", MessageBoxButtons.OK, 
                    MessageBoxIcon.Error);
            }
            catch (WebException)
            {
                MessageBox.Show("Image URL is invalid.\nStart with http:// " +
                    "and end with\na proper image extension", 
                    "Not a valid URL",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
    }
    else
    {
        MessageBox.Show("The Image Uri is invalid.\nStart with http:// " +
            "and end with\na proper image extension", "Uri was not created",
            MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

我必须在这里做错事。 :(

10 个答案:

答案 0 :(得分:108)

仅当您的网址字符串是有效网址时,才使用Uri.TryCreate创建新的Uri对象。如果字符串不是有效的URL,TryCreate将返回false。

string myString = "http://someUrl";
Uri myUri;
if (Uri.TryCreate(myString, UriKind.RelativeOrAbsolute, out myUri))
{
    //use the uri here
}

<强>更新

TryCreate或Uri构造函数将很乐意接受可能看似无效的字符串,例如“Host:www.stackoverflow.com”,“Host:%20www.stackoverflow.com”或“chrome:about”。实际上,这些是完全有效的URI,它们指定自定义方案而不是“http”。

Uri.Scheme属性的文档提供了更多示例,例如“gopher:”(有人记得吗?),“news”,“mailto”,“uuid”。

应用程序可以将自己注册为MSDN中描述的自定义协议处理程序或其他SO问题,例如How do I register a custom URL protocol in Windows?

TryCreate不提供将自身限制为特定方案的方法。代码需要检查Uri.Scheme属性以确保它包含可接受的值

更新2

传递像"></script><script>alert(9)</script>这样奇怪的字符串将返回true并构造一个相对的Uri对象。但是,调用Uri.IsWellFormedOriginalString将返回false。所以如果你想确保相对的Uris形成良好,你可能需要调用IsWellFormedOriginalString

另一方面,在这种情况下,使用TryCreate调用UriKind.Absolute将返回false。

有趣的是,Uri.IsWellFormedUriString在内部调用TryCreate,如果创建了相对Uri则返回IsWellFormedOriginalString的值。

答案 1 :(得分:52)

快捷方式是使用Uri.IsWellFormedUriString

if (Uri.IsWellFormedUriString(myURL, UriKind.RelativeOrAbsolute))
...

答案 2 :(得分:5)

使用Uri测试有效网址时的一些示例

Uri myUri = null;
if (Uri.TryCreate("Host: www.stackoverflow.com", UriKind.Absolute, out myUri))
{
}

  myUri = null;
if (Uri.TryCreate("Accept: application/json, text/javascript, */*; q=0.01", UriKind.Absolute, out myUri))
{
}

  myUri = null;
if (Uri.TryCreate("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0", UriKind.Absolute, out myUri))
{
}

  myUri = null;
if (Uri.TryCreate("DNT: 1", UriKind.Absolute, out myUri))
{
}

在使用上述内容验证后,我很惊讶地将所有这些废话都出现在我的列表视图中。但这一切都通过了验证测试。

现在我在上述验证后添加以下内容

url = url.ToLower();
if (url.StartsWith("http://") || url.StartsWith("https://")) return true;

答案 3 :(得分:4)

嗨,您验证https http,ftp,sftp,ftps,以www开头的任何内容。

string regular = @"^(ht|f|sf)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*)?$";
string regular123 = @"^(www.)[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*)?$";

string myString = textBox1.Text.Trim();
if (Regex.IsMatch(myString, regular))
{
    MessageBox.Show("It is valide url  " + myString);
}
else if (Regex.IsMatch(myString, regular123))
{
    MessageBox.Show("Valide url with www. " + myString);
}
else 
{
    MessageBox.Show("InValide URL  " + myString);
}

答案 4 :(得分:3)

或者这个源代码良好的图像有效优化:

 public static string ValidateImage(string absoluteUrl,string defaultUrl)
        { 
           Uri myUri=null; 
           if (Uri.TryCreate(absoluteUrl, UriKind.Absolute, out myUri))
            {
                using (WebClient client = new WebClient())
                {
                    try
                    {
                        using (Stream stream = client.OpenRead(myUri))
                        {
                            Image image = Image.FromStream(stream);
                            return (image != null) ? absoluteUrl : defaultUrl;
                        }
                    }
                    catch (ArgumentException)
                    {
                        return defaultUrl;
                    }
                    catch (WebException)
                    {
                        return defaultUrl;
                    }
                }
            }
            else
            {
                return defaultUrl;
            }
        }

创建的asp和demo asp.net mvc源图像:

<img src="@ValidateImage("http://example.com/demo.jpg","nophoto.png")"/>

答案 5 :(得分:3)

我的解决方案:

string regular = @"^(ht|f|sf)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*)?$";
string myString = textBox1.Text.Trim();
if (Regex.IsMatch(myString, regular))
{
    MessageBox.Show("it is valide url  " + myString);
}
else
{
    MessageBox.Show("InValide url  " + myString);
}

答案 6 :(得分:3)

使用它.....

string myString = http//:google.com;
Uri myUri;
Uri.TryCreate(myString, UriKind.RelativeOrAbsolute, out myUri);
 if (myUri.IsAbsoluteUri == false)
 {
  MessageBox.Show("Please Input Valid Feed Url");
 }

答案 7 :(得分:2)

如果您想测试并创建网址,可以使用Uri.TryCreate作为Panagiotis Kanavos建议的功能,或者如果您只是Todd Menier建议的话,可以使用Uri.IsWellFormedUriString功能想测试一下Url的有效性。如果您现在只是验证用户输入并且需要在应用程序的生命周期中的某个时间创建URL,这可以很方便。

**但我的帖子是给人民的,就像我一样:(,仍然对着.net 1.1 **

以上两种方法都是在.net 2.0中引入的,所以你们仍然必须使用try catch方法,在我看来,这种方法仍然比使用正则表达式要好得多。

private bool IsValidHTTPURL(string url)
{
    bool result = false;

    try
    {
        Uri uri = new Uri(url);

        result = (uri.Scheme == "http" || uri.Scheme == "https");
    }
    catch (Exception ex) 
    { 
        log.Error("Exception while validating url", ex); 
    }

    return result;
}

答案 8 :(得分:1)

我想检查网址是否也包含域扩展名,它必须是有效的网站网址。

这就是我提出的:

 public static bool IsValidUrl(string url)
        {
            if (string.IsNullOrEmpty(url)) { return false;}

            if (!url.StartsWith("http://"))
            {
                url = "http://" + url;    
            }

            Uri outWebsite;

            return Uri.TryCreate(url, UriKind.Absolute, out outWebsite) && outWebsite.Host.Replace("www.", "").Split('.').Count() > 1 && outWebsite.HostNameType == UriHostNameType.Dns && outWebsite.Host.Length > outWebsite.Host.LastIndexOf(".") + 1 && 255 >= url.Length;
        }

我用linqpad测试了代码:

    void Main()
{
        // Errors
        IsValidUrl("www.google/cookie.png").Dump();
        IsValidUrl("1234").Dump();
        IsValidUrl("abcdef").Dump();
        IsValidUrl("abcdef/test.png").Dump();
        IsValidUrl("www.org").Dump();
        IsValidUrl("google").Dump();
        IsValidUrl("google.").Dump();
        IsValidUrl("google/test").Dump();
        IsValidUrl("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0").Dump();
        IsValidUrl("</script><script>alert(9)</script>").Dump();
        IsValidUrl("Accept: application/json, text/javascript, */*; q=0.01").Dump();
        IsValidUrl("DNT: 1").Dump();

        Environment.NewLine.Dump();

        // Success
        IsValidUrl("google.nl").Dump();
        IsValidUrl("www.google.nl").Dump();
        IsValidUrl("http://google.nl").Dump();
        IsValidUrl("http://www.google.nl").Dump();
}

结果:

  

假假假假假假假假假假   假

     

真实真实

答案 9 :(得分:1)

我遇到了一个非常相似的案例,所以我编写了一个静态类,该类可以轻松地与xUnit测试一起使用,以验证逻辑是否通过了几种情况。

用法(返回ValidationModel

var message = UrlValidator.Validate(input).ValidationMessage;

var result = UrlValidator.Validate(input).IsValid;

ValidationModel.cs

    public class ValidationModel
    {
        public const string InvalidScheme = "Invalid URI scheme.";
        public const string EmptyInputValue = "Empty input value.";
        public const string InvalidUriFormat = "Invalid URI format.";
        public const string PassedValidation = "Passed validation";
        public const string HttpScheme = "http://";
        public const string HttpsScheme = "https://";

        public bool IsValid { get; set; }
        public string ValidationMessage { get; set; }

    }

UrlValidator.cs

    public static class UrlValidator
    {
        public static ValidationModel Validate(string input)
        {
            var validation = new ValidationModel();

            if (input == string.Empty)
            {
                validation.IsValid = false;
                validation.ValidationMessage = ValidationModel.EmptyInputValue;
                return validation;
            }

            try
            {
                var uri = new Uri(input);
                var leftPart = uri.GetLeftPart(UriPartial.Scheme);

                if (leftPart.Equals(ValidationModel.HttpScheme) || leftPart.Equals(ValidationModel.HttpsScheme))
                {
                    validation.IsValid = true;
                    validation.ValidationMessage = ValidationModel.PassedValidation;
                    return validation;
                }

                validation.IsValid = false;
                validation.ValidationMessage = ValidationModel.InvalidScheme;
            }
            catch (UriFormatException)
            {
                validation.IsValid = false;
                validation.ValidationMessage = ValidationModel.InvalidUriFormat;
            }

            return validation;
        }
    }

UrlValidatorTests.cs

    public class UrlValidatorTests
    {
        [Theory]
        [InlineData("http://intel.com", true, ValidationModel.PassedValidation)]
        [InlineData("https://intel.com", true, ValidationModel.PassedValidation)]
        [InlineData("https://intel.com/index.html", true, ValidationModel.PassedValidation)]
        [InlineData("", false, ValidationModel.EmptyInputValue)]
        [InlineData("http://", false, ValidationModel.InvalidUriFormat)]
        [InlineData("//intel.com", false, ValidationModel.InvalidScheme)]
        [InlineData("://intel.com", false, ValidationModel.InvalidUriFormat)]
        [InlineData("f://intel.com", false, ValidationModel.InvalidScheme)]
        [InlineData("htttp://intel.com", false, ValidationModel.InvalidScheme)]
        [InlineData("intel.com", false, ValidationModel.InvalidUriFormat)]
        [InlineData("ftp://intel.com", false, ValidationModel.InvalidScheme)]
        [InlineData("http:intel.com", false, ValidationModel.InvalidUriFormat)]
        public void Validate_Input_ExpectedResult(string input, bool expectedResult, string expectedInvalidMessage)
        {
            //Act
            var result = UrlValidator.Validate(input);

            //Assert
            Assert.Equal(expectedResult, result.IsValid);
            Assert.Equal(expectedInvalidMessage, result.ValidationMessage);
        }
    }

测试结果:

test results