Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Normalize paths returned by view location expanders
Browse files Browse the repository at this point in the history
Fixes #6448
  • Loading branch information
pranavkm committed Jun 26, 2017
1 parent 03e555a commit 51081fd
Show file tree
Hide file tree
Showing 15 changed files with 187 additions and 128 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. 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.Extensions.Primitives;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ public RazorPageFactoryResult CreateFactory(string relativePath)
var pageFactory = Expression
.Lambda<Func<IRazorPage>>(objectInitializeExpression)
.Compile();
return new RazorPageFactoryResult(pageFactory, viewDescriptor.ExpirationTokens, viewDescriptor.IsPrecompiled);
return new RazorPageFactoryResult(viewDescriptor, pageFactory);
}
else
{
return new RazorPageFactoryResult(viewDescriptor.ExpirationTokens);
return new RazorPageFactoryResult(viewDescriptor, razorPageFactory: null);
}
}
}
Expand Down
64 changes: 7 additions & 57 deletions src/Microsoft.AspNetCore.Mvc.Razor/RazorPageFactoryResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// 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.Extensions.Primitives;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;

namespace Microsoft.AspNetCore.Mvc.Razor
{
Expand All @@ -12,61 +11,18 @@ namespace Microsoft.AspNetCore.Mvc.Razor
/// </summary>
public struct RazorPageFactoryResult
{
/// <summary>
/// Initializes a new instance of <see cref="RazorPageFactoryResult"/> with the
/// specified <paramref name="expirationTokens"/>.
/// </summary>
/// <param name="expirationTokens">One or more <see cref="IChangeToken"/> instances.</param>
public RazorPageFactoryResult(IList<IChangeToken> expirationTokens)
{
if (expirationTokens == null)
{
throw new ArgumentNullException(nameof(expirationTokens));
}

ExpirationTokens = expirationTokens;
RazorPageFactory = null;
IsPrecompiled = false;
}

/// <summary>
/// Initializes a new instance of <see cref="RazorPageFactoryResult"/> with the
/// specified <see cref="IRazorPage"/> factory.
/// </summary>
/// <param name="razorPageFactory">The <see cref="IRazorPage"/> factory.</param>
/// <param name="expirationTokens">One or more <see cref="IChangeToken"/> instances.</param>
/// <param name="viewDescriptor">The <see cref="CompiledViewDescriptor"/>.</param>
public RazorPageFactoryResult(
Func<IRazorPage> razorPageFactory,
IList<IChangeToken> expirationTokens)
: this(razorPageFactory, expirationTokens, isPrecompiled: false)
CompiledViewDescriptor viewDescriptor,
Func<IRazorPage> razorPageFactory)
{
}

/// <summary>
/// Initializes a new instance of <see cref="RazorPageFactoryResult"/> with the
/// specified <see cref="IRazorPage"/> factory.
/// </summary>
/// <param name="razorPageFactory">The <see cref="IRazorPage"/> factory.</param>
/// <param name="expirationTokens">One or more <see cref="IChangeToken"/> instances.</param>
/// <param name="isPrecompiled"><c>true</c> if the view is precompiled, <c>false</c> otherwise.</param>
public RazorPageFactoryResult(
Func<IRazorPage> razorPageFactory,
IList<IChangeToken> expirationTokens,
bool isPrecompiled)
{
if (razorPageFactory == null)
{
throw new ArgumentNullException(nameof(razorPageFactory));
}

if (expirationTokens == null)
{
throw new ArgumentNullException(nameof(expirationTokens));
}

ViewDescriptor = viewDescriptor ?? throw new ArgumentNullException(nameof(viewDescriptor));
RazorPageFactory = razorPageFactory;
ExpirationTokens = expirationTokens;
IsPrecompiled = isPrecompiled;
}

/// <summary>
Expand All @@ -76,19 +32,13 @@ public RazorPageFactoryResult(
public Func<IRazorPage> RazorPageFactory { get; }

/// <summary>
/// One or more <see cref="IChangeToken"/>s associated with this instance of
/// <see cref="RazorPageFactoryResult"/>.
/// Gets the <see cref="CompiledViewDescriptor"/>.
/// </summary>
public IList<IChangeToken> ExpirationTokens { get; }
public CompiledViewDescriptor ViewDescriptor { get; }

/// <summary>
/// Gets a value that determines if the page was successfully located.
/// </summary>
public bool Success => RazorPageFactory != null;

/// <summary>
/// Gets a value that determines if the view is precompiled.
/// </summary>
public bool IsPrecompiled { get; }
}
}
43 changes: 17 additions & 26 deletions src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,21 +393,22 @@ internal ViewLocationCacheResult CreateCacheResult(
bool isMainPage)
{
var factoryResult = _pageFactory.CreateFactory(relativePath);
if (factoryResult.ExpirationTokens != null)
var viewDescriptor = factoryResult.ViewDescriptor;
if (viewDescriptor?.ExpirationTokens != null)
{
for (var i = 0; i < factoryResult.ExpirationTokens.Count; i++)
for (var i = 0; i < viewDescriptor.ExpirationTokens.Count; i++)
{
expirationTokens.Add(factoryResult.ExpirationTokens[i]);
expirationTokens.Add(viewDescriptor.ExpirationTokens[i]);
}
}

if (factoryResult.Success)
{
// Only need to lookup _ViewStarts for the main page.
var viewStartPages = isMainPage ?
GetViewStartPages(relativePath, expirationTokens) :
GetViewStartPages(viewDescriptor.RelativePath, expirationTokens) :
Array.Empty<ViewLocationCacheItem>();
if (factoryResult.IsPrecompiled)
if (viewDescriptor.IsPrecompiled)
{
_logger.PrecompiledViewFound(relativePath);
}
Expand All @@ -424,17 +425,23 @@ private IReadOnlyList<ViewLocationCacheItem> GetViewStartPages(
string path,
HashSet<IChangeToken> expirationTokens)
{
var applicationRelativePath = MakePathApplicationRelative(path);
if (IsApplicationRelativePath(path))
{
path = path.Substring(1);
}

var relativePath = ViewPath.NormalizePath(path);
var viewStartPages = new List<ViewLocationCacheItem>();

foreach (var viewStartProjectItem in _razorProject.FindHierarchicalItems(applicationRelativePath, ViewStartFileName))
foreach (var viewStartProjectItem in _razorProject.FindHierarchicalItems(relativePath, ViewStartFileName))
{
var result = _pageFactory.CreateFactory(viewStartProjectItem.Path);
if (result.ExpirationTokens != null)
var viewDescriptor = result.ViewDescriptor;
if (viewDescriptor?.ExpirationTokens != null)
{
for (var i = 0; i < result.ExpirationTokens.Count; i++)
for (var i = 0; i < viewDescriptor.ExpirationTokens.Count; i++)
{
expirationTokens.Add(result.ExpirationTokens[i]);
expirationTokens.Add(viewDescriptor.ExpirationTokens[i]);
}
}

Expand Down Expand Up @@ -476,22 +483,6 @@ private static bool IsApplicationRelativePath(string name)
return name[0] == '~' || name[0] == '/';
}

private string MakePathApplicationRelative(string path)
{
Debug.Assert(!string.IsNullOrEmpty(path));
if (path[0] == '~')
{
path = path.Substring(1);
}

if (path[0] != '/')
{
path = '/' + path;
}

return path;
}

private static bool IsRelativePath(string name)
{
Debug.Assert(!string.IsNullOrEmpty(name));
Expand Down
16 changes: 16 additions & 0 deletions test/Microsoft.AspNetCore.Mvc.FunctionalTests/ViewEngineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -476,5 +476,21 @@ public async Task RazorView_SetsViewPathAndExecutingPagePath()
ignoreLineEndingDifferences: true);
#endif
}

[Fact]
public async Task ViewEngine_NormalizesPathsReturnedByViewLocationExpanders()
{
// Arrange
var expected =
@"Layout
Page
Partial";

// Act
var responseContent = await Client.GetStringAsync("/BackSlash");

// Assert
Assert.Equal(expected, responseContent.Trim());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
public class DefaultRazorPageFactoryProviderTest
{
[Fact]
public void CreateFactory_ReturnsExpirationTokensFromCompilerCache_ForUnsuccessfulResults()
public void CreateFactory_ReturnsViewDescriptor_ForUnsuccessfulResults()
{
// Arrange
var path = "/file-does-not-exist";
Expand All @@ -39,11 +39,11 @@ public void CreateFactory_ReturnsExpirationTokensFromCompilerCache_ForUnsuccessf

// Assert
Assert.False(result.Success);
Assert.Equal(expirationTokens, result.ExpirationTokens);
Assert.Same(descriptor, result.ViewDescriptor);
}

[Fact]
public void CreateFactory_ReturnsExpirationTokensFromCompilerCache_ForSuccessfulResults()
public void CreateFactory_ReturnsViewDescriptor_ForSuccessfulResults()
{
// Arrange
var relativePath = "/file-exists";
Expand Down
Loading

0 comments on commit 51081fd

Please sign in to comment.