优化HttpServer架构和注释,重构部分实现
石头 authored at 2026-01-01 19:16:01
2.53 KiB
X
using NewLife.Remoting;

namespace NewLife.Http;

/// <summary>静态文件处理器</summary>
/// <remarks>将指定目录映射为静态文件服务,支持常见的文件类型</remarks>
public class StaticFilesHandler : IHttpHandler
{
    #region 属性
    /// <summary>映射路径。如 /js/</summary>
    public String Path { get; set; } = null!;

    /// <summary>内容目录。如 /wwwroot/js</summary>
    public String ContentPath { get; set; } = null!;
    #endregion

    /// <summary>处理请求</summary>
    /// <param name="context">Http上下文</param>
    public virtual void ProcessRequest(IHttpContext context)
    {
        if (!context.Path.StartsWithIgnoreCase(Path)) throw new ApiException(ApiCode.NotFound, $"File {context.Path} not found");

        var file = context.Path[Path.Length..];
        file = ContentPath.CombinePath(file);

        // 路径安全检查,防止目录穿越
        if (!file.GetFullPath().StartsWithIgnoreCase(ContentPath.GetFullPath()))
            throw new ApiException(ApiCode.NotFound, $"File {context.Path} not found");

        var fi = file.AsFile();
        if (!fi.Exists) throw new ApiException(ApiCode.NotFound, $"File {context.Path} not found");

        var contentType = GetContentType(fi.Extension);

        // 确保使用完以后关闭文件流
        using var fs = fi.OpenRead();
        context.Response.SetResult(fs, contentType);
    }

    /// <summary>根据文件扩展名获取MIME类型</summary>
    /// <param name="extension">文件扩展名(含点号)</param>
    /// <returns>MIME类型;未知类型返回null</returns>
    protected virtual String? GetContentType(String extension) => extension switch
    {
        // 文本类型
        ".htm" or ".html" => "text/html",
        ".txt" or ".log" => "text/plain",
        ".xml" => "text/xml",
        ".css" => "text/css",
        ".csv" => "text/csv",

        // 脚本和数据
        ".js" => "text/javascript",
        ".json" => "application/json",
        ".map" => "application/json",

        // 图片类型
        ".png" => "image/png",
        ".jpg" or ".jpeg" => "image/jpeg",
        ".gif" => "image/gif",
        ".ico" => "image/x-icon",
        ".svg" => "image/svg+xml",
        ".webp" => "image/webp",

        // 字体类型
        ".woff" => "font/woff",
        ".woff2" => "font/woff2",
        ".ttf" => "font/ttf",
        ".eot" => "application/vnd.ms-fontobject",

        // 二进制类型
        ".zip" => "application/zip",
        ".pdf" => "application/pdf",

        _ => null,
    };
}