4

I have a model with a text field. The text can contain several URLs. It does not have to contain URLs and it does not have a specific format.

Using

@Html.DisplayFor(model => model.TextWithSomeUrls)

the text and the URLs are displayed like normal text of course. I would like to get the URLs displayed as working individual links though. Is there a helper method for this in ASP.NET / Razor?

Edit: Right now the output is:

http://www.google.com, foo: bar;  http://www.yahoo.com

Which is exactly the content of the text field.

But I want to get the URLs and only the URLs rendered as links like this:

<a href="http://www.google.com">http://www.google.com</a>, foo: bar; <a href="http://www.yahoo.com">http://www.yahoo.com</a>

My solution:

public static partial class HtmlExtensions
{
    private const string urlRegEx = @"((http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)";

    public static MvcHtmlString DisplayWithLinksFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        string content = GetContent<TModel, TProperty>(htmlHelper, expression);
        string result = ReplaceUrlsWithLinks(content);
        return MvcHtmlString.Create(result);
    }

    private static string ReplaceUrlsWithLinks(string input)
    {
        Regex rx = new Regex(urlRegEx);
        string result = rx.Replace(input, delegate(Match match)
        {
            string url = match.ToString();
            return String.Format("<a href=\"{0}\">{0}</a>", url);
        });
        return result;
    }

    private static string GetContent<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        Func<TModel, TProperty> func = expression.Compile();
        return func(htmlHelper.ViewData.Model).ToString();
    }
}

This extension can now be used in views:

@Html.DisplayWithLinksFor(model => model.FooBar)
2
  • Could you post an example of the output, please? Commented Oct 7, 2013 at 9:28
  • While that solution correctly replaces URL with links, it does not HTML-encode the contents of the field. Commented Sep 9, 2014 at 13:56

4 Answers 4

4

I had some issues with the solution:

  1. It did not work for hostnames without dots, like localhost or any other LAN-URL
  2. It did not work for URLs with spaces (minor issue)
  3. It did not encode all the rest of my data. So if there is "< !--" in the database, the page would become truncated.
  4. The URI is not escaped

I used the code from above, extended it a bit and ended up with this:

private static readonly Regex urlRegEx = new Regex(@"(?<!="")((http|ftp|https|file):\/\/[\d\w\-_]+(\.[\w\-_]+)*([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)");
private static readonly Regex quotedUrlRegEx = new Regex(@"(?<!=)([""']|&quot;|&#39;)((http|ftp|https|file):\/\/[\d\w\-_]+(\.[\w\-_]+)*([\w\-\.,@?^=%&amp;:/~\+# ])*)\1");

public static MvcHtmlString DisplayWithLinksFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression,
    string templateName = null)
{
    var encodedHTML = htmlHelper.DisplayFor(expression, templateName);
    return MvcHtmlString.Create(ReplaceUrlsWithLinks(encodedHTML.ToHtmlString()));
}
private static string ReplaceUrlsWithLinks(string input)
{
    input = input.Replace(@"\\", @"file://").Replace('\\', '/');
    var result = quotedUrlRegEx.Replace(input, delegate(Match match)
    {
        string url = match.Groups[2].Value;
        return String.Format("<a href=\"{0}\">{1}</a>", Uri.EscapeUriString(url), ShortenURL(url));
    });
    return urlRegEx.Replace(result, delegate(Match match)
    {
        string url = match.ToString();
        return String.Format("<a href=\"{0}\">{1}</a>", Uri.EscapeUriString(url), ShortenURL(url));
    });
}
private static string ShortenURL(string url)
{
    url = url.Substring(url.IndexOf("//", StringComparison.Ordinal) + 2);
    if (url.Length < 60)
        return url;
    var host = url.Substring(0, url.IndexOf("/", StringComparison.Ordinal));
    return host + "/&hellip;";
}

Obviously not 100% tested for all URL schemes, but seems to work alright.

Example Input:

"\\02lanpc\abc\def\Bugs in the database.docx"
http://localhost:81/applications/2/?releaseNumber=1.1&buildNumber=2

Output:

<a href="file://02lanpc/abc/def/Bugs%20in%20the%20database.docx">02lanpc/abc/def/Bugs in the database.docx</a>

<a href="http://localhost:81/applications/2/?releaseNumber=1.1&amp;buildNumber=2">localhost:81/&hellip;</a>
Sign up to request clarification or add additional context in comments.

Comments

2

There is no helper like that, but you can create your own custom helper or create a template for DisplayFor helper, which will contain logic you need.

Comments

1

Try to write your own Html Helper, like the following.

public static string Urls(this HtmlHelper helper, string value)
{  
    var items = value.Split(';'); // use your delimiter
    var sb = new StringBuilder();
    foreach(var i in items)
    {
        if(IsUrl(i)) // write a static method that checks if the value is a valid url
            sb.Append("<a href=\"" + i + "\">" + i + "</a>,");
        else
            sb.Append(i + ",");
    }
    return sb.ToString();
}

And use like that

@Html.Urls(myValue)

2 Comments

It's a normal text. I can't enforce a fomat like CSV. But thanks for the Html Helper hint. I guess that's the way to go.
Glad to have been helpful. However, inside your helper you could use a RegEx to replace every url with an html anchor. It should be ok also with simple and unstructured text.
-2

You may use @Html.Action(actionName) if text contains mvc URL.

2 Comments

There can be several URLs in the text. I want to have individual links for each URL.
you can try normal anchor tag <a></a> instead of HTML helper.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.