Después de investigar en voz alta, pude resolver este problema con IIS Express y anular el método OnAuthorization de la clase Controller (Ref. # 1). También he seguido la ruta recomendada por Hanselman (Ref. 2). Sin embargo, no estaba completamente satisfecho con estas dos soluciones debido a dos razones: 1. La OnAuthorization de Ref # 1 solo funciona en el nivel de acción, no en el nivel de clase de controlador 2. La Ref # 2 requiere mucha configuración (Win7 SDK para makecert ), comandos de netsh y, para poder usar el puerto 80 y el puerto 443, necesito iniciar VS2010 como administrador, lo que desapruebo.
Entonces, se me ocurrió esta solución que se centra en la simplicidad con las siguientes condiciones:
Quiero poder utilizar el RequireHttps attbbute en la clase Controlador o en el nivel de acción
Quiero que MVC use HTTPS cuando el atributo RequireHttps esté presente, y use HTTP si está ausente
No quiero tener que ejecutar Visual Studio como administrador
Quiero poder usar cualquier puerto HTTP y HTTPS asignado por IIS Express (Ver Nota # 1)
Puedo reutilizar el certificado SSL autofirmado de IIS Express, y no me importa si veo el mensaje SSL no válido
Quiero que el desarrollo, la prueba y la producción tengan exactamente la misma base de código y el mismo binario y sean tan independientes de la configuración adicional (por ejemplo, usando netsh, complemento de cert mmc, etc.) como sea posible
Ahora, con los antecedentes y la explicación fuera del camino, espero que este código ayude a alguien y ahorre algo de tiempo. Básicamente, cree una clase BaseController que herede de Controller y obtenga sus clases de controlador de esta clase base. Como has leído hasta aquí, supongo que sabes cómo hacer esto. Entonces, ¡feliz codificación!
Nota # 1: Esto se logra mediante el uso de una función útil 'getConfig' (ver código)
Ref # 1: http://puredotnetcoder.blogspot.com/2011/09/requirehttps-attribute-in-mvc3.html
Ref # 2: http://www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx
========== Código en BaseController ===================
#region Override to reroute to non-SSL port if controller action does not have RequireHttps attribute to save on CPU
// By L. Keng, 2012/08/27
// Note that this code works with RequireHttps at the controller class or action level.
// Credit: Various stackoverflow.com posts and http://puredotnetcoder.blogspot.com/2011/09/requirehttps-attribute-in-mvc3.html
protected override void OnAuthorization(AuthorizationContext filterContext)
{
// if the controller class or the action has RequireHttps attribute
var requireHttps = (filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), true).Count() > 0
|| filterContext.ActionDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), true).Count() > 0);
if (Request.IsSecureConnection)
{
// If request has a secure connection but we don't need SSL, and we are not on a child action
if (!requireHttps && !filterContext.IsChildAction)
{
var uriBuilder = new UriBuilder(Request.Url)
{
Scheme = "http",
Port = int.Parse(getConfig("HttpPort", "80")) // grab from config; default to port 80
};
filterContext.Result = this.Redirect(uriBuilder.Uri.AbsoluteUri);
}
}
else
{
// If request does not have a secure connection but we need SSL, and we are not on a child action
if (requireHttps && !filterContext.IsChildAction)
{
var uriBuilder = new UriBuilder(Request.Url)
{
Scheme = "https",
Port = int.Parse(getConfig("HttpsPort", "443")) // grab from config; default to port 443
};
filterContext.Result = this.Redirect(uriBuilder.Uri.AbsoluteUri);
}
}
base.OnAuthorization(filterContext);
}
#endregion
// a useful helper function to get appSettings value; allow caller to specify a default value if one cannot be found
internal static string getConfig(string name, string defaultValue = null)
{
var val = System.Configuration.ConfigurationManager.AppSettings[name];
return (val == null ? defaultValue : val);
}
============== código final ================
En Web.Release.Config, agregue lo siguiente para borrar HttpPort y HttpsPort (para usar los valores predeterminados 80 y 443).
<appSettings>
<add key="HttpPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="HttpsPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
</appSettings>