Use a "FakeRoot" to allow Globbing pattern to walk up the heirarchy (#14803)
Use a "FakeRoot" to allow Globbing pattern to walk up the heirarchy
This commit is contained in:
parent
a168e50d12
commit
405f002f58
|
|
@ -2,6 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
|
@ -29,7 +33,7 @@ namespace Microsoft.AspNetCore.Hosting.StaticWebAssets
|
|||
|
||||
public StaticWebAssetsFileProvider(string pathPrefix, string contentRoot)
|
||||
{
|
||||
BasePath = new PathString(pathPrefix.StartsWith("/") ? pathPrefix : "/" + pathPrefix);
|
||||
BasePath = NormalizePath(pathPrefix);
|
||||
InnerProvider = new PhysicalFileProvider(contentRoot);
|
||||
}
|
||||
|
||||
|
|
@ -40,20 +44,30 @@ namespace Microsoft.AspNetCore.Hosting.StaticWebAssets
|
|||
/// <inheritdoc />
|
||||
public IDirectoryContents GetDirectoryContents(string subpath)
|
||||
{
|
||||
if (!StartsWithBasePath(subpath, out var physicalPath))
|
||||
{
|
||||
return NotFoundDirectoryContents.Singleton;
|
||||
}
|
||||
else
|
||||
var modifiedSub = NormalizePath(subpath);
|
||||
|
||||
if (StartsWithBasePath(modifiedSub, out var physicalPath))
|
||||
{
|
||||
return InnerProvider.GetDirectoryContents(physicalPath.Value);
|
||||
}
|
||||
else if (string.Equals(subpath, string.Empty) || string.Equals(modifiedSub, "/"))
|
||||
{
|
||||
return new StaticWebAssetsDirectoryRoot(BasePath);
|
||||
}
|
||||
else if (BasePath.StartsWithSegments(modifiedSub, FilePathComparison, out var remaining))
|
||||
{
|
||||
return new StaticWebAssetsDirectoryRoot(remaining);
|
||||
}
|
||||
|
||||
return NotFoundDirectoryContents.Singleton;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IFileInfo GetFileInfo(string subpath)
|
||||
{
|
||||
if (!StartsWithBasePath(subpath, out var physicalPath))
|
||||
var modifiedSub = NormalizePath(subpath);
|
||||
|
||||
if (!StartsWithBasePath(modifiedSub, out var physicalPath))
|
||||
{
|
||||
return new NotFoundFileInfo(subpath);
|
||||
}
|
||||
|
|
@ -69,9 +83,69 @@ namespace Microsoft.AspNetCore.Hosting.StaticWebAssets
|
|||
return InnerProvider.Watch(filter);
|
||||
}
|
||||
|
||||
private static string NormalizePath(string path)
|
||||
{
|
||||
path = path.Replace('\\', '/');
|
||||
return path != null && path.StartsWith("/") ? path : "/" + path;
|
||||
}
|
||||
|
||||
private bool StartsWithBasePath(string subpath, out PathString rest)
|
||||
{
|
||||
return new PathString(subpath).StartsWithSegments(BasePath, FilePathComparison, out rest);
|
||||
}
|
||||
|
||||
private class StaticWebAssetsDirectoryRoot : IDirectoryContents
|
||||
{
|
||||
private readonly string _nextSegment;
|
||||
|
||||
public StaticWebAssetsDirectoryRoot(PathString remainingPath)
|
||||
{
|
||||
// We MUST use the Value property here because it is unescaped.
|
||||
_nextSegment = remainingPath.Value.Split("/", StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
|
||||
}
|
||||
|
||||
public bool Exists => true;
|
||||
|
||||
public IEnumerator<IFileInfo> GetEnumerator()
|
||||
{
|
||||
return GenerateEnum();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GenerateEnum();
|
||||
}
|
||||
|
||||
private IEnumerator<IFileInfo> GenerateEnum()
|
||||
{
|
||||
return new[] { new StaticWebAssetsFileInfo(_nextSegment) }
|
||||
.Cast<IFileInfo>().GetEnumerator();
|
||||
}
|
||||
|
||||
private class StaticWebAssetsFileInfo : IFileInfo
|
||||
{
|
||||
public StaticWebAssetsFileInfo(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public bool Exists => true;
|
||||
|
||||
public long Length => throw new NotImplementedException();
|
||||
|
||||
public string PhysicalPath => throw new NotImplementedException();
|
||||
|
||||
public DateTimeOffset LastModified => throw new NotImplementedException();
|
||||
|
||||
public bool IsDirectory => true;
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public Stream CreateReadStream()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
||||
|
|
@ -9,7 +9,6 @@
|
|||
<Compile Include="$(SharedSourceRoot)EventSource.Testing\TestEventListener.cs" />
|
||||
<Compile Include="$(SharedSourceRoot)EventSource.Testing\TestCounterListener.cs" />
|
||||
<Content Include="testroot\**\*" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
|
||||
<None Remove="testroot\wwwroot\Static Web Assets.txt" />
|
||||
<Content Include="Microsoft.AspNetCore.Hosting.StaticWebAssets.xml" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,75 @@ namespace Microsoft.AspNetCore.Hosting.StaticWebAssets
|
|||
Assert.Equal("/_content", provider.BasePath);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("\\", "_content")]
|
||||
[InlineData("\\_content\\RazorClassLib\\Dir", "Castle.Core.dll")]
|
||||
[InlineData("", "_content")]
|
||||
[InlineData("/", "_content")]
|
||||
[InlineData("/_content", "RazorClassLib")]
|
||||
[InlineData("/_content/RazorClassLib", "Dir")]
|
||||
[InlineData("/_content/RazorClassLib/Dir", "Microsoft.AspNetCore.Hosting.Tests.dll")]
|
||||
[InlineData("/_content/RazorClassLib/Dir/testroot/", "TextFile.txt")]
|
||||
[InlineData("/_content/RazorClassLib/Dir/testroot/wwwroot/", "README")]
|
||||
public void GetDirectoryContents_WalksUpContentRoot(string searchDir, string expected)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new StaticWebAssetsFileProvider("/_content/RazorClassLib/Dir", AppContext.BaseDirectory);
|
||||
|
||||
// Act
|
||||
var directory = provider.GetDirectoryContents(searchDir);
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(directory);
|
||||
Assert.Contains(directory, file => string.Equals(file.Name, expected));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDirectoryContents_DoesNotFindNonExistentFiles()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new StaticWebAssetsFileProvider("/_content/RazorClassLib/", AppContext.BaseDirectory);
|
||||
|
||||
// Act
|
||||
var directory = provider.GetDirectoryContents("/_content/RazorClassLib/False");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(directory);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/False/_content/RazorClassLib/")]
|
||||
[InlineData("/_content/RazorClass")]
|
||||
public void GetDirectoryContents_PartialMatchFails(string requestedUrl)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new StaticWebAssetsFileProvider("/_content/RazorClassLib", AppContext.BaseDirectory);
|
||||
|
||||
// Act
|
||||
var directory = provider.GetDirectoryContents(requestedUrl);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(directory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDirectoryContents_HandlesWhitespaceInBase()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new StaticWebAssetsFileProvider("/_content/Static Web Assets",
|
||||
Path.Combine(AppContext.BaseDirectory, "testroot", "wwwroot"));
|
||||
|
||||
// Act
|
||||
var directory = provider.GetDirectoryContents("/_content/Static Web Assets/Static Web/");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(directory,
|
||||
file =>
|
||||
{
|
||||
Assert.Equal("Static Web.txt", file.Name);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StaticWebAssetsFileProvider_FindsFileWithSpaces()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="..\..\Mvc.Formatters.Xml\test\XmlAssert.cs" />
|
||||
<EmbeddedResource Include="compiler\resources\**\*" />
|
||||
<None Remove="compiler\resources\TagHelpersWebSite.Home.GlobbingTagHelpers.html" />
|
||||
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
public HttpClient EncodedClient { get; }
|
||||
|
||||
[Theory]
|
||||
[InlineData("GlobbingTagHelpers")]
|
||||
[InlineData("Index")]
|
||||
[InlineData("About")]
|
||||
[InlineData("Help")]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Globbing Page - My ASP.NET Application</title>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="font-family: cursive;">ASP.NET vNext - Globbing Page</h1>
|
||||
<p>| <a href="/" style="background-color: gray;color: white;border-radius: 3px;border: 1px solid black;padding: 3px;font-family: cursive;">My Home</a>
|
||||
| <a href="/home/about" style="background-color: gray;color: white;border-radius: 3px;border: 1px solid black;padding: 3px;font-family: cursive;">My About</a>
|
||||
| <a href="/home/help" style="background-color: gray;color: white;border-radius: 3px;border: 1px solid black;padding: 3px;font-family: cursive;">My Help</a> |</p>
|
||||
<div>
|
||||
|
||||
|
||||
Globbing
|
||||
|
||||
<hr />
|
||||
<footer>
|
||||
|
||||
<!-- File wildcard -->
|
||||
<script src="/js/dist/dashboardHome.js"></script>
|
||||
<!-- RazorClassLib folder wildcard -->
|
||||
<script src="/_content/RazorPagesClassLibrary/razorlib/file.js"></script>
|
||||
<!-- RazorClassLib deep wildcard -->
|
||||
<script src="/js/dist/dashboardHome.js"></script><script src="/_content/RazorPagesClassLibrary/razorlib/file.js"></script>
|
||||
<!-- RazorClassLib Exclude local -->
|
||||
<script src="/_content/RazorPagesClassLibrary/razorlib/file.js"></script>
|
||||
<!-- local Exclude lib-->
|
||||
<script src="/js/dist/dashboardHome.js"></script>
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<IsTestAssetProject>true</IsTestAssetProject>
|
||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
window.exampleJsFunctions = {
|
||||
showPrompt: function (message) {
|
||||
return prompt(message, 'Type anything here')
|
||||
}
|
||||
};
|
||||
|
|
@ -25,6 +25,11 @@ namespace TagHelpersWebSite.Controllers
|
|||
return View();
|
||||
}
|
||||
|
||||
public IActionResult GlobbingTagHelpers()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Help()
|
||||
{
|
||||
return View();
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ namespace TagHelpersWebSite
|
|||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseRouting();
|
||||
app.UseStaticFiles();
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapDefaultControllerRoute();
|
||||
|
|
@ -38,6 +39,7 @@ namespace TagHelpersWebSite
|
|||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||
new WebHostBuilder()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseStaticWebAssets()
|
||||
.UseStartup<Startup>()
|
||||
.UseKestrel()
|
||||
.UseIISIntegration();
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@
|
|||
<IsTestAssetProject>true</IsTestAssetProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RazorPagesClassLibrary\RazorPagesClassLibrary.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
||||
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
@{
|
||||
ViewData["Title"] = "Globbing Page";
|
||||
}
|
||||
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
|
||||
@section footerContent {
|
||||
<!-- File wildcard -->
|
||||
<script asp-src-include="/js/dist/dashboard*.js"></script>
|
||||
<!-- RazorClassLib folder wildcard -->
|
||||
<script asp-src-include="/_content/RazorPagesClassLibrary/**/file.js"></script>
|
||||
<!-- RazorClassLib deep wildcard -->
|
||||
<script asp-src-include="/**/*.js"></script>
|
||||
<!-- RazorClassLib Exclude local -->
|
||||
<script asp-src-exclude="/js/dist/*.js" asp-src-include="**/*.js"></script>
|
||||
<!-- local Exclude lib-->
|
||||
<script asp-src-exclude="/_content/**/*.js" asp-src-include="**/*.js"></script>
|
||||
}
|
||||
Globbing
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
Loading…
Reference in New Issue