Changes the umbraco route values to use http features intead of in route values which is much nicer, fixes the redirect to page result, tests a surface controller POST and it works, ensures the routing takes place before the form check, removes a bunch of old code
This commit is contained in:
@@ -7,8 +7,6 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public static class Web
|
||||
{
|
||||
public const string UmbracoRouteDefinitionDataToken = "umbraco-route-def";
|
||||
|
||||
/// <summary>
|
||||
/// The preview cookie name
|
||||
/// </summary>
|
||||
|
||||
@@ -60,22 +60,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.ModelBinders
|
||||
// Arrange
|
||||
IPublishedContent pc = CreatePublishedContent();
|
||||
ModelBindingContext bindingContext = CreateBindingContextForUmbracoRequest(typeof(ContentModel), pc);
|
||||
bindingContext.ActionContext.RouteData.Values.Remove(Constants.Web.UmbracoRouteDefinitionDataToken);
|
||||
|
||||
// Act
|
||||
await _contentModelBinder.BindModelAsync(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(bindingContext.Result.IsModelSet);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Does_Not_Bind_Model_When_UmbracoToken_Has_Incorrect_Model()
|
||||
{
|
||||
// Arrange
|
||||
IPublishedContent pc = CreatePublishedContent();
|
||||
ModelBindingContext bindingContext = CreateBindingContextForUmbracoRequest(typeof(ContentModel), pc);
|
||||
bindingContext.ActionContext.RouteData.Values[Constants.Web.UmbracoRouteDefinitionDataToken] = new NonContentModel();
|
||||
bindingContext.ActionContext.HttpContext.Features.Set<UmbracoRouteValues>(null);
|
||||
|
||||
// Act
|
||||
await _contentModelBinder.BindModelAsync(bindingContext);
|
||||
@@ -220,9 +205,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.ModelBinders
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var routeData = new RouteData();
|
||||
routeData.Values.Add(Constants.Web.UmbracoRouteDefinitionDataToken, new UmbracoRouteValues(publishedRequest));
|
||||
{
|
||||
}
|
||||
httpContext.Features.Set(new UmbracoRouteValues(publishedRequest));
|
||||
|
||||
var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());
|
||||
var metadataProvider = new EmptyModelMetadataProvider();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Moq;
|
||||
@@ -126,15 +127,15 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Controllers
|
||||
|
||||
var routeDefinition = new UmbracoRouteValues(publishedRequest);
|
||||
|
||||
var routeData = new RouteData();
|
||||
routeData.Values.Add(CoreConstants.Web.UmbracoRouteDefinitionDataToken, routeDefinition);
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Features.Set(routeDefinition);
|
||||
|
||||
var ctrl = new TestSurfaceController(umbracoContextAccessor, Mock.Of<IPublishedContentQuery>(), Mock.Of<IPublishedUrlProvider>())
|
||||
{
|
||||
ControllerContext = new ControllerContext()
|
||||
{
|
||||
HttpContext = Mock.Of<HttpContext>(),
|
||||
RouteData = routeData
|
||||
HttpContext = httpContext,
|
||||
RouteData = new RouteData()
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Routing
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class HijackedRouteEvaluatorTests
|
||||
public class ControllerActionSearcherTests
|
||||
{
|
||||
private class TestActionDescriptorCollectionProvider : ActionDescriptorCollectionProvider
|
||||
{
|
||||
@@ -88,11 +88,11 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Routing
|
||||
[TestCase("Custom", "Render1", nameof(Render1Controller.Custom), true)]
|
||||
public void Matches_Controller(string action, string controller, string resultAction, bool matches)
|
||||
{
|
||||
var evaluator = new HijackedRouteEvaluator(
|
||||
new NullLogger<HijackedRouteEvaluator>(),
|
||||
var query = new ControllerActionSearcher(
|
||||
new NullLogger<ControllerActionSearcher>(),
|
||||
GetActionDescriptors());
|
||||
|
||||
HijackedRouteResult result = evaluator.Evaluate(controller, action);
|
||||
ControllerActionSearchResult result = query.Find<IRenderController>(controller, action);
|
||||
Assert.AreEqual(matches, result.Success);
|
||||
if (matches)
|
||||
{
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
@@ -49,7 +50,9 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Routing
|
||||
TestHelper.GetHostingEnvironment(),
|
||||
state,
|
||||
routeValuesFactory ?? Mock.Of<IUmbracoRouteValuesFactory>(),
|
||||
filter ?? Mock.Of<IRoutableDocumentFilter>(x => x.IsDocumentRequest(It.IsAny<string>()) == true));
|
||||
filter ?? Mock.Of<IRoutableDocumentFilter>(x => x.IsDocumentRequest(It.IsAny<string>()) == true),
|
||||
Mock.Of<IDataProtectionProvider>(),
|
||||
Mock.Of<IControllerActionSearcher>());
|
||||
|
||||
return transformer;
|
||||
}
|
||||
@@ -74,7 +77,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Routing
|
||||
typeof(TestController));
|
||||
|
||||
private IUmbracoRouteValuesFactory GetRouteValuesFactory(IPublishedRequest request)
|
||||
=> Mock.Of<IUmbracoRouteValuesFactory>(x => x.Create(It.IsAny<HttpContext>(), It.IsAny<RouteValueDictionary>(), It.IsAny<IPublishedRequest>()) == GetRouteValues(request));
|
||||
=> Mock.Of<IUmbracoRouteValuesFactory>(x => x.Create(It.IsAny<HttpContext>(), It.IsAny<IPublishedRequest>()) == GetRouteValues(request));
|
||||
|
||||
private IPublishedRouter GetRouter(IPublishedRequest request)
|
||||
=> Mock.Of<IPublishedRouter>(x => x.RouteRequestAsync(It.IsAny<IPublishedRequestBuilder>(), It.IsAny<RouteRequestOptions>()) == Task.FromResult(request));
|
||||
@@ -140,6 +143,25 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Routing
|
||||
Assert.AreEqual(request, umbracoContext.PublishedRequest);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Assigns_UmbracoRouteValues_To_HttpContext_Feature()
|
||||
{
|
||||
IUmbracoContext umbracoContext = GetUmbracoContext(true);
|
||||
IPublishedRequest request = Mock.Of<IPublishedRequest>();
|
||||
|
||||
UmbracoRouteValueTransformer transformer = GetTransformerWithRunState(
|
||||
Mock.Of<IUmbracoContextAccessor>(x => x.UmbracoContext == umbracoContext),
|
||||
router: GetRouter(request),
|
||||
routeValuesFactory: GetRouteValuesFactory(request));
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
RouteValueDictionary result = await transformer.TransformAsync(httpContext, new RouteValueDictionary());
|
||||
|
||||
UmbracoRouteValues routeVals = httpContext.Features.Get<UmbracoRouteValues>();
|
||||
Assert.IsNotNull(routeVals);
|
||||
Assert.AreEqual(routeVals.PublishedRequest, umbracoContext.PublishedRequest);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Assigns_Values_To_RouteValueDictionary()
|
||||
{
|
||||
|
||||
@@ -39,8 +39,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Routing
|
||||
renderingDefaults,
|
||||
Mock.Of<IShortStringHelper>(),
|
||||
new UmbracoFeatures(),
|
||||
new HijackedRouteEvaluator(
|
||||
new NullLogger<HijackedRouteEvaluator>(),
|
||||
new ControllerActionSearcher(
|
||||
new NullLogger<ControllerActionSearcher>(),
|
||||
Mock.Of<IActionDescriptorCollectionProvider>()),
|
||||
publishedRouter.Object);
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Routing
|
||||
|
||||
UmbracoRouteValuesFactory factory = GetFactory(out Mock<IPublishedRouter> publishedRouter, out _);
|
||||
|
||||
UmbracoRouteValues result = factory.Create(new DefaultHttpContext(), new RouteValueDictionary(), request);
|
||||
UmbracoRouteValues result = factory.Create(new DefaultHttpContext(), request);
|
||||
|
||||
// The request has content, no template, no hijacked route and no disabled template features so UpdateRequestToNotFound will be called
|
||||
publishedRouter.Verify(m => m.UpdateRequestToNotFound(It.IsAny<IPublishedRequest>()), Times.Once);
|
||||
@@ -73,11 +73,9 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Routing
|
||||
UmbracoRouteValuesFactory factory = GetFactory(out _, out UmbracoRenderingDefaults renderingDefaults);
|
||||
|
||||
var routeVals = new RouteValueDictionary();
|
||||
UmbracoRouteValues result = factory.Create(new DefaultHttpContext(), routeVals, request);
|
||||
UmbracoRouteValues result = factory.Create(new DefaultHttpContext(), request);
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
Assert.IsTrue(routeVals.ContainsKey(Constants.Web.UmbracoRouteDefinitionDataToken));
|
||||
Assert.AreEqual(result, routeVals[Constants.Web.UmbracoRouteDefinitionDataToken]);
|
||||
Assert.AreEqual(renderingDefaults.DefaultControllerType, result.ControllerType);
|
||||
Assert.AreEqual(UmbracoRouteValues.DefaultActionName, result.ActionName);
|
||||
Assert.IsNull(result.TemplateName);
|
||||
|
||||
@@ -17,12 +17,13 @@ namespace Umbraco.Web.Common.Controllers
|
||||
/// </summary>
|
||||
protected UmbracoRouteValues GetUmbracoRouteValues(ResultExecutingContext context)
|
||||
{
|
||||
if (!context.RouteData.Values.TryGetValue(Core.Constants.Web.UmbracoRouteDefinitionDataToken, out var def))
|
||||
UmbracoRouteValues routeVals = context.HttpContext.Features.Get<UmbracoRouteValues>();
|
||||
if (routeVals == null)
|
||||
{
|
||||
throw new InvalidOperationException($"No route value found with key {Core.Constants.Web.UmbracoRouteDefinitionDataToken}");
|
||||
throw new InvalidOperationException($"No {nameof(UmbracoRouteValues)} feature was found in the HttpContext");
|
||||
}
|
||||
|
||||
return (UmbracoRouteValues)def;
|
||||
return routeVals;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -71,11 +71,11 @@ namespace Umbraco.Web.Common.Controllers
|
||||
return _umbracoRouteValues;
|
||||
}
|
||||
|
||||
_umbracoRouteValues = HttpContext.GetRouteValue(Core.Constants.Web.UmbracoRouteDefinitionDataToken) as UmbracoRouteValues;
|
||||
_umbracoRouteValues = HttpContext.Features.Get<UmbracoRouteValues>();
|
||||
|
||||
if (_umbracoRouteValues == null)
|
||||
{
|
||||
throw new InvalidOperationException($"No route value found with key {Core.Constants.Web.UmbracoRouteDefinitionDataToken}");
|
||||
throw new InvalidOperationException($"No {nameof(UmbracoRouteValues)} feature was found in the HttpContext");
|
||||
}
|
||||
|
||||
return _umbracoRouteValues;
|
||||
|
||||
@@ -29,7 +29,8 @@ namespace Umbraco.Web.Common.Localization
|
||||
/// <inheritdoc/>
|
||||
public override Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
|
||||
{
|
||||
if (httpContext.GetRouteValue(Core.Constants.Web.UmbracoRouteDefinitionDataToken) is UmbracoRouteValues routeValues)
|
||||
UmbracoRouteValues routeValues = httpContext.Features.Get<UmbracoRouteValues>();
|
||||
if (routeValues != null)
|
||||
{
|
||||
string culture = routeValues.PublishedRequest?.Culture;
|
||||
if (culture != null)
|
||||
|
||||
@@ -30,8 +30,8 @@ namespace Umbraco.Web.Common.ModelBinders
|
||||
// only IPublishedContent will ever exist in the request so when this model binder is used as an IModelBinder
|
||||
// in the aspnet pipeline it will really only support converting from IPublishedContent which is contained
|
||||
// in the UmbracoRouteValues --> IContentModel
|
||||
if (!bindingContext.ActionContext.RouteData.Values.TryGetValue(Core.Constants.Web.UmbracoRouteDefinitionDataToken, out var source)
|
||||
|| !(source is UmbracoRouteValues umbracoRouteValues))
|
||||
UmbracoRouteValues umbracoRouteValues = bindingContext.HttpContext.Features.Get<UmbracoRouteValues>();
|
||||
if (umbracoRouteValues == null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Web;
|
||||
@@ -12,39 +13,43 @@ namespace Umbraco.Web.Common.Security
|
||||
{
|
||||
public class EncryptionHelper
|
||||
{
|
||||
// TODO: Decide if these belong here... I don't think so since this all has to do with surface controller routes
|
||||
// could also just be injected too....
|
||||
|
||||
private static IDataProtector CreateDataProtector(IDataProtectionProvider dataProtectionProvider)
|
||||
{
|
||||
return dataProtectionProvider.CreateProtector(nameof(EncryptionHelper));
|
||||
}
|
||||
=> dataProtectionProvider.CreateProtector(nameof(EncryptionHelper));
|
||||
|
||||
public static string Decrypt(string encryptedString, IDataProtectionProvider dataProtectionProvider)
|
||||
{
|
||||
return CreateDataProtector(dataProtectionProvider).Unprotect(encryptedString);
|
||||
}
|
||||
=> CreateDataProtector(dataProtectionProvider).Unprotect(encryptedString);
|
||||
|
||||
public static string Encrypt(string plainString, IDataProtectionProvider dataProtectionProvider)
|
||||
{
|
||||
return CreateDataProtector(dataProtectionProvider).Protect(plainString);
|
||||
}
|
||||
=> CreateDataProtector(dataProtectionProvider).Protect(plainString);
|
||||
|
||||
/// <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="dataProtectionProvider"></param>
|
||||
/// <param name="controllerName"></param>
|
||||
/// <param name="controllerAction"></param>
|
||||
/// <param name="area"></param>
|
||||
/// <param name="additionalRouteVals"></param>
|
||||
/// <returns></returns>
|
||||
public static string CreateEncryptedRouteString(IDataProtectionProvider dataProtectionProvider, string controllerName, string controllerAction, string area, object additionalRouteVals = null)
|
||||
{
|
||||
if (dataProtectionProvider == null) throw new ArgumentNullException(nameof(dataProtectionProvider));
|
||||
if (controllerName == null) throw new ArgumentNullException(nameof(controllerName));
|
||||
if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName));
|
||||
if (controllerAction == null) throw new ArgumentNullException(nameof(controllerAction));
|
||||
if (string.IsNullOrEmpty(controllerAction)) throw new ArgumentException("Value can't be empty.", nameof(controllerAction));
|
||||
if (area == null) throw new ArgumentNullException(nameof(area));
|
||||
if (dataProtectionProvider is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(dataProtectionProvider));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(controllerName))
|
||||
{
|
||||
throw new ArgumentException($"'{nameof(controllerName)}' cannot be null or empty.", nameof(controllerName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(controllerAction))
|
||||
{
|
||||
throw new ArgumentException($"'{nameof(controllerAction)}' cannot be null or empty.", nameof(controllerAction));
|
||||
}
|
||||
|
||||
if (area is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(area));
|
||||
}
|
||||
|
||||
// need to create a params string as Base64 to put into our hidden field to use during the routes
|
||||
var surfaceRouteParams = $"{ViewConstants.ReservedAdditionalKeys.Controller}={WebUtility.UrlEncode(controllerName)}&{ViewConstants.ReservedAdditionalKeys.Action}={WebUtility.UrlEncode(controllerAction)}&{ViewConstants.ReservedAdditionalKeys.Area}={area}";
|
||||
@@ -54,22 +59,34 @@ namespace Umbraco.Web.Common.Security
|
||||
if (additionalRouteVals != null)
|
||||
{
|
||||
if (additionalRouteVals is Dictionary<string, object> additionalRouteValsAsDictionary)
|
||||
{
|
||||
additionalRouteValsAsQuery = additionalRouteValsAsDictionary.ToQueryString();
|
||||
else
|
||||
additionalRouteValsAsQuery = additionalRouteVals.ToDictionary<object>().ToQueryString();
|
||||
}
|
||||
else
|
||||
{
|
||||
additionalRouteValsAsQuery = additionalRouteVals.ToDictionary<object>().ToQueryString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
additionalRouteValsAsQuery = null;
|
||||
}
|
||||
|
||||
if (additionalRouteValsAsQuery.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
surfaceRouteParams += "&" + additionalRouteValsAsQuery;
|
||||
}
|
||||
|
||||
return Encrypt(surfaceRouteParams, dataProtectionProvider);
|
||||
}
|
||||
|
||||
public static bool DecryptAndValidateEncryptedRouteString(IDataProtectionProvider dataProtectionProvider, string encryptedString, out IDictionary<string, string> parts)
|
||||
{
|
||||
if (dataProtectionProvider == null) throw new ArgumentNullException(nameof(dataProtectionProvider));
|
||||
if (dataProtectionProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(dataProtectionProvider));
|
||||
}
|
||||
|
||||
string decryptedString;
|
||||
try
|
||||
{
|
||||
@@ -81,22 +98,32 @@ namespace Umbraco.Web.Common.Security
|
||||
parts = null;
|
||||
return false;
|
||||
}
|
||||
var parsedQueryString = HttpUtility.ParseQueryString(decryptedString);
|
||||
|
||||
NameValueCollection parsedQueryString = HttpUtility.ParseQueryString(decryptedString);
|
||||
parts = new Dictionary<string, string>();
|
||||
foreach (var key in parsedQueryString.AllKeys)
|
||||
{
|
||||
parts[key] = parsedQueryString[key];
|
||||
}
|
||||
|
||||
// validate all required keys exist
|
||||
// the controller
|
||||
if (parts.All(x => x.Key != ViewConstants.ReservedAdditionalKeys.Controller))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// the action
|
||||
if (parts.All(x => x.Key != ViewConstants.ReservedAdditionalKeys.Action))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// the area
|
||||
if (parts.All(x => x.Key != ViewConstants.ReservedAdditionalKeys.Area))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
@@ -13,6 +14,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Web.Website.ActionResults
|
||||
{
|
||||
@@ -33,13 +35,15 @@ namespace Umbraco.Web.Website.ActionResults
|
||||
var routeData = context.RouteData;
|
||||
|
||||
ResetRouteData(routeData);
|
||||
ValidateRouteData(routeData);
|
||||
ValidateRouteData(context);
|
||||
|
||||
var factory = context.HttpContext.RequestServices.GetRequiredService<IControllerFactory>();
|
||||
IControllerFactory factory = context.HttpContext.RequestServices.GetRequiredService<IControllerFactory>();
|
||||
Controller controller = null;
|
||||
|
||||
if (!(context is ControllerContext controllerContext))
|
||||
return Task.FromCanceled(new System.Threading.CancellationToken());
|
||||
{
|
||||
return Task.FromCanceled(CancellationToken.None);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -97,9 +101,10 @@ namespace Umbraco.Web.Website.ActionResults
|
||||
/// <summary>
|
||||
/// Validate that the current page execution is not being handled by the normal umbraco routing system
|
||||
/// </summary>
|
||||
private static void ValidateRouteData(RouteData routeData)
|
||||
private static void ValidateRouteData(ActionContext actionContext)
|
||||
{
|
||||
if (routeData.Values.ContainsKey(Constants.Web.UmbracoRouteDefinitionDataToken) == false)
|
||||
UmbracoRouteValues umbracoRouteValues = actionContext.HttpContext.Features.Get<UmbracoRouteValues>();
|
||||
if (umbracoRouteValues == null)
|
||||
{
|
||||
throw new InvalidOperationException("Can only use " + typeof(UmbracoPageResult).Name +
|
||||
" in the context of an Http POST when using a SurfaceController form");
|
||||
@@ -114,7 +119,9 @@ namespace Umbraco.Web.Website.ActionResults
|
||||
controller.ViewData.ModelState.Merge(context.ModelState);
|
||||
|
||||
foreach (var d in controller.ViewData)
|
||||
{
|
||||
controller.ViewData[d.Key] = d.Value;
|
||||
}
|
||||
|
||||
// We cannot simply merge the temp data because during controller execution it will attempt to 'load' temp data
|
||||
// but since it has not been saved, there will be nothing to load and it will revert to nothing, so the trick is
|
||||
@@ -135,7 +142,9 @@ namespace Umbraco.Web.Website.ActionResults
|
||||
private static Controller CreateController(ControllerContext context, IControllerFactory factory)
|
||||
{
|
||||
if (!(factory.CreateController(context) is Controller controller))
|
||||
{
|
||||
throw new InvalidOperationException("Could not create controller with name " + context.ActionDescriptor.ControllerName + ".");
|
||||
}
|
||||
|
||||
return controller;
|
||||
}
|
||||
@@ -146,7 +155,9 @@ namespace Umbraco.Web.Website.ActionResults
|
||||
private static void CleanupController(ControllerContext context, Controller controller, IControllerFactory factory)
|
||||
{
|
||||
if (!(controller is null))
|
||||
{
|
||||
factory.ReleaseController(context, controller);
|
||||
}
|
||||
|
||||
controller?.DisposeIfDisposable();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -38,14 +39,13 @@ namespace Umbraco.Web.Website.Controllers
|
||||
{
|
||||
get
|
||||
{
|
||||
var routeDefAttempt = TryGetRouteDefinitionFromAncestorViewContexts();
|
||||
if (routeDefAttempt.Success == false)
|
||||
UmbracoRouteValues umbracoRouteValues = HttpContext.Features.Get<UmbracoRouteValues>();
|
||||
if (umbracoRouteValues == null)
|
||||
{
|
||||
throw routeDefAttempt.Exception;
|
||||
throw new InvalidOperationException($"No {nameof(UmbracoRouteValues)} feature was found in the HttpContext");
|
||||
}
|
||||
|
||||
var routeDef = routeDefAttempt.Result;
|
||||
return routeDef.PublishedRequest.PublishedContent;
|
||||
return umbracoRouteValues.PublishedRequest.PublishedContent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,24 +101,5 @@ namespace Umbraco.Web.Website.Controllers
|
||||
/// </summary>
|
||||
protected UmbracoPageResult CurrentUmbracoPage()
|
||||
=> new UmbracoPageResult(ProfilingLogger);
|
||||
|
||||
/// <summary>
|
||||
/// we need to recursively find the route definition based on the parent view context
|
||||
/// </summary>
|
||||
private Attempt<UmbracoRouteValues> TryGetRouteDefinitionFromAncestorViewContexts()
|
||||
{
|
||||
var currentContext = ControllerContext;
|
||||
while (!(currentContext is null))
|
||||
{
|
||||
var currentRouteData = currentContext.RouteData;
|
||||
if (currentRouteData.Values.ContainsKey(Constants.Web.UmbracoRouteDefinitionDataToken))
|
||||
{
|
||||
return Attempt.Succeed((UmbracoRouteValues)currentRouteData.Values[Constants.Web.UmbracoRouteDefinitionDataToken]);
|
||||
}
|
||||
}
|
||||
|
||||
return Attempt<UmbracoRouteValues>.Fail(
|
||||
new InvalidOperationException("Cannot find the Umbraco route definition in the route values, the request must be made in the context of an Umbraco request"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Umbraco.Web.Website.DependencyInjection
|
||||
builder.Services.AddDataProtection();
|
||||
|
||||
builder.Services.AddScoped<UmbracoRouteValueTransformer>();
|
||||
builder.Services.AddSingleton<HijackedRouteEvaluator>();
|
||||
builder.Services.AddSingleton<IControllerActionSearcher, ControllerActionSearcher>();
|
||||
builder.Services.AddSingleton<IUmbracoRouteValuesFactory, UmbracoRouteValuesFactory>();
|
||||
builder.Services.AddSingleton<IUmbracoRenderingDefaults, UmbracoRenderingDefaults>();
|
||||
builder.Services.AddSingleton<IRoutableDocumentFilter, RoutableDocumentFilter>();
|
||||
|
||||
@@ -243,15 +243,12 @@ namespace Umbraco.Extensions
|
||||
return htmlHelper.ActionLink(actionName, metaData.ControllerName, routeVals);
|
||||
}
|
||||
|
||||
#region BeginUmbracoForm
|
||||
|
||||
/// <summary>
|
||||
/// Used for rendering out the Form for BeginUmbracoForm
|
||||
/// </summary>
|
||||
internal class UmbracoForm : MvcForm
|
||||
{
|
||||
private readonly ViewContext _viewContext;
|
||||
private bool _disposed;
|
||||
private readonly string _encryptedString;
|
||||
private readonly string _controllerName;
|
||||
|
||||
@@ -272,15 +269,8 @@ namespace Umbraco.Extensions
|
||||
_encryptedString = EncryptionHelper.CreateEncryptedRouteString(GetRequiredService<IDataProtectionProvider>(viewContext), controllerName, controllerAction, area, additionalRouteVals);
|
||||
}
|
||||
|
||||
protected new void Dispose()
|
||||
protected override void GenerateEndForm()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
|
||||
// Detect if the call is targeting UmbRegisterController/UmbProfileController/UmbLoginStatusController/UmbLoginController and if it is we automatically output a AntiForgeryToken()
|
||||
// We have a controllerName and area so we can match
|
||||
if (_controllerName == "UmbRegister"
|
||||
@@ -295,7 +285,7 @@ namespace Umbraco.Extensions
|
||||
// write out the hidden surface form routes
|
||||
_viewContext.Writer.Write("<input name=\"ufprt\" type=\"hidden\" value=\"" + _encryptedString + "\" />");
|
||||
|
||||
base.Dispose();
|
||||
base.GenerateEndForm();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,7 +700,6 @@ namespace Umbraco.Extensions
|
||||
return theForm;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region If
|
||||
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.Website.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// The result from evaluating if a route can be hijacked
|
||||
/// The result from querying a controller/action in the existing routes
|
||||
/// </summary>
|
||||
public class HijackedRouteResult
|
||||
public class ControllerActionSearchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a failed result
|
||||
/// Initializes a new instance of the <see cref="ControllerActionSearchResult"/> class.
|
||||
/// </summary>
|
||||
public static HijackedRouteResult Failed() => new HijackedRouteResult(false, null, null, null);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HijackedRouteResult"/> class.
|
||||
/// </summary>
|
||||
public HijackedRouteResult(
|
||||
private ControllerActionSearchResult(
|
||||
bool success,
|
||||
string controllerName,
|
||||
Type controllerType,
|
||||
@@ -28,7 +23,18 @@ namespace Umbraco.Web.Website.Routing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating if the route could be hijacked
|
||||
/// Initializes a new instance of the <see cref="ControllerActionSearchResult"/> class.
|
||||
/// </summary>
|
||||
public ControllerActionSearchResult(
|
||||
string controllerName,
|
||||
Type controllerType,
|
||||
string actionName)
|
||||
: this(true, controllerName, controllerType, actionName)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the route could be hijacked
|
||||
/// </summary>
|
||||
public bool Success { get; }
|
||||
|
||||
@@ -46,5 +52,10 @@ namespace Umbraco.Web.Website.Routing
|
||||
/// Gets the Acton name
|
||||
/// </summary>
|
||||
public string ActionName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a failed result
|
||||
/// </summary>
|
||||
public static ControllerActionSearchResult Failed() => new ControllerActionSearchResult(false, null, null, null);
|
||||
}
|
||||
}
|
||||
@@ -11,19 +11,19 @@ using Umbraco.Web.Common.Controllers;
|
||||
namespace Umbraco.Web.Website.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if a custom controller can hijack the current route
|
||||
/// Used to find a controller/action in the current available routes
|
||||
/// </summary>
|
||||
public class HijackedRouteEvaluator
|
||||
public class ControllerActionSearcher : IControllerActionSearcher
|
||||
{
|
||||
private readonly ILogger<HijackedRouteEvaluator> _logger;
|
||||
private readonly ILogger<ControllerActionSearcher> _logger;
|
||||
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
|
||||
private const string DefaultActionName = nameof(RenderController.Index);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HijackedRouteEvaluator"/> class.
|
||||
/// Initializes a new instance of the <see cref="ControllerActionSearcher"/> class.
|
||||
/// </summary>
|
||||
public HijackedRouteEvaluator(
|
||||
ILogger<HijackedRouteEvaluator> logger,
|
||||
public ControllerActionSearcher(
|
||||
ILogger<ControllerActionSearcher> logger,
|
||||
IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
|
||||
{
|
||||
_logger = logger;
|
||||
@@ -33,7 +33,8 @@ namespace Umbraco.Web.Website.Routing
|
||||
/// <summary>
|
||||
/// Determines if a custom controller can hijack the current route
|
||||
/// </summary>
|
||||
public HijackedRouteResult Evaluate(string controller, string action)
|
||||
/// <typeparam name="T">The controller type to find</typeparam>
|
||||
public ControllerActionSearchResult Find<T>(string controller, string action)
|
||||
{
|
||||
IReadOnlyList<ControllerActionDescriptor> candidates = FindControllerCandidates(controller, action, DefaultActionName);
|
||||
|
||||
@@ -45,8 +46,8 @@ namespace Umbraco.Web.Website.Routing
|
||||
{
|
||||
ControllerActionDescriptor controllerDescriptor = customControllerCandidates[0];
|
||||
|
||||
// ensure the controller is of type IRenderController and ControllerBase
|
||||
if (TypeHelper.IsTypeAssignableFrom<IRenderController>(controllerDescriptor.ControllerTypeInfo)
|
||||
// ensure the controller is of type T and ControllerBase
|
||||
if (TypeHelper.IsTypeAssignableFrom<T>(controllerDescriptor.ControllerTypeInfo)
|
||||
&& TypeHelper.IsTypeAssignableFrom<ControllerBase>(controllerDescriptor.ControllerTypeInfo))
|
||||
{
|
||||
// now check if the custom action matches
|
||||
@@ -61,8 +62,7 @@ namespace Umbraco.Web.Website.Routing
|
||||
}
|
||||
|
||||
// it's a hijacked route with a custom controller, so return the the values
|
||||
return new HijackedRouteResult(
|
||||
true,
|
||||
return new ControllerActionSearchResult(
|
||||
controllerDescriptor.ControllerName,
|
||||
controllerDescriptor.ControllerTypeInfo,
|
||||
resultingAction);
|
||||
@@ -73,7 +73,7 @@ namespace Umbraco.Web.Website.Routing
|
||||
"The current Document Type {ContentTypeAlias} matches a locally declared controller of type {ControllerName}. Custom Controllers for Umbraco routing must implement '{UmbracoRenderController}' and inherit from '{UmbracoControllerBase}'.",
|
||||
controller,
|
||||
controllerDescriptor.ControllerTypeInfo.FullName,
|
||||
typeof(IRenderController).FullName,
|
||||
typeof(T).FullName,
|
||||
typeof(ControllerBase).FullName);
|
||||
|
||||
// we cannot route to this custom controller since it is not of the correct type so we'll continue with the defaults
|
||||
@@ -81,7 +81,7 @@ namespace Umbraco.Web.Website.Routing
|
||||
}
|
||||
}
|
||||
|
||||
return HijackedRouteResult.Failed();
|
||||
return ControllerActionSearchResult.Failed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Web.Website.Routing
|
||||
{
|
||||
public interface IControllerActionSearcher
|
||||
{
|
||||
ControllerActionSearchResult Find<T>(string controller, string action);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,6 @@ namespace Umbraco.Web.Website.Routing
|
||||
/// <summary>
|
||||
/// Creates <see cref="UmbracoRouteValues"/>
|
||||
/// </summary>
|
||||
UmbracoRouteValues Create(HttpContext httpContext, RouteValueDictionary values, IPublishedRequest request);
|
||||
UmbracoRouteValues Create(HttpContext httpContext, IPublishedRequest request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Common.Routing;
|
||||
using Umbraco.Web.Common.Security;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Website.Controllers;
|
||||
using RouteDirection = Umbraco.Web.Routing.RouteDirection;
|
||||
@@ -35,6 +43,10 @@ namespace Umbraco.Web.Website.Routing
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly IUmbracoRouteValuesFactory _routeValuesFactory;
|
||||
private readonly IRoutableDocumentFilter _routableDocumentFilter;
|
||||
private readonly IDataProtectionProvider _dataProtectionProvider;
|
||||
private readonly IControllerActionSearcher _controllerActionSearcher;
|
||||
private const string ControllerToken = "controller";
|
||||
private const string ActionToken = "action";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoRouteValueTransformer"/> class.
|
||||
@@ -47,21 +59,25 @@ namespace Umbraco.Web.Website.Routing
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
IRuntimeState runtime,
|
||||
IUmbracoRouteValuesFactory routeValuesFactory,
|
||||
IRoutableDocumentFilter routableDocumentFilter)
|
||||
IRoutableDocumentFilter routableDocumentFilter,
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
IControllerActionSearcher controllerActionSearcher)
|
||||
{
|
||||
if (globalSettings is null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(globalSettings));
|
||||
throw new ArgumentNullException(nameof(globalSettings));
|
||||
}
|
||||
|
||||
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_publishedRouter = publishedRouter ?? throw new System.ArgumentNullException(nameof(publishedRouter));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_publishedRouter = publishedRouter ?? throw new ArgumentNullException(nameof(publishedRouter));
|
||||
_globalSettings = globalSettings.Value;
|
||||
_hostingEnvironment = hostingEnvironment ?? throw new System.ArgumentNullException(nameof(hostingEnvironment));
|
||||
_runtime = runtime ?? throw new System.ArgumentNullException(nameof(runtime));
|
||||
_routeValuesFactory = routeValuesFactory ?? throw new System.ArgumentNullException(nameof(routeValuesFactory));
|
||||
_routableDocumentFilter = routableDocumentFilter ?? throw new System.ArgumentNullException(nameof(routableDocumentFilter));
|
||||
_hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
|
||||
_runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
|
||||
_routeValuesFactory = routeValuesFactory ?? throw new ArgumentNullException(nameof(routeValuesFactory));
|
||||
_routableDocumentFilter = routableDocumentFilter ?? throw new ArgumentNullException(nameof(routableDocumentFilter));
|
||||
_dataProtectionProvider = dataProtectionProvider;
|
||||
_controllerActionSearcher = controllerActionSearcher;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -87,23 +103,33 @@ namespace Umbraco.Web.Website.Routing
|
||||
// Check if there is no existing content and return the no content controller
|
||||
if (!_umbracoContextAccessor.UmbracoContext.Content.HasContent())
|
||||
{
|
||||
values["controller"] = ControllerExtensions.GetControllerName<RenderNoContentController>();
|
||||
values["action"] = nameof(RenderNoContentController.Index);
|
||||
values[ControllerToken] = ControllerExtensions.GetControllerName<RenderNoContentController>();
|
||||
values[ActionToken] = nameof(RenderNoContentController.Index);
|
||||
|
||||
return await Task.FromResult(values);
|
||||
return values;
|
||||
}
|
||||
|
||||
IPublishedRequest publishedRequest = await RouteRequestAsync(_umbracoContextAccessor.UmbracoContext);
|
||||
|
||||
UmbracoRouteValues routeDef = _routeValuesFactory.Create(httpContext, values, publishedRequest);
|
||||
UmbracoRouteValues umbracoRouteValues = _routeValuesFactory.Create(httpContext, publishedRequest);
|
||||
|
||||
values["controller"] = routeDef.ControllerName;
|
||||
if (string.IsNullOrWhiteSpace(routeDef.ActionName) == false)
|
||||
// Store the route values as a httpcontext feature
|
||||
httpContext.Features.Set(umbracoRouteValues);
|
||||
|
||||
// Need to check if there is form data being posted back to an Umbraco URL
|
||||
PostedDataProxyInfo postedInfo = GetFormInfo(httpContext, values);
|
||||
if (postedInfo != null)
|
||||
{
|
||||
values["action"] = routeDef.ActionName;
|
||||
return HandlePostedValues(postedInfo, httpContext, values);
|
||||
}
|
||||
|
||||
return await Task.FromResult(values);
|
||||
values[ControllerToken] = umbracoRouteValues.ControllerName;
|
||||
if (string.IsNullOrWhiteSpace(umbracoRouteValues.ActionName) == false)
|
||||
{
|
||||
values[ActionToken] = umbracoRouteValues.ActionName;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private async Task<IPublishedRequest> RouteRequestAsync(IUmbracoContext umbracoContext)
|
||||
@@ -123,5 +149,92 @@ namespace Umbraco.Web.Website.Routing
|
||||
|
||||
return routedRequest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the request and query strings to see if it matches the definition of having a Surface controller
|
||||
/// posted/get value, if so, then we return a PostedDataProxyInfo object with the correct information.
|
||||
/// </summary>
|
||||
private PostedDataProxyInfo GetFormInfo(HttpContext httpContext, RouteValueDictionary values)
|
||||
{
|
||||
if (httpContext is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContext));
|
||||
}
|
||||
|
||||
// if it is a POST/GET then a value must be in the request
|
||||
if (!httpContext.Request.Query.TryGetValue("ufprt", out StringValues encodedVal)
|
||||
&& (!httpContext.Request.HasFormContentType || !httpContext.Request.Form.TryGetValue("ufprt", out encodedVal)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!EncryptionHelper.DecryptAndValidateEncryptedRouteString(
|
||||
_dataProtectionProvider,
|
||||
encodedVal,
|
||||
out IDictionary<string, string> decodedParts))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get all route values that are not the default ones and add them separately so they eventually get to action parameters
|
||||
foreach (KeyValuePair<string, string> item in decodedParts.Where(x => ReservedAdditionalKeys.AllKeys.Contains(x.Key) == false))
|
||||
{
|
||||
values[item.Key] = item.Value;
|
||||
}
|
||||
|
||||
// return the proxy info without the surface id... could be a local controller.
|
||||
return new PostedDataProxyInfo
|
||||
{
|
||||
ControllerName = WebUtility.UrlDecode(decodedParts.First(x => x.Key == ReservedAdditionalKeys.Controller).Value),
|
||||
ActionName = WebUtility.UrlDecode(decodedParts.First(x => x.Key == ReservedAdditionalKeys.Action).Value),
|
||||
Area = WebUtility.UrlDecode(decodedParts.First(x => x.Key == ReservedAdditionalKeys.Area).Value),
|
||||
};
|
||||
}
|
||||
|
||||
private RouteValueDictionary HandlePostedValues(PostedDataProxyInfo postedInfo, HttpContext httpContext, RouteValueDictionary values)
|
||||
{
|
||||
// set the standard route values/tokens
|
||||
values[ControllerToken] = postedInfo.ControllerName;
|
||||
values[ActionToken] = postedInfo.ActionName;
|
||||
|
||||
ControllerActionSearchResult surfaceControllerQueryResult = _controllerActionSearcher.Find<SurfaceController>(postedInfo.ControllerName, postedInfo.ActionName);
|
||||
|
||||
if (surfaceControllerQueryResult == null || !surfaceControllerQueryResult.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find a Surface controller route in the RouteTable for controller name " + postedInfo.ControllerName);
|
||||
}
|
||||
|
||||
// set the area if one is there.
|
||||
if (!postedInfo.Area.IsNullOrWhiteSpace())
|
||||
{
|
||||
values["area"] = postedInfo.Area;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private class PostedDataProxyInfo
|
||||
{
|
||||
public string ControllerName { get; set; }
|
||||
|
||||
public string ActionName { get; set; }
|
||||
|
||||
public string Area { get; set; }
|
||||
}
|
||||
|
||||
// Define reserved dictionary keys for controller, action and area specified in route additional values data
|
||||
private static class ReservedAdditionalKeys
|
||||
{
|
||||
internal static readonly string[] AllKeys = new[]
|
||||
{
|
||||
Controller,
|
||||
Action,
|
||||
Area
|
||||
};
|
||||
|
||||
internal const string Controller = "c";
|
||||
internal const string Action = "a";
|
||||
internal const string Area = "ar";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Routing;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Common.Controllers;
|
||||
using Umbraco.Web.Common.Routing;
|
||||
using Umbraco.Web.Features;
|
||||
using Umbraco.Web.Routing;
|
||||
@@ -20,7 +21,7 @@ namespace Umbraco.Web.Website.Routing
|
||||
private readonly IUmbracoRenderingDefaults _renderingDefaults;
|
||||
private readonly IShortStringHelper _shortStringHelper;
|
||||
private readonly UmbracoFeatures _umbracoFeatures;
|
||||
private readonly HijackedRouteEvaluator _hijackedRouteEvaluator;
|
||||
private readonly IControllerActionSearcher _controllerActionSearcher;
|
||||
private readonly IPublishedRouter _publishedRouter;
|
||||
private readonly Lazy<string> _defaultControllerName;
|
||||
|
||||
@@ -31,13 +32,13 @@ namespace Umbraco.Web.Website.Routing
|
||||
IUmbracoRenderingDefaults renderingDefaults,
|
||||
IShortStringHelper shortStringHelper,
|
||||
UmbracoFeatures umbracoFeatures,
|
||||
HijackedRouteEvaluator hijackedRouteEvaluator,
|
||||
IControllerActionSearcher controllerActionSearcher,
|
||||
IPublishedRouter publishedRouter)
|
||||
{
|
||||
_renderingDefaults = renderingDefaults;
|
||||
_shortStringHelper = shortStringHelper;
|
||||
_umbracoFeatures = umbracoFeatures;
|
||||
_hijackedRouteEvaluator = hijackedRouteEvaluator;
|
||||
_controllerActionSearcher = controllerActionSearcher;
|
||||
_publishedRouter = publishedRouter;
|
||||
_defaultControllerName = new Lazy<string>(() => ControllerExtensions.GetControllerName(_renderingDefaults.DefaultControllerType));
|
||||
}
|
||||
@@ -48,18 +49,13 @@ namespace Umbraco.Web.Website.Routing
|
||||
protected string DefaultControllerName => _defaultControllerName.Value;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public UmbracoRouteValues Create(HttpContext httpContext, RouteValueDictionary values, IPublishedRequest request)
|
||||
public UmbracoRouteValues Create(HttpContext httpContext, IPublishedRequest request)
|
||||
{
|
||||
if (httpContext is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContext));
|
||||
}
|
||||
|
||||
if (values is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
}
|
||||
|
||||
if (request is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
@@ -90,9 +86,6 @@ namespace Umbraco.Web.Website.Routing
|
||||
|
||||
def = CheckNoTemplate(def);
|
||||
|
||||
// store the route definition
|
||||
values.TryAdd(Constants.Web.UmbracoRouteDefinitionDataToken, def);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -106,7 +99,7 @@ namespace Umbraco.Web.Website.Routing
|
||||
var customControllerName = request.PublishedContent?.ContentType?.Alias;
|
||||
if (customControllerName != null)
|
||||
{
|
||||
HijackedRouteResult hijackedResult = _hijackedRouteEvaluator.Evaluate(customControllerName, def.TemplateName);
|
||||
ControllerActionSearchResult hijackedResult = _controllerActionSearcher.Find<IRenderController>(customControllerName, def.TemplateName);
|
||||
if (hijackedResult.Success)
|
||||
{
|
||||
return new UmbracoRouteValues(
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Redirects to an Umbraco page by Id or Entity
|
||||
/// </summary>
|
||||
/// Migrated already to .Net Core
|
||||
public class RedirectToUmbracoPageResult : ActionResult
|
||||
{
|
||||
private IPublishedContent _publishedContent;
|
||||
private readonly int _pageId;
|
||||
private readonly Guid _key;
|
||||
private NameValueCollection _queryStringValues;
|
||||
private IPublishedUrlProvider _publishedUrlProvider;
|
||||
private string _url;
|
||||
|
||||
public string Url
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_url.IsNullOrWhiteSpace()) return _url;
|
||||
|
||||
if (PublishedContent == null)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Cannot redirect, no entity was found for id {0}", _pageId));
|
||||
}
|
||||
|
||||
var result = _publishedUrlProvider.GetUrl(PublishedContent.Id);
|
||||
if (result != "#")
|
||||
{
|
||||
_url = result;
|
||||
return _url;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(string.Format("Could not route to entity with id {0}, the NiceUrlProvider could not generate a URL", _pageId));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public int PageId
|
||||
{
|
||||
get { return _pageId; }
|
||||
}
|
||||
|
||||
public Guid Key
|
||||
{
|
||||
get { return _key; }
|
||||
}
|
||||
public IPublishedContent PublishedContent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_publishedContent != null) return _publishedContent;
|
||||
|
||||
if (_pageId != default(int))
|
||||
{
|
||||
_publishedContent = Current.UmbracoContext.Content.GetById(_pageId);
|
||||
}
|
||||
|
||||
else if (_key != default(Guid))
|
||||
{
|
||||
_publishedContent = Current.UmbracoContext.Content.GetById(_key);
|
||||
}
|
||||
|
||||
return _publishedContent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
public RedirectToUmbracoPageResult(int pageId)
|
||||
: this(pageId, Current.PublishedUrlProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <param name="queryStringValues"></param>
|
||||
public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues)
|
||||
: this(pageId, queryStringValues, Current.PublishedUrlProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <param name="queryString"></param>
|
||||
public RedirectToUmbracoPageResult(int pageId, string queryString)
|
||||
: this(pageId, queryString, Current.PublishedUrlProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="publishedContent"></param>
|
||||
public RedirectToUmbracoPageResult(IPublishedContent publishedContent)
|
||||
: this(publishedContent, Current.PublishedUrlProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="publishedContent"></param>
|
||||
/// <param name="queryStringValues"></param>
|
||||
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, NameValueCollection queryStringValues)
|
||||
: this(publishedContent, queryStringValues, Current.PublishedUrlProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="queryString"></param>
|
||||
/// <param name="queryStringValues"></param>
|
||||
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, string queryString)
|
||||
: this(publishedContent, queryString, Current.PublishedUrlProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <param name="umbracoContextAccessor"></param>
|
||||
public RedirectToUmbracoPageResult(int pageId, IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
_pageId = pageId;
|
||||
_publishedUrlProvider = publishedUrlProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <param name="queryStringValues"></param>
|
||||
/// <param name="umbracoContextAccessor"></param>
|
||||
public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues, IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
_pageId = pageId;
|
||||
_queryStringValues = queryStringValues;
|
||||
_publishedUrlProvider = publishedUrlProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <param name="queryString"></param>
|
||||
/// <param name="umbracoContextAccessor"></param>
|
||||
public RedirectToUmbracoPageResult(int pageId, string queryString, IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
_pageId = pageId;
|
||||
_queryStringValues = ParseQueryString(queryString);
|
||||
_publishedUrlProvider = publishedUrlProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="umbracoContextAccessor"></param>
|
||||
public RedirectToUmbracoPageResult(Guid key)
|
||||
{
|
||||
_key = key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="queryStringValues"></param>
|
||||
public RedirectToUmbracoPageResult(Guid key, NameValueCollection queryStringValues)
|
||||
{
|
||||
_key = key;
|
||||
_queryStringValues = queryStringValues;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="queryString"></param>
|
||||
public RedirectToUmbracoPageResult(Guid key, string queryString)
|
||||
{
|
||||
_key = key;
|
||||
_queryStringValues = ParseQueryString(queryString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="publishedContent"></param>
|
||||
/// <param name="umbracoContextAccessor"></param>
|
||||
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
_publishedContent = publishedContent;
|
||||
_pageId = publishedContent.Id;
|
||||
_publishedUrlProvider = publishedUrlProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="publishedContent"></param>
|
||||
/// <param name="queryStringValues"></param>
|
||||
/// <param name="umbracoContextAccessor"></param>
|
||||
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, NameValueCollection queryStringValues, IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
_publishedContent = publishedContent;
|
||||
_pageId = publishedContent.Id;
|
||||
_queryStringValues = queryStringValues;
|
||||
_publishedUrlProvider = publishedUrlProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="publishedContent"></param>
|
||||
/// <param name="queryString"></param>
|
||||
/// <param name="umbracoContextAccessor"></param>
|
||||
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, string queryString, IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
_publishedContent = publishedContent;
|
||||
_pageId = publishedContent.Id;
|
||||
_queryStringValues = ParseQueryString(queryString);
|
||||
_publishedUrlProvider = publishedUrlProvider;
|
||||
}
|
||||
|
||||
public override void ExecuteResult(ControllerContext context)
|
||||
{
|
||||
if (context == null) throw new ArgumentNullException("context");
|
||||
|
||||
if (context.IsChildAction)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot redirect from a Child Action");
|
||||
}
|
||||
|
||||
var destinationUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);
|
||||
|
||||
if (_queryStringValues != null && _queryStringValues.Count > 0)
|
||||
{
|
||||
destinationUrl = destinationUrl += "?" + string.Join("&",
|
||||
_queryStringValues.AllKeys.Select(x => x + "=" + HttpUtility.UrlEncode(_queryStringValues[x])));
|
||||
}
|
||||
|
||||
context.Controller.TempData.Keep();
|
||||
|
||||
context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
|
||||
}
|
||||
|
||||
private NameValueCollection ParseQueryString(string queryString)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(queryString))
|
||||
{
|
||||
return HttpUtility.ParseQueryString(queryString);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using System;
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Redirects to the current URL rendering an Umbraco page including it's query strings
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is useful if you need to redirect
|
||||
/// to the current page but the current page is actually a rewritten URL normally done with something like
|
||||
/// Server.Transfer. It is also handy if you want to persist the query strings.
|
||||
/// </remarks>
|
||||
/// Migrated already to .Net Core
|
||||
public class RedirectToUmbracoUrlResult : ActionResult
|
||||
{
|
||||
private readonly IUmbracoContext _umbracoContext;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RedirectToUmbracoResult
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
public RedirectToUmbracoUrlResult(IUmbracoContext umbracoContext)
|
||||
{
|
||||
_umbracoContext = umbracoContext;
|
||||
}
|
||||
|
||||
public override void ExecuteResult(ControllerContext context)
|
||||
{
|
||||
if (context == null) throw new ArgumentNullException("context");
|
||||
|
||||
if (context.IsChildAction)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot redirect from a Child Action");
|
||||
}
|
||||
|
||||
var destinationUrl = _umbracoContext.OriginalRequestUrl.PathAndQuery;
|
||||
context.Controller.TempData.Keep();
|
||||
|
||||
context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,12 @@ using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using System.Web.SessionState;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Web.Features;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Core.Strings;
|
||||
using Current = Umbraco.Web.Composing.Current;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
@@ -27,29 +23,21 @@ namespace Umbraco.Web.Mvc
|
||||
internal const string Area = "ar";
|
||||
}
|
||||
|
||||
private readonly IControllerFactory _controllerFactory;
|
||||
private readonly IShortStringHelper _shortStringHelper;
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly IUmbracoContext _umbracoContext;
|
||||
|
||||
public RenderRouteHandler(IUmbracoContextAccessor umbracoContextAccessor, IControllerFactory controllerFactory, IShortStringHelper shortStringHelper)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_controllerFactory = controllerFactory ?? throw new ArgumentNullException(nameof(controllerFactory));
|
||||
_shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper));
|
||||
}
|
||||
|
||||
public RenderRouteHandler(IUmbracoContext umbracoContext, IControllerFactory controllerFactory, IShortStringHelper shortStringHelper)
|
||||
{
|
||||
_umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext));
|
||||
_controllerFactory = controllerFactory ?? throw new ArgumentNullException(nameof(controllerFactory));
|
||||
_shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper));
|
||||
}
|
||||
|
||||
private IUmbracoContext UmbracoContext => _umbracoContext ?? _umbracoContextAccessor.UmbracoContext;
|
||||
|
||||
private UmbracoFeatures Features => Current.Factory.GetRequiredService<UmbracoFeatures>(); // TODO: inject
|
||||
|
||||
#region IRouteHandler Members
|
||||
|
||||
/// <summary>
|
||||
@@ -74,21 +62,10 @@ namespace Umbraco.Web.Mvc
|
||||
|
||||
#endregion
|
||||
|
||||
private void UpdateRouteDataForRequest(ContentModel contentModel, RequestContext requestContext)
|
||||
{
|
||||
if (contentModel == null) throw new ArgumentNullException(nameof(contentModel));
|
||||
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
|
||||
|
||||
// requestContext.RouteData.DataTokens[Core.Constants.Web.UmbracoDataToken] = contentModel;
|
||||
// the rest should not change -- it's only the published content that has changed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the request and query strings to see if it matches the definition of having a Surface controller
|
||||
/// posted/get value, if so, then we return a PostedDataProxyInfo object with the correct information.
|
||||
/// </summary>
|
||||
/// <param name="requestContext"></param>
|
||||
/// <returns></returns>
|
||||
internal static PostedDataProxyInfo GetFormInfo(RequestContext requestContext)
|
||||
{
|
||||
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
|
||||
@@ -144,8 +121,6 @@ namespace Umbraco.Web.Mvc
|
||||
/// Handles a posted form to an Umbraco URL and ensures the correct controller is routed to and that
|
||||
/// the right DataTokens are set.
|
||||
/// </summary>
|
||||
/// <param name="requestContext"></param>
|
||||
/// <param name="postedInfo"></param>
|
||||
internal static IHttpHandler HandlePostedValues(RequestContext requestContext, PostedDataProxyInfo postedInfo)
|
||||
{
|
||||
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
|
||||
|
||||
@@ -10,9 +10,6 @@ using Umbraco.Web.Composing;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for front-end add-in controllers.
|
||||
/// </summary>
|
||||
/// Migrated already to .Net Core without MergeModelStateToChildAction and MergeParentContextViewData action filters
|
||||
/// TODO: Migrate MergeModelStateToChildAction and MergeParentContextViewData action filters
|
||||
[MergeModelStateToChildAction]
|
||||
@@ -26,197 +23,5 @@ namespace Umbraco.Web.Mvc
|
||||
: base(umbracoContextAccessor, databaseFactory, services, appCaches,profilingLogger)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(pageId, Current.PublishedUrlProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id and passes provided querystring
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <param name="queryStringValues"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, NameValueCollection queryStringValues)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(pageId, queryStringValues, Current.PublishedUrlProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id and passes provided querystring
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <param name="queryString"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, string queryString)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(pageId, queryString, Current.PublishedUrlProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(Guid key)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id and passes provided querystring
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <param name="queryStringValues"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(Guid key, NameValueCollection queryStringValues)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(key, queryStringValues);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id and passes provided querystring
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <param name="queryString"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(Guid key, string queryString)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(key, queryString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id
|
||||
/// </summary>
|
||||
/// <param name="publishedContent"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(publishedContent, Current.PublishedUrlProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id and passes provided querystring
|
||||
/// </summary>
|
||||
/// <param name="publishedContent"></param>
|
||||
/// <param name="queryStringValues"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent, NameValueCollection queryStringValues)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(publishedContent, queryStringValues, Current.PublishedUrlProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id and passes provided querystring
|
||||
/// </summary>
|
||||
/// <param name="publishedContent"></param>
|
||||
/// <param name="queryString"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent, string queryString)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(publishedContent, queryString, Current.PublishedUrlProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the currently rendered Umbraco page
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage()
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(CurrentPage, Current.PublishedUrlProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the currently rendered Umbraco page and passes provided querystring
|
||||
/// </summary>
|
||||
/// <param name="queryStringValues"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage(NameValueCollection queryStringValues)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(CurrentPage, queryStringValues, Current.PublishedUrlProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the currently rendered Umbraco page and passes provided querystring
|
||||
/// </summary>
|
||||
/// <param name="queryString"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage(string queryString)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(CurrentPage, queryString, Current.PublishedUrlProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the currently rendered Umbraco URL
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// this is useful if you need to redirect
|
||||
/// to the current page but the current page is actually a rewritten URL normally done with something like
|
||||
/// Server.Transfer.
|
||||
/// </remarks>
|
||||
protected RedirectToUmbracoUrlResult RedirectToCurrentUmbracoUrl()
|
||||
{
|
||||
return new RedirectToUmbracoUrlResult(UmbracoContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently rendered Umbraco page
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected UmbracoPageResult CurrentUmbracoPage()
|
||||
{
|
||||
return new UmbracoPageResult(ProfilingLogger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current page.
|
||||
/// </summary>
|
||||
protected virtual IPublishedContent CurrentPage
|
||||
{
|
||||
get
|
||||
{
|
||||
var routeDefAttempt = TryGetRouteDefinitionFromAncestorViewContexts();
|
||||
if (routeDefAttempt.Success == false)
|
||||
throw routeDefAttempt.Exception;
|
||||
|
||||
var routeDef = routeDefAttempt.Result;
|
||||
return routeDef.PublishedRequest.PublishedContent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// we need to recursively find the route definition based on the parent view context
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// We may have Child Actions within Child actions so we need to recursively look this up.
|
||||
/// see: http://issues.umbraco.org/issue/U4-1844
|
||||
/// </remarks>
|
||||
private Attempt<RouteDefinition> TryGetRouteDefinitionFromAncestorViewContexts()
|
||||
{
|
||||
var currentContext = ControllerContext;
|
||||
while (currentContext != null)
|
||||
{
|
||||
var currentRouteData = currentContext.RouteData;
|
||||
if (currentRouteData.Values.ContainsKey(Core.Constants.Web.UmbracoRouteDefinitionDataToken))
|
||||
{
|
||||
return Attempt.Succeed((RouteDefinition)currentRouteData.Values[Core.Constants.Web.UmbracoRouteDefinitionDataToken]);
|
||||
}
|
||||
|
||||
currentContext = currentContext.IsChildAction
|
||||
? currentContext.ParentActionViewContext
|
||||
: null;
|
||||
}
|
||||
return Attempt<RouteDefinition>.Fail(
|
||||
new InvalidOperationException("Cannot find the Umbraco route definition in the route values, the request must be made in the context of an Umbraco request"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Used by posted forms to proxy the result to the page in which the current URL matches on
|
||||
/// </summary>
|
||||
/// Migrated already to .Net Core
|
||||
public class UmbracoPageResult : ActionResult
|
||||
{
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
|
||||
public UmbracoPageResult(IProfilingLogger profilingLogger)
|
||||
{
|
||||
_profilingLogger = profilingLogger;
|
||||
}
|
||||
|
||||
public override void ExecuteResult(ControllerContext context)
|
||||
{
|
||||
ResetRouteData(context.RouteData);
|
||||
|
||||
ValidateRouteData(context.RouteData);
|
||||
|
||||
var routeDef = (RouteDefinition)context.RouteData.Values[Umbraco.Core.Constants.Web.UmbracoRouteDefinitionDataToken];
|
||||
|
||||
var factory = ControllerBuilder.Current.GetControllerFactory();
|
||||
context.RouteData.Values["action"] = routeDef.ActionName;
|
||||
ControllerBase controller = null;
|
||||
|
||||
try
|
||||
{
|
||||
controller = CreateController(context, factory, routeDef);
|
||||
|
||||
CopyControllerData(context, controller);
|
||||
|
||||
ExecuteControllerAction(context, controller);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanupController(controller, factory);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the controller action
|
||||
/// </summary>
|
||||
private void ExecuteControllerAction(ControllerContext context, IController controller)
|
||||
{
|
||||
using (_profilingLogger.TraceDuration<UmbracoPageResult>("Executing Umbraco RouteDefinition controller", "Finished"))
|
||||
{
|
||||
controller.Execute(context.RequestContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Since we could be returning the current page from a surface controller posted values in which the routing values are changed, we
|
||||
/// need to revert these values back to nothing in order for the normal page to render again.
|
||||
/// </summary>
|
||||
private static void ResetRouteData(RouteData routeData)
|
||||
{
|
||||
routeData.DataTokens["area"] = null;
|
||||
routeData.DataTokens["Namespaces"] = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that the current page execution is not being handled by the normal umbraco routing system
|
||||
/// </summary>
|
||||
private static void ValidateRouteData(RouteData routeData)
|
||||
{
|
||||
if (routeData.Values.ContainsKey(Umbraco.Core.Constants.Web.UmbracoRouteDefinitionDataToken) == false)
|
||||
{
|
||||
throw new InvalidOperationException("Can only use " + typeof(UmbracoPageResult).Name +
|
||||
" in the context of an Http POST when using a SurfaceController form");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure ModelState, ViewData and TempData is copied across
|
||||
/// </summary>
|
||||
private static void CopyControllerData(ControllerContext context, ControllerBase controller)
|
||||
{
|
||||
controller.ViewData.ModelState.Merge(context.Controller.ViewData.ModelState);
|
||||
|
||||
foreach (var d in context.Controller.ViewData)
|
||||
controller.ViewData[d.Key] = d.Value;
|
||||
|
||||
//We cannot simply merge the temp data because during controller execution it will attempt to 'load' temp data
|
||||
// but since it has not been saved, there will be nothing to load and it will revert to nothing, so the trick is
|
||||
// to Save the state of the temp data first then it will automatically be picked up.
|
||||
// http://issues.umbraco.org/issue/U4-1339
|
||||
|
||||
var targetController = controller as Controller;
|
||||
var sourceController = context.Controller as Controller;
|
||||
if (targetController != null && sourceController != null)
|
||||
{
|
||||
targetController.TempDataProvider = sourceController.TempDataProvider;
|
||||
targetController.TempData = sourceController.TempData;
|
||||
targetController.TempData.Save(sourceController.ControllerContext, sourceController.TempDataProvider);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a controller using the controller factory
|
||||
/// </summary>
|
||||
private static ControllerBase CreateController(ControllerContext context, IControllerFactory factory, RouteDefinition routeDef)
|
||||
{
|
||||
var controller = factory.CreateController(context.RequestContext, routeDef.ControllerName) as ControllerBase;
|
||||
|
||||
if (controller == null)
|
||||
throw new InvalidOperationException("Could not create controller with name " + routeDef.ControllerName + ".");
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up the controller by releasing it using the controller factory, and by disposing it.
|
||||
/// </summary>
|
||||
private static void CleanupController(IController controller, IControllerFactory factory)
|
||||
{
|
||||
if (controller != null)
|
||||
factory.ReleaseController(controller);
|
||||
|
||||
if (controller != null)
|
||||
controller.DisposeIfDisposable();
|
||||
}
|
||||
|
||||
private class DummyView : IView
|
||||
{
|
||||
public void Render(ViewContext viewContext, TextWriter writer)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using System.Web.Mvc;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Composing;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// If Umbraco.Core.UseHttps property in web.config is set to true, this filter will redirect any http access to https.
|
||||
/// </summary>
|
||||
public class UmbracoRequireHttpsAttribute : RequireHttpsAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// If Umbraco.Core.UseHttps is true and we have a non-HTTPS request, handle redirect.
|
||||
/// </summary>
|
||||
/// <param name="filterContext">Filter context</param>
|
||||
protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
|
||||
{
|
||||
// If Umbraco.Core.UseHttps is set, let base method handle redirect. Otherwise, we don't care.
|
||||
if (/*Current.Configs.Global().UseHttps*/ false)
|
||||
{
|
||||
base.HandleNonHttpsRequest(filterContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if HTTPS is currently being used if Umbraco.Core.UseHttps is true.
|
||||
/// </summary>
|
||||
/// <param name="filterContext">Filter context</param>
|
||||
public override void OnAuthorization(AuthorizationContext filterContext)
|
||||
{
|
||||
// If umbracoSSL is set, let base method handle checking for HTTPS. Otherwise, we don't care.
|
||||
if (/*Current.Configs.Global().UseHttps*/ false)
|
||||
{
|
||||
base.OnAuthorization(filterContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -180,10 +180,8 @@
|
||||
<Compile Include="WebApi\SessionHttpControllerRouteHandler.cs" />
|
||||
<Compile Include="WebApi\UmbracoApiControllerTypeCollectionBuilder.cs" />
|
||||
<Compile Include="Runtime\WebInitialComponent.cs" />
|
||||
<Compile Include="Mvc\UmbracoRequireHttpsAttribute.cs" />
|
||||
<Compile Include="Mvc\ProfilingView.cs" />
|
||||
<Compile Include="Mvc\NotFoundHandler.cs" />
|
||||
<Compile Include="Mvc\RedirectToUmbracoUrlResult.cs" />
|
||||
<Compile Include="Mvc\UmbracoVirtualNodeByIdRouteHandler.cs" />
|
||||
<Compile Include="Mvc\EnsurePublishedContentRequestAttribute.cs" />
|
||||
<Compile Include="Mvc\UmbracoVirtualNodeRouteHandler.cs" />
|
||||
@@ -216,7 +214,6 @@
|
||||
<Compile Include="Mvc\MergeModelStateToChildActionAttribute.cs" />
|
||||
<Compile Include="Mvc\PluginController.cs" />
|
||||
<Compile Include="Mvc\PostedDataProxyInfo.cs" />
|
||||
<Compile Include="Mvc\RedirectToUmbracoPageResult.cs" />
|
||||
<Compile Include="Mvc\Strings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
@@ -224,7 +221,6 @@
|
||||
</Compile>
|
||||
<Compile Include="Mvc\SurfaceController.cs" />
|
||||
<Compile Include="Mvc\PluginControllerAttribute.cs" />
|
||||
<Compile Include="Mvc\UmbracoPageResult.cs" />
|
||||
<Compile Include="RouteCollectionExtensions.cs" />
|
||||
<Compile Include="UmbracoHelper.cs" />
|
||||
<Compile Include="Mvc\ViewDataContainerExtensions.cs" />
|
||||
|
||||
Reference in New Issue
Block a user