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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
|
@ -29,7 +33,7 @@ namespace Microsoft.AspNetCore.Hosting.StaticWebAssets
|
||||||
|
|
||||||
public StaticWebAssetsFileProvider(string pathPrefix, string contentRoot)
|
public StaticWebAssetsFileProvider(string pathPrefix, string contentRoot)
|
||||||
{
|
{
|
||||||
BasePath = new PathString(pathPrefix.StartsWith("/") ? pathPrefix : "/" + pathPrefix);
|
BasePath = NormalizePath(pathPrefix);
|
||||||
InnerProvider = new PhysicalFileProvider(contentRoot);
|
InnerProvider = new PhysicalFileProvider(contentRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,20 +44,30 @@ namespace Microsoft.AspNetCore.Hosting.StaticWebAssets
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IDirectoryContents GetDirectoryContents(string subpath)
|
public IDirectoryContents GetDirectoryContents(string subpath)
|
||||||
{
|
{
|
||||||
if (!StartsWithBasePath(subpath, out var physicalPath))
|
var modifiedSub = NormalizePath(subpath);
|
||||||
{
|
|
||||||
return NotFoundDirectoryContents.Singleton;
|
if (StartsWithBasePath(modifiedSub, out var physicalPath))
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return InnerProvider.GetDirectoryContents(physicalPath.Value);
|
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 />
|
/// <inheritdoc />
|
||||||
public IFileInfo GetFileInfo(string subpath)
|
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);
|
return new NotFoundFileInfo(subpath);
|
||||||
}
|
}
|
||||||
|
|
@ -69,9 +83,69 @@ namespace Microsoft.AspNetCore.Hosting.StaticWebAssets
|
||||||
return InnerProvider.Watch(filter);
|
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)
|
private bool StartsWithBasePath(string subpath, out PathString rest)
|
||||||
{
|
{
|
||||||
return new PathString(subpath).StartsWithSegments(BasePath, FilePathComparison, out 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>
|
<PropertyGroup>
|
||||||
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
<Compile Include="$(SharedSourceRoot)EventSource.Testing\TestEventListener.cs" />
|
<Compile Include="$(SharedSourceRoot)EventSource.Testing\TestEventListener.cs" />
|
||||||
<Compile Include="$(SharedSourceRoot)EventSource.Testing\TestCounterListener.cs" />
|
<Compile Include="$(SharedSourceRoot)EventSource.Testing\TestCounterListener.cs" />
|
||||||
<Content Include="testroot\**\*" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
|
<Content Include="testroot\**\*" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
|
||||||
<None Remove="testroot\wwwroot\Static Web Assets.txt" />
|
|
||||||
<Content Include="Microsoft.AspNetCore.Hosting.StaticWebAssets.xml" CopyToOutputDirectory="PreserveNewest" />
|
<Content Include="Microsoft.AspNetCore.Hosting.StaticWebAssets.xml" CopyToOutputDirectory="PreserveNewest" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,75 @@ namespace Microsoft.AspNetCore.Hosting.StaticWebAssets
|
||||||
Assert.Equal("/_content", provider.BasePath);
|
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]
|
[Fact]
|
||||||
public void StaticWebAssetsFileProvider_FindsFileWithSpaces()
|
public void StaticWebAssetsFileProvider_FindsFileWithSpaces()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\..\Mvc.Formatters.Xml\test\XmlAssert.cs" />
|
<Compile Include="..\..\Mvc.Formatters.Xml\test\XmlAssert.cs" />
|
||||||
<EmbeddedResource Include="compiler\resources\**\*" />
|
<EmbeddedResource Include="compiler\resources\**\*" />
|
||||||
|
<None Remove="compiler\resources\TagHelpersWebSite.Home.GlobbingTagHelpers.html" />
|
||||||
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
public HttpClient EncodedClient { get; }
|
public HttpClient EncodedClient { get; }
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
[InlineData("GlobbingTagHelpers")]
|
||||||
[InlineData("Index")]
|
[InlineData("Index")]
|
||||||
[InlineData("About")]
|
[InlineData("About")]
|
||||||
[InlineData("Help")]
|
[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>
|
<IsTestAssetProject>true</IsTestAssetProject>
|
||||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
||||||
</ItemGroup>
|
</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();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IActionResult GlobbingTagHelpers()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
public IActionResult Help()
|
public IActionResult Help()
|
||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ namespace TagHelpersWebSite
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
app.UseStaticFiles();
|
||||||
app.UseEndpoints(endpoints =>
|
app.UseEndpoints(endpoints =>
|
||||||
{
|
{
|
||||||
endpoints.MapDefaultControllerRoute();
|
endpoints.MapDefaultControllerRoute();
|
||||||
|
|
@ -38,6 +39,7 @@ namespace TagHelpersWebSite
|
||||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||||
new WebHostBuilder()
|
new WebHostBuilder()
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseStaticWebAssets()
|
||||||
.UseStartup<Startup>()
|
.UseStartup<Startup>()
|
||||||
.UseKestrel()
|
.UseKestrel()
|
||||||
.UseIISIntegration();
|
.UseIISIntegration();
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,10 @@
|
||||||
<IsTestAssetProject>true</IsTestAssetProject>
|
<IsTestAssetProject>true</IsTestAssetProject>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\RazorPagesClassLibrary\RazorPagesClassLibrary.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
<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