How to get links to URLs in text in ASP.NET MVC 4 with Razor syntax?

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" rel="nofollow noreferrer noopener">http://www.google.com</a>, foo: bar; <a href="http://www.yahoo.com" rel="nofollow noreferrer noopener">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)

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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)://[dw-_]+(.[w-_]+)*([w-.,@?^=%&amp;:/~+#]*[w-@?^=%&amp;/~+#])?)");
private static readonly Regex quotedUrlRegEx = new Regex(@"(?<!=)([""']|&quot;|')((http|ftp|https|file)://[dw-_]+(.[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:

"\02lanpcabcdefBugs 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" rel="nofollow noreferrer noopener">02lanpc/abc/def/Bugs in the database.docx</a>

<a href="http://localhost:81/applications/2/?releaseNumber=1.1&amp;buildNumber=2" rel="nofollow noreferrer noopener">localhost:81/&hellip;</a>

Method 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.

Method 3

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)

Method 4

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


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x