Completes: U4-1639 Add support to generate GET urls to SurfaceController actions

This commit is contained in:
Shannon
2013-09-06 11:40:37 +10:00
parent c269f55074
commit fee02af4eb
5 changed files with 197 additions and 86 deletions

View File

@@ -188,21 +188,19 @@ namespace Umbraco.Web
/// </summary>
internal class UmbracoForm : MvcForm
{
/// <summary>
/// Creates an UmbracoForm
/// </summary>
/// <param name="viewContext"></param>
/// <param name="surfaceController"></param>
/// <param name="surfaceAction"></param>
/// <param name="controllerName"></param>
/// <param name="controllerAction"></param>
/// <param name="area"></param>
/// <param name="method"></param>
/// <param name="additionalRouteVals"></param>
public UmbracoForm(
ViewContext viewContext,
string surfaceController,
string surfaceAction,
string controllerName,
string controllerAction,
string area,
FormMethod method,
object additionalRouteVals = null)
@@ -210,21 +208,7 @@ namespace Umbraco.Web
{
_viewContext = viewContext;
_method = method;
//need to create a params string as Base64 to put into our hidden field to use during the routes
var surfaceRouteParams = string.Format("c={0}&a={1}&ar={2}",
viewContext.HttpContext.Server.UrlEncode(surfaceController),
viewContext.HttpContext.Server.UrlEncode(surfaceAction),
area);
var additionalRouteValsAsQuery = additionalRouteVals != null ? additionalRouteVals.ToDictionary<object>().ToQueryString() : null;
if (additionalRouteValsAsQuery.IsNullOrWhiteSpace() == false)
surfaceRouteParams += "&" + additionalRouteValsAsQuery;
if (string.IsNullOrWhiteSpace(surfaceRouteParams) == false)
{
_encryptedString = surfaceRouteParams.EncryptWithMachineKey();
}
_encryptedString = UmbracoHelper.CreateEncryptedRouteString(controllerName, controllerAction, area, additionalRouteVals);
}
private readonly ViewContext _viewContext;
@@ -238,29 +222,8 @@ namespace Umbraco.Web
return;
this._disposed = true;
//Need to ensure any stale GET cookies are cleared before continuing
foreach (var c in _viewContext.HttpContext.Request.Cookies.AllKeys.Where(x => x.StartsWith("ufprt_")))
{
_viewContext.HttpContext.Request.Cookies[c].Expires = DateTime.Now.AddDays(-1);
_viewContext.HttpContext.Response.SetCookie(_viewContext.HttpContext.Request.Cookies[c]);
}
if (_method == FormMethod.Post)
{
//write out the hidden surface form routes
_viewContext.Writer.Write("<input name='ufprt' type='hidden' value='" + _encryptedString + "' />");
}
else
{
//since we are getting and we don't want this ugly value in the query string, we'll chuck it into a cookie with an id so we can retreive
//it on the server side with the same id and then remove it.
var id = Guid.NewGuid().ToString("N");
var cookie = new HttpCookie("ufprt_" + id, _encryptedString);
_viewContext.HttpContext.Response.SetCookie(cookie);
_viewContext.Writer.Write("<input name='ufprt' type='hidden' value='" + id + "' />");
}
//write out the hidden surface form routes
_viewContext.Writer.Write("<input name='ufprt' type='hidden' value='" + _encryptedString + "' />");
base.Dispose(disposing);
}
@@ -544,6 +507,7 @@ namespace Umbraco.Web
/// <param name="action"></param>
/// <param name="additionalRouteVals"></param>
/// <param name="htmlAttributes"></param>
/// <param name="method"></param>
/// <returns></returns>
public static MvcForm BeginUmbracoForm<T>(this HtmlHelper html, string action,
object additionalRouteVals,

View File

@@ -116,8 +116,8 @@ namespace Umbraco.Web.Mvc
private static PostedDataProxyInfo GetFormInfo(RequestContext requestContext)
{
//if it is a POST/GET then a value must be in the request
if ((requestContext.HttpContext.Request.RequestType == "POST" || requestContext.HttpContext.Request.RequestType == "GET")
&& requestContext.HttpContext.Request["ufprt"].IsNullOrWhiteSpace())
if (requestContext.HttpContext.Request.QueryString["ufprt"].IsNullOrWhiteSpace()
&& requestContext.HttpContext.Request.Form["ufprt"].IsNullOrWhiteSpace())
{
return null;
}
@@ -129,31 +129,28 @@ namespace Umbraco.Web.Mvc
case "POST":
//get the value from the request.
//this field will contain an encrypted version of the surface route vals.
encodedVal = requestContext.HttpContext.Request["ufprt"];
encodedVal = requestContext.HttpContext.Request.Form["ufprt"];
break;
case "GET":
//get the value from the cookie based on the id sent as a query string.
var cookieId = "ufprt_" + requestContext.HttpContext.Request["ufprt"];
if (requestContext.HttpContext.Request.Cookies[cookieId] == null || requestContext.HttpContext.Request.Cookies[cookieId].Value.IsNullOrWhiteSpace())
{
LogHelper.Warn<RenderRouteHandler>("Umbraco cannot process the GET form action, could not find the required cookie value for cookie name " + cookieId);
//we cannot continue if there is no cookie value
return null;
}
//we need to ensure the cookie is gone
var outgoingCookie = requestContext.HttpContext.Request.Cookies[cookieId];
outgoingCookie.Expires = DateTime.Now.AddDays(-1);
requestContext.HttpContext.Response.SetCookie(outgoingCookie);
//this field will contain an encrypted version of the surface route vals.
encodedVal = requestContext.HttpContext.Request.Cookies[cookieId].Value;
encodedVal = requestContext.HttpContext.Request.QueryString["ufprt"];
break;
default:
return null;
}
var decryptedString = encodedVal.DecryptWithMachineKey();
string decryptedString;
try
{
decryptedString = encodedVal.DecryptWithMachineKey();
}
catch (FormatException)
{
LogHelper.Warn<RenderRouteHandler>("A value was detected in the ufprt parameter but Umbraco could not decrypt the string");
return null;
}
var parsedQueryString = HttpUtility.ParseQueryString(decryptedString);
var decodedParts = new Dictionary<string, string>();
@@ -165,31 +162,19 @@ namespace Umbraco.Web.Mvc
//validate all required keys exist
//the controller
if (!decodedParts.Any(x => x.Key == ReservedAdditionalKeys.Controller))
if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Controller))
return null;
//the action
if (!decodedParts.Any(x => x.Key == ReservedAdditionalKeys.Action))
if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Action))
return null;
//the area
if (!decodedParts.Any(x => x.Key == ReservedAdditionalKeys.Area))
if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Area))
return null;
////the controller type, if it contains this then it is a plugin controller, not locally declared.
//if (decodedParts.Any(x => x.Key == "t"))
//{
// return new PostedDataProxyInfo
// {
// ControllerName = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == "c").Value),
// ActionName = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == "a").Value),
// Area = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == "ar").Value),
// ControllerType = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == "t").Value)
// };
//}
foreach (var item in decodedParts.Where(x => !new string[] {
ReservedAdditionalKeys.Controller,
ReservedAdditionalKeys.Action,
ReservedAdditionalKeys.Area }.Contains(x.Key)))
foreach (var item in decodedParts.Where(x => new[] {
ReservedAdditionalKeys.Controller,
ReservedAdditionalKeys.Action,
ReservedAdditionalKeys.Area }.Contains(x.Key) == false))
{
// Populate route with additional values which aren't reserved values so they eventually to action parameters
requestContext.RouteData.Values[item.Key] = item.Value;
@@ -198,9 +183,9 @@ namespace Umbraco.Web.Mvc
//return the proxy info without the surface id... could be a local controller.
return new PostedDataProxyInfo
{
ControllerName = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Controller).Value),
ActionName = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Action).Value),
Area = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Area).Value),
ControllerName = HttpUtility.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Controller).Value),
ActionName = HttpUtility.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Action).Value),
Area = HttpUtility.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Area).Value),
};
}

View File

@@ -405,6 +405,7 @@
</Compile>
<Compile Include="Controllers\LoginController.cs" />
<Compile Include="UrlHelperExtensions.cs" />
<Compile Include="UrlHelperRenderExtensions.cs" />
<Compile Include="WebApi\MemberAuthorizeAttribute.cs" />
<Compile Include="WebApi\UmbracoApiController.cs" />
<Compile Include="WebApi\UmbracoApiControllerResolver.cs" />

View File

@@ -1305,6 +1305,34 @@ namespace Umbraco.Web
#endregion
/// <summary>
/// This is used in methods like BeginUmbracoForm and SurfaceAction to generate an encrypted string which gets submitted in a request for which
/// Umbraco can decrypt during the routing process in order to delegate the request to a specific MVC Controller.
/// </summary>
/// <param name="controllerName"></param>
/// <param name="controllerAction"></param>
/// <param name="area"></param>
/// <param name="additionalRouteVals"></param>
/// <returns></returns>
internal static string CreateEncryptedRouteString(string controllerName, string controllerAction, string area, object additionalRouteVals = null)
{
Mandate.ParameterNotNullOrEmpty(controllerName, "controllerName");
Mandate.ParameterNotNullOrEmpty(controllerAction, "controllerAction");
Mandate.ParameterNotNull(area, "area");
//need to create a params string as Base64 to put into our hidden field to use during the routes
var surfaceRouteParams = string.Format("c={0}&a={1}&ar={2}",
HttpUtility.UrlEncode(controllerName),
HttpUtility.UrlEncode(controllerAction),
area);
var additionalRouteValsAsQuery = additionalRouteVals != null ? additionalRouteVals.ToDictionary<object>().ToQueryString() : null;
if (additionalRouteValsAsQuery.IsNullOrWhiteSpace() == false)
surfaceRouteParams += "&" + additionalRouteValsAsQuery;
return surfaceRouteParams.EncryptWithMachineKey();
}
}
}

View File

@@ -0,0 +1,133 @@
using System;
using System.Linq;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Web.Mvc;
namespace Umbraco.Web
{
/// <summary>
/// Extension methods for UrlHelper for use in templates
/// </summary>
public static class UrlHelperRenderExtensions
{
/// <summary>
/// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController
/// </summary>
/// <param name="url"></param>
/// <param name="action"></param>
/// <param name="controllerName"></param>
/// <returns></returns>
public static string SurfaceAction(this UrlHelper url, string action, string controllerName)
{
return url.SurfaceAction(action, controllerName, null);
}
/// <summary>
/// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController
/// </summary>
/// <param name="url"></param>
/// <param name="action"></param>
/// <param name="controllerName"></param>
/// <param name="additionalRouteVals"></param>
/// <returns></returns>
public static string SurfaceAction(this UrlHelper url, string action, string controllerName, object additionalRouteVals)
{
return url.SurfaceAction(action, controllerName, "", additionalRouteVals);
}
/// <summary>
/// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController
/// </summary>
/// <param name="url"></param>
/// <param name="action"></param>
/// <param name="controllerName"></param>
/// <param name="area"></param>
/// <param name="additionalRouteVals"></param>
/// <returns></returns>
public static string SurfaceAction(this UrlHelper url, string action, string controllerName, string area, object additionalRouteVals)
{
Mandate.ParameterNotNullOrEmpty(action, "action");
Mandate.ParameterNotNullOrEmpty(controllerName, "controllerName");
var encryptedRoute = UmbracoHelper.CreateEncryptedRouteString(controllerName, action, area, additionalRouteVals);
var result = UmbracoContext.Current.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute;
return result;
}
/// <summary>
/// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController
/// </summary>
/// <param name="url"></param>
/// <param name="action"></param>
/// <param name="surfaceType"></param>
/// <returns></returns>
public static string SurfaceAction(this UrlHelper url, string action, Type surfaceType)
{
return url.SurfaceAction(action, surfaceType, null);
}
/// <summary>
/// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController
/// </summary>
/// <param name="url"></param>
/// <param name="action"></param>
/// <param name="surfaceType"></param>
/// <param name="additionalRouteVals"></param>
/// <returns></returns>
public static string SurfaceAction(this UrlHelper url, string action, Type surfaceType, object additionalRouteVals)
{
Mandate.ParameterNotNullOrEmpty(action, "action");
Mandate.ParameterNotNull(surfaceType, "surfaceType");
var area = "";
var surfaceController = SurfaceControllerResolver.Current.RegisteredSurfaceControllers
.SingleOrDefault(x => x == surfaceType);
if (surfaceController == null)
throw new InvalidOperationException("Could not find the surface controller of type " + surfaceType.FullName);
var metaData = PluginController.GetMetadata(surfaceController);
if (metaData.AreaName.IsNullOrWhiteSpace() == false)
{
//set the area to the plugin area
area = metaData.AreaName;
}
var encryptedRoute = UmbracoHelper.CreateEncryptedRouteString(metaData.ControllerName, action, area, additionalRouteVals);
var result = UmbracoContext.Current.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute;
return result;
}
/// <summary>
/// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="action"></param>
/// <returns></returns>
public static string SurfaceAction<T>(this UrlHelper url, string action)
where T : SurfaceController
{
return url.SurfaceAction(action, typeof (T));
}
/// <summary>
/// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="action"></param>
/// <param name="additionalRouteVals"></param>
/// <returns></returns>
public static string SurfaceAction<T>(this UrlHelper url, string action, object additionalRouteVals)
where T : SurfaceController
{
return url.SurfaceAction(action, typeof (T), additionalRouteVals);
}
}
}