diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectResult.cs index 60c692d3ec..a9c9d86b1d 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectResult.cs @@ -3,6 +3,7 @@ using System; using Microsoft.AspNet.Mvc.Core; +using Microsoft.Framework.DependencyInjection; namespace Microsoft.AspNet.Mvc { @@ -30,10 +31,18 @@ namespace Microsoft.AspNet.Mvc public override void ExecuteResult([NotNull] ActionContext context) { - // It is redirected directly to the input URL. - // We would use the context to construct the full URL, - // only when relative URLs are supported. (Issue - WEBFX-202) - context.HttpContext.Response.Redirect(Url, Permanent); + var destinationUrl = Url; + var urlHelper = context.HttpContext + .RequestServices + .GetService(); + + // IsLocalUrl is called to handle Urls starting with '~/'. + if (urlHelper.IsLocalUrl(destinationUrl)) + { + destinationUrl = urlHelper.Content(Url); + } + + context.HttpContext.Response.Redirect(destinationUrl, Permanent); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectResultTest.cs new file mode 100644 index 0000000000..7a7f62b967 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectResultTest.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Routing; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Core.Test +{ + public class RedirectResultTest + { + [Theory] + [InlineData("", "/Home/About", "/Home/About")] + [InlineData("/myapproot", "/test", "/test")] + public void Execute_ReturnsContentPath_WhenItDoesNotStartWithTilde(string appRoot, + string contentPath, + string expectedPath) + { + // Arrange + var httpResponse = new Mock(); + httpResponse.Setup(o => o.Redirect(expectedPath, false)) + .Verifiable(); + + var httpContext = GetHttpContext(appRoot, contentPath, expectedPath, httpResponse.Object); + var actionContext = GetActionContext(httpContext); + var result = new RedirectResult(contentPath); + + // Act + result.ExecuteResult(actionContext); + + // Assert + // Verifying if Redirect was called with the specific Url and parameter flag. + httpResponse.Verify(); + } + + [Theory] + [InlineData(null, "~/Home/About", "/Home/About")] + [InlineData("/", "~/Home/About", "/Home/About")] + [InlineData("/", "~/", "/")] + [InlineData("", "~/Home/About", "/Home/About")] + [InlineData("/myapproot", "~/", "/myapproot/")] + [InlineData("", "~/Home/About", "/Home/About")] + [InlineData("/myapproot", "~/", "/myapproot/")] + public void Execute_ReturnsAppRelativePath_WhenItStartsWithTilde(string appRoot, + string contentPath, + string expectedPath) + { + // Arrange + var httpResponse = new Mock(); + httpResponse.Setup(o => o.Redirect(expectedPath, false)) + .Verifiable(); + + var httpContext = GetHttpContext(appRoot, contentPath, expectedPath, httpResponse.Object); + var actionContext = GetActionContext(httpContext); + var result = new RedirectResult(contentPath); + + // Act + result.ExecuteResult(actionContext); + + // Assert + // Verifying if Redirect was called with the specific Url and parameter flag. + httpResponse.Verify(); + } + + private static ActionContext GetActionContext(HttpContext httpContext) + { + return new ActionContext(httpContext, + Mock.Of(), + new Dictionary(), + new ActionDescriptor()); + } + + private static IServiceProvider GetServiceProvider(IUrlHelper urlHelper) + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddInstance(urlHelper); + return serviceCollection.BuildServiceProvider(); + } + + private static HttpContext GetHttpContext(string appRoot, + string contentPath, + string expectedPath, + HttpResponse response) + { + var httpContext = new Mock(); + var actionContext = GetActionContext(httpContext.Object); + var mockContentAccessor = new Mock>(); + mockContentAccessor.SetupGet(o => o.Value).Returns(actionContext); + var mockActionSelector = new Mock(); + var urlHelper = new UrlHelper(mockContentAccessor.Object, mockActionSelector.Object); + var serviceProvider = GetServiceProvider(urlHelper); + + httpContext.Setup(o => o.Response) + .Returns(response); + httpContext.SetupGet(o => o.RequestServices) + .Returns(serviceProvider); + httpContext.Setup(o => o.Request.PathBase) + .Returns(new PathString(appRoot)); + + return httpContext.Object; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj b/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj index 088dc97262..f150153062 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj @@ -25,6 +25,7 @@ +