Learn with me about the static file processing of .NetCore

Preface

Nowadays, the front-end and back-end separation development model is in full swing, and the development responsibilities are more distinct (of course, the model that the front-end and back-end work together has not completely faded); for each company's product implementation, the deployment model will be slightly different, and some will separate the front-end files Deploy as a site, and some will integrate the front-end files and the back-end sites together; usually when the project scale is relatively large, separate site deployment is a good choice, management and maintenance are clear, and for some small projects, integrated together Deploying as a site is relatively convenient. After all, sometimes the development is you, the deployment is you, and the maintenance is also you; if you choose integrated deployment, or the project contains static files (such as pictures) access, the following content is useful Ground~~~

text

The request pipeline of Asp.NetCore is constructed by registering middleware according to the requirements (the construction process reference: learn with me the introduction of the middleware of .NetCore and the construction of the parsing request pipeline ), and the project created through the template, request There are only a few key middlewares in the pipeline by default. If you have other needs, you can add registrations yourself. The static file middleware is not available by default, as in the following cases:

The result of the above example is that the added index.html cannot be accessed. Some friends may say that it is because there is no directory added, but this is not the reason; now try to register the static file middleware:

Why create the wwwroot directory? Can't other directories work?

When registering the static file middleware, you can see through the constructor (see the screenshot of the static file middleware constructor below), you can specify the corresponding static file directory, when the directory is not specified, the WebRootFileProvider in IHostingEnvironment will be used by default. And WebRootFileProvider specifies wwwroot by default:

Specify in the extension method Initialize of IHostingEnvironment;

I won't go to the code one by one here. If you are interested, you can follow the following ideas:

How to specify the directory, you should see in the process of picking up the code, you can pass parameters to specify when registering the middleware, as follows:

You can register multiple static file middleware according to your needs. As shown above, when a request is made to the request pipeline, it will first find a matching file in the wwwroot directory. If it can’t find the next middleware, go to the specified myFile directory to match file.

Often in the development process, the related static files are classified, and the Url address is also different. Usually, when registering the middleware, the corresponding static file directory is mapped to the specified Url directory, as follows:

Friends who have engaged in IIS should know the configuration of setting default files, which can also be achieved through ready-made middleware, as follows:

Registering middleware implementation, which can reduce configuration is of course also a good choice:

At this point, friends should try it, change the name of index.html in the wwwroot directory, and run it again. The same access URL address will definitely not be accessible. If it can, it is estimated that there is a cache. You can clear the cache. Try again; then why? The positioning is very precise. It must be the middleware of the default file. Let’s take a look at how it is implemented:

// 定义默认文件中间件
public class DefaultFilesMiddleware
{
    // 选项配置
    private readonly DefaultFilesOptions _options;
    private readonly PathString _matchUrl;
    private readonly RequestDelegate _next;
    // 静态文件目录读取Provider,默认目录是wwwroot
    private readonly IFileProvider _fileProvider;
    // 构造函数,用于初始化对应的变量
    public DefaultFilesMiddleware(RequestDelegate next, IWebHostEnvironment hostingEnv, IOptions<DefaultFilesOptions> options)
    {
        // 校验参数
        if (next == null)
        {
            throw new ArgumentNullException(nameof(next));
        }
        if (hostingEnv == null)
        {
            throw new ArgumentNullException(nameof(hostingEnv));
        }
        if (options == null)
        {
            throw new ArgumentNullException(nameof(options));
        }
        _next = next;
        // 初始化配置信息
        _options = options.Value;
        // 如果没有指定对应的IFileProvider,就用IWebHostEnvironment的WebRootFileProvider,默认目录就wwwroot
        _fileProvider = _options.FileProvider ?? Helpers.ResolveFileProvider(hostingEnv);
        _matchUrl = _options.RequestPath;
    }
    // 默认文件中间件的关键方法
    public Task Invoke(HttpContext context)
    {
        if (context.GetEndpoint() == null &&
            Helpers.IsGetOrHeadMethod(context.Request.Method)
            && Helpers.TryMatchPath(context, _matchUrl, forDirectory: true, subpath: out var subpath))
        {
            var dirContents = _fileProvider.GetDirectoryContents(subpath.Value);
            if (dirContents.Exists)
            {
                // 依次遍历默认文件,检查对应文件是否在指定目录中存在,这里是关键
                for (int matchIndex = 0; matchIndex < _options.DefaultFileNames.Count; matchIndex++)
                {
                    string defaultFile = _options.DefaultFileNames[matchIndex];
                    var file = _fileProvider.GetFileInfo(subpath.Value + defaultFile);
                    // TryMatchPath will make sure subpath always ends with a "/" by adding it if needed.
                    if (file.Exists)
                    {
                        // 如果路径与目录匹配,但没有以斜杠结尾,则重定向以添加斜杠.
                        // This prevents relative links from breaking.
                        if (!Helpers.PathEndsInSlash(context.Request.Path))
                        {
                            context.Response.StatusCode = StatusCodes.Status301MovedPermanently;
                            var request = context.Request;
                            var redirect = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path + "/", request.QueryString);
                            context.Response.Headers[HeaderNames.Location] = redirect;
                            return Task.CompletedTask;
                        }


                        // 如果匹配找到,就重写请求地址,由下一个中间件处理,所以在个中间件的注册一定要在UseStaticFiles前面,否则会报错
                        context.Request.Path = new PathString(context.Request.Path.Value + defaultFile);
                        break;
                    }
                }
            }
        }
        // 执行下一个中间件
        return _next(context);
    }
}

In the middleware Invoke method, traverse _options.DefaultFileNames to match, but we did not specify it. I guess there should be a default setting. Go and see the corresponding DefaultFilesOptions:

public DefaultFilesOptions(SharedOptions sharedOptions)
           : base(sharedOptions)
{
    // 果然,在构造函数中指定了默认列表
    DefaultFileNames = new List<string>
            {
                "default.htm",
                "default.html",
                "index.htm",
                "index.html",
            };
}

Sure enough, there is a corresponding default list in the constructor of DefaultFilesOptions, is it suddenly clear now~~~; What if you must specify other files? Old rules, pass parameters when registering middleware:

Isn’t it simple? Let’s have another requirement. For example, if you want to build an online file management system, you must access the directory. You can’t access it now. You can try it; 

It can be achieved by registering the middleware. Do you think middleware is very flexible and powerful:

Here are not examples of parameter settings one by one. The usage is almost the same as the UseStaticFiles parameter. If you are interested, you can try it privately.

In fact, Microsoft has long thought of doing this for a while and doing that for a while, so it directly provides a full-featured middleware, directly UseFileServer, which can be configured for each item mentioned above, as follows:

In fact, the internal is to integrate the middleware mentioned above, the following source code:

The detailed configuration here will not be configured and tested one by one. The use is the same as when registering the middleware separately, here it is just integrated.

to sum up

Speaking of a good partial application, I still can't hold back the code, but I feel that the appropriate one can make it clearer; the next section will talk about the best practice of routing.

Guess you like

Origin blog.csdn.net/sD7O95O/article/details/108839262