SSL Links/URLs in MVC

June 11, 2008 9:06 PM

A couple days ago a reader sent me a question regarding how to use SSL with the MVC framework. Specifically the reader wanted to know the easiest way to make an Ajax call to a HTTPS page from a non HTTPS page. The tricky part here is to do so without hard-coding any URLs (as one of the best practices of the MVC framework is that views and controllers should be divorced from the routes that access them).

Currently an ActionLink like:

   1: <% =Html.ActionLink( "Test", "About", "Home" ) %>

... outputs the following hyperlink:

   1: <a href="/Home/About">Test</a>

I've created a few extension methods that enable you to fully-qualify these URLs and change the protocol to HTTPS at run-time. Using the extension method "ToSslLink" like so:

   1: <% =Html.ActionLink( "Test", "About", "Home" ).ToSslLink() %>

... now outputs a hyperlink with protocol, server, port, etc:

   1: <a href="https://localhost:57626/Home/About">Test</a>

If you're using Ajax, however, you'll be more interested in getting to the URL directly rather than building an entire hyperlink. In that case you can use "ToSslUrl":

   1: <% =Url.Action( "About", "Home" ).ToSslUrl() %>

... to output this:

   1: https://localhost:57626/Home/About

Here is the code for the extension methods I've created, hopefully some of you will find this useful:

   1: using System.Text.RegularExpressions;
   2: using System.Web;
   3:  
   4: namespace SquaredRoot.Mvc.Extensions.Ssl
   5: {
   6:     /// <summary>
   7:     /// Provides helper extensions for turning strings into fully-qualified and SSL-enabled Urls.
   8:     /// </summary>
   9:     public static class UrlStringExtensions
  10:     {
  11:         /// <summary>
  12:         /// Takes a relative or absolute url and returns the fully-qualified url path.
  13:         /// </summary>
  14:         /// <param name="text">The url to make fully-qualified. Ex: Home/About</param>
  15:         /// <returns>The absolute url plus protocol, server, & port. Ex: http://localhost:1234/Home/About</returns>
  16:         public static string ToFullyQualifiedUrl( this string text )
  17:         {
  18:             //### the VirtualPathUtility doesn"t handle querystrings, so we break the original url up
  19:             var oldUrl = text;
  20:             var oldUrlArray = ( oldUrl.Contains( "?" ) ? oldUrl.Split( '?' ) : new[]{ oldUrl, "" } );
  21:  
  22:             //### we"ll use the Request.Url object to recreate the current server request"s base url
  23:             //### requestUri.AbsoluteUri = "http://domain.com:1234/Home/Index"
  24:             //### requestUri.LocalPath = "/Home/Index"
  25:             //### subtract the two and you get "http://domain.com:1234", which is urlBase
  26:             var requestUri = HttpContext.Current.Request.Url;
  27:             //### fix for Mike Hadlow's reported issue regarding extraneous link elements when a querystring is present
  28:             //var urlBase = requestUri.AbsoluteUri.Substring( 0, requestUri.AbsoluteUri.Length - requestUri.LocalPath.Length );
  29:             var localPathAndQuery = requestUri.LocalPath + requestUri.Query;
  30:             var urlBase = requestUri.AbsoluteUri.Substring( 0, requestUri.AbsoluteUri.Length - localPathAndQuery.Length ); 
  31:  
  32:             //### convert the request url into an absolute path, then reappend the querystring, if one was specified
  33:             var newUrl = VirtualPathUtility.ToAbsolute( oldUrlArray[0] );
  34:             if( !string.IsNullOrEmpty( oldUrlArray[1] ) )
  35:                 newUrl += "?" + oldUrlArray[1];
  36:  
  37:             //### combine the old url base (protocol + server + port) with the new local path
  38:             return urlBase + newUrl;
  39:         }
  40:  
  41:         /// <summary>
  42:         /// Looks for Html links in the passed string and turns each relative or absolute url and returns the fully-qualified url path.
  43:         /// </summary>
  44:         /// <param name="text">The url to make fully-qualified. Ex: <a href="Home/About">Blah</a></param>
  45:         /// <returns>The absolute url plus protocol, server, & port. Ex: <a href="http://localhost:1234/Home/About">Blah</a></returns>
  46:         public static string ToFullyQualifiedLink( this string text )
  47:         {
  48:             var regex = new Regex(
  49:                 "(?<Before><a.*href=\")(?!http)(?<Url>.*?)(?<After>\".+>)",
  50:                 RegexOptions.Multiline | RegexOptions.IgnoreCase
  51:                 );
  52:  
  53:             return regex.Replace( text, ( Match m ) =>
  54:                                         m.Groups["Before"].Value +
  55:                                         ToFullyQualifiedUrl( m.Groups["Url"].Value ) +
  56:                                         m.Groups["After"].Value
  57:                 );
  58:         }
  59:  
  60:         /// <summary>
  61:         /// Takes a relative or absolute url and returns the fully-qualified url path using the Https protocol.
  62:         /// </summary>
  63:         /// <param name="text">The url to make fully-qualified. Ex: Home/About</param>
  64:         /// <returns>The absolute url plus server, & port using the Https protocol. Ex: https://localhost:1234/Home/About</returns>
  65:         public static string ToSslUrl( this string text )
  66:         {
  67:             return ToFullyQualifiedUrl( text ).Replace( "http:", "https:" );
  68:         }
  69:  
  70:         /// <summary>
  71:         /// Looks for Html links in the passed string and turns each relative or absolute url into a fully-qualified url path using the Https protocol.
  72:         /// </summary>
  73:         /// <param name="text">The url to make fully-qualified. Ex: <a href="Home/About">Blah</a></param>
  74:         /// <returns>The absolute url plus server, & port using the Https protocol. Ex: <a href="https://localhost:1234/Home/About">Blah</a></returns>
  75:         public static string ToSslLink( this string text )
  76:         {
  77:             return ToFullyQualifiedLink( text ).Replace( "http:", "https:" );
  78:         }
  79:     }
  80: }

One important missing element from all is this posting to a form via HTTPS from a non-HTTPS page. You can certainly create your form tags manually and populate the action attribute using Url.Action(...).ToSslUrl(), but you cannot try and combine these extension methods with the Html.Form(...) helper that is most commonly used inside a using statement.

Note: The above code was updated on July 7th, 2008 to fix a bug reported by Mike Hadlow. Thanks Mike!

Tags: , , ,
Categories: MVC
Actions: E-mail | Permalink | Comments (9) RSS Feed for this post's comments.

Comments

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading




Troy Goode

Troy Goode
Microsoft Certified Professional Developer
AddThis Feed Button

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in  anyway.

© Copyright 2008

Colophon

Powered by:
BlogEngine.NET 1.4.5
Template:
Designs by Darren
Header Font:
Stamper
Syntax Highlighting:
WLW Code Snippet Plugin
Filter by APML