【问题标题】:Make ASP.NET Core server (Kestrel) case sensitive on Windows在 Windows 上使 ASP.NET Core 服务器 (Kestrel) 区分大小写
【发布时间】:2018-10-10 08:11:22
【问题描述】:

在 Linux 容器中运行的 ASP.NET Core 应用使用区分大小写的文件系统,这意味着 CSS 和 JS 文件引用必须区分大小写。

但是,Windows 文件系统不区分大小写。因此,在开发过程中,您可以使用不正确的大小写引用 CSS 和 JS 文件,但它们可以正常工作。因此,在 Windows 上进行开发期间,您不会知道您的应用在 Linux 服务器上运行时会崩溃。

有没有办法让 Windows 上的 Kestrel 区分大小写,以便我们可以有一致的行为并在上线之前找到参考错误?

【问题讨论】:

  • 澄清一下,Kestrel 是不相关的,它是 PhysicalFileProvider 和 StaticFiles 组件为您执行此匹配。

标签: asp.net asp.net-mvc asp.net-core kestrel-http-server kestrel


【解决方案1】:

我在 ASP.NET Core 中使用中间件解决了这个问题。 而不是我使用的标准app.UseStaticFiles()

 if (env.IsDevelopment()) app.UseStaticFilesCaseSensitive();
 else app.UseStaticFiles();

并将该方法定义为:

/// <summary>
/// Enforces case-correct requests on Windows to make it compatible with Linux.
/// </summary>
public static IApplicationBuilder UseStaticFilesCaseSensitive(this IApplicationBuilder app)
{
    var fileOptions = new StaticFileOptions
    {
        OnPrepareResponse = x =>
        {
            if (!x.File.PhysicalPath.AsFile().Exists()) return;
            var requested = x.Context.Request.Path.Value;
            if (requested.IsEmpty()) return;

            var onDisk = x.File.PhysicalPath.AsFile().GetExactFullName().Replace("\\", "/");
            if (!onDisk.EndsWith(requested))
            {
                throw new Exception("The requested file has incorrect casing and will fail on Linux servers." +
                    Environment.NewLine + "Requested:" + requested + Environment.NewLine +
                    "On disk: " + onDisk.Right(requested.Length));
            }
        }
    };

    return app.UseStaticFiles(fileOptions);
}

其中也有:

public static string GetExactFullName(this FileSystemInfo @this)
{
    var path = @this.FullName;
    if (!File.Exists(path) && !Directory.Exists(path)) return path;

    var asDirectory = new DirectoryInfo(path);
    var parent = asDirectory.Parent;

    if (parent == null) // Drive:
        return asDirectory.Name.ToUpper();

    return Path.Combine(parent.GetExactFullName(), parent.GetFileSystemInfos(asDirectory.Name)[0].Name);
}

【讨论】:

  • 您也可以使用自定义 IFileProvider 在较低层执行此操作。
  • 感谢 Tracher。你有没有机会实现?
  • @Paymon 查看下面的答案
【解决方案2】:

根据@Tratcher 提议和this 博客文章,这里有一个解决方案,可以让物理文件提供程序区分大小写,您可以在其中选择强制区分大小写或允许任何大小写而不考虑操作系统。

public class CaseAwarePhysicalFileProvider : IFileProvider
{
    private readonly PhysicalFileProvider _provider;
    //holds all of the actual paths to the required files
    private static Dictionary<string, string> _paths;

    public bool CaseSensitive { get; set; } = false;

    public CaseAwarePhysicalFileProvider(string root)
    {
        _provider = new PhysicalFileProvider(root);
        _paths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    }

    public CaseAwarePhysicalFileProvider(string root, ExclusionFilters filters)
    {
        _provider = new PhysicalFileProvider(root, filters);
        _paths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    }

    public IFileInfo GetFileInfo(string subpath)
    {
        var actualPath = GetActualFilePath(subpath);
        if(CaseSensitive && actualPath != subpath) return new NotFoundFileInfo(subpath);
        return _provider.GetFileInfo(actualPath);
    }

    public IDirectoryContents GetDirectoryContents(string subpath)
    {
        var actualPath = GetActualFilePath(subpath);
        if(CaseSensitive && actualPath != subpath) return NotFoundDirectoryContents.Singleton;
        return _provider.GetDirectoryContents(actualPath);
    }

    public IChangeToken Watch(string filter) => _provider.Watch(filter);

    // Determines (and caches) the actual path for a file
    private string GetActualFilePath(string path)
    {
        // Check if this has already been matched before
        if (_paths.ContainsKey(path)) return _paths[path];

        // Break apart the path and get the root folder to work from
        var currPath = _provider.Root;
        var segments = path.Split(new [] { '/' }, StringSplitOptions.RemoveEmptyEntries);

        // Start stepping up the folders to replace with the correct cased folder name
        for (var i = 0; i < segments.Length; i++)
        {
            var part = segments[i];
            var last = i == segments.Length - 1;

            // Ignore the root
            if (part.Equals("~")) continue;

            // Process the file name if this is the last segment
            part = last ? GetFileName(part, currPath) : GetDirectoryName(part, currPath);

            // If no matches were found, just return the original string
            if (part == null) return path;

            // Update the actualPath with the correct name casing
            currPath = Path.Combine(currPath, part);
            segments[i] = part;
        }

        // Save this path for later use
        var actualPath = string.Join(Path.DirectorySeparatorChar, segments);
        _paths.Add(path, actualPath);
        return actualPath;
    }

    // Searches for a matching file name in the current directory regardless of case
    private static string GetFileName(string part, string folder) =>
        new DirectoryInfo(folder).GetFiles().FirstOrDefault(file => file.Name.Equals(part, StringComparison.OrdinalIgnoreCase))?.Name;

    // Searches for a matching folder in the current directory regardless of case
    private static string GetDirectoryName(string part, string folder) =>
        new DirectoryInfo(folder).GetDirectories().FirstOrDefault(dir => dir.Name.Equals(part, StringComparison.OrdinalIgnoreCase))?.Name;
}

然后在 Startup 类中,确保您为内容和 Web 根注册了一个提供程序,如下所示:

        _environment.ContentRootFileProvider = new CaseAwarePhysicalFileProvider(_environment.ContentRootPath);
        _environment.WebRootFileProvider = new CaseAwarePhysicalFileProvider(_environment.WebRootPath);

【讨论】:

  • 我正在使用 net core 6 并从代码 _provider = new PhysicalFileProvider(root); 得到错误 System.ArgumentException: 'The path must be absolute. (Parameter 'root')'
【解决方案3】:

这在Windows 7 中是可能的,但在windows 10 中是不可能的,据我所知,这在 Windows Server 上也是不可能的。

我只能谈论操作系统,因为 Kestrel 文档说:

UseDirectoryBrowserUseStaticFiles 公开的内容的URL 受底层文件系统的区分大小写和字符限制。例如,Windows 不区分大小写——macOS 和 Linux 不区分。

我建议所有文件名都采用约定(“全小写”通常效果最好)。要检查不一致,您可以运行一个简单的 PowerShell 脚本,该脚本使用正则表达式来检查大小写是否错误。为了方便起见,该脚本可以安排在日程表上。

【讨论】:

  • 我们已经有了始终使用小写字母的标准,但很容易出错并忽略该规则。
猜你喜欢
  • 2021-11-05
  • 2020-03-09
  • 2022-01-25
  • 2015-01-02
  • 2020-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-26
相关资源
最近更新 更多