虽然ASP.NET Core是一款“动态”的Web服务端框架,但是由它接收并处理的大部分是针对静态文件的请求,最常见的是开发Web站点使用的3种静态文件(JavaScript脚本、CSS样式和图片)。ASP.NET Core提供了3个中间件来处理针对静态文件的请求,利用它们不仅可以将物理文件发布为可以通过HTTP请求获取的Web资源,还可以将所在的物理目录的结构呈现出来。通过HTTP请求获取的Web资源大部分来源于存储在服务器磁盘上的静态文件。对于ASP.NET Core应用来说,如果将静态文件存储到约定的目录下,绝大部分文件类型都是可以通过Web的形式对外发布的。基于静态文件的请求由3个中间件负责处理,它们均定义在NuGet包“Microsoft.AspNetCore.StaticFiles”中,利用这3个中间件完全可以搭建一个基于Web的文件服务器,下面做相关的实例演示。

目录
一、发布物理文件

二、呈现目录结构

三、显示默认页面

四、映射媒体类型

一、发布物理文件

我们创建的演示实例是一个简单的ASP.NET Core应用,它的项目结构如下图所示。在默认作为WebRoot的“wwwroot”目录下,可以将JavaScript脚本文件、CSS样式文件和图片文件存放到对应的子目录(js、css和img)下。WebRoot目录下的所有文件将自动发布为Web资源,客户端可以访问相应的URL来读取对应文件的内容。

针对具体某个静态文件的请求是通过一个名为StaticFileMiddleware的中间件来处理的。如下面的代码片段所示,承载ASP.NET Core应用的程序中调用IApplicationBuilder接口的UseStaticFiles扩展方法注册的就是这样一个中间件。

public class Program
{
public static void Main()
{
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder=>builder.Configure(app => app.UseStaticFiles()))
.Build()
.Run();
}
}

上述程序运行之后,就可以通过GET请求的方式来读取对应文件的内容。请求采用的URL由目标文件的路径决定。具体来说,目标文件相对于WebRoot目录的路径就是对应URL的路径,如JPG图片文件“~/wwwroot/img/dolphin1.jpg”对应的URL路径为“/img/dolphin1.jpg”。如果直接利用浏览器访问这个URL,目标图片就会直接以下图所示的形式显示出来。

上面通过一个简单的实例将WebRoot所在目录下的所有静态文件发布为Web资源,如果需要发布的静态文件存储在其他目录下呢?下面将上面演示的应用程序的一些文档存储在下图所示的“~/doc/”目录下,那么对应的程序又该如何编写?

ASP.NET Core应用在大部分情况下都是利用一个IFileProvider对象来读取文件的针对静态文件的读取请求也不例外。对于IApplicationBuilder接口的UseStaticFiles扩展方法注册的StaticFileMiddleware中间件来说,它的内部维护着一个IFileProvider对象和请求路径的映射关系。如果调用UseStaticFiles方法没有指定任何参数,那么这个映射关系的请求路径就是应用的基地址(PathBase),对应的IFileProvider对象自然就是指向WebRoot目录的PhysicalFileProvider对象。

上述需求可以通过显式定制这个映射关系的方式来实现。如下面的代码片段所示,我们在现有程序的基础上额外添加了一次针对UseStaticFiles扩展方法的调用,在本次调用中指定一个对应的Options对象(一个类型为StaticFileOptions的对象)作为参数来定制请求路径(“/documents”)与对应IFileProvider对象(针对路径“~/doc/”的PhysicalFileProvider对象)之间的映射关系。

public class Program
{
public static void Main()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var options = new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(path),
RequestPath = "/documents"
};
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app
.UseStaticFiles()
.UseStaticFiles(options)))
.Build()
.Run();
}
}

按照上面这段程序指定的映射关系,对于存储在“~/doc/”目录下的这个PDF文件(checklist.pdf),对应URL的路径就应该是“/documents/checklist.pdf”。如果利用浏览器请求这个地址时,PDF文件的内容就会按照下图所示的形式显示在浏览器上。

二、呈现目录结构

上面的演示实例注册的StaticFileMiddleware中间件只会处理针对具体的某个静态文件的请求,如果利用浏览器发送一个针对目录的请求(如“http://localhost:5000/img/”),得到的将是一个状态为“404 Not Found”的响应。如果希望浏览器呈现出目标目录的结构,就可以注册另一个名为DirectoryBrowserMiddleware的中间件。这个中间件会返回一个HTML页面,请求目录下的结构会以表格的形式显示在这个页面中。我们演示的程序可以按照如下方式调用IApplicationBuilder接口的UseDirectoryBrowser扩展方法来注册DirectoryBrowserMiddleware中间件。

public class Program
{
public static void Main()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var fileProvider = new PhysicalFileProvider(path); var fileOptions = new StaticFileOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
}; var diretoryOptions = new DirectoryBrowserOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
}; Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app
.UseStaticFiles()
.UseStaticFiles(fileOptions)
.UseDirectoryBrowser()
.UseDirectoryBrowser(diretoryOptions)))

.Build()
.Run();
}
}

当上面的应用启动之后,如果利用浏览器向针对某个目录的URL(如“http://localhost:5000/”或者“http://localhost:5000/img/”)发起请求,目标目录的内容(包括子目录和文件)就会以图14-5所示的形式显示在一个表格中。可以看出,在呈现的表格中,当前目录的子目录和文件均会显示为链接。

三、显示默认页面

从安全的角度来讲,利用注册的UseDirectoryBrowser中间件会将整个目标目录的结构和所有文件全部暴露出来,所以这个中间件需要根据自身的安全策略谨慎使用。对于针对目录的请求,更加常用的处理策略就是显示一个保存在这个目录下的默认页面。默认页面文件一般采用如下4种命名约定:default.htm、default.html、index.htm和index.html。针对默认页面的呈现实现在一个名为DefaultFilesMiddleware的中间件中,我们演示的这个应用就可以按照如下方式调用IApplicationBuilder接口的UseDefaultFiles扩展方法来注册这个中间件。

public class Program
{
public static void Main()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var fileProvider = new PhysicalFileProvider(path); var fileOptions = new StaticFileOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var diretoryOptions = new DirectoryBrowserOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var defaultOptions = new DefaultFilesOptions
{
RequestPath = "/documents",
FileProvider = fileProvider,
}; Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app
.UseDefaultFiles()
.UseDefaultFiles(defaultOptions)

.UseStaticFiles()
.UseStaticFiles(fileOptions)
.UseDirectoryBrowser()
.UseDirectoryBrowser(diretoryOptions)))
.Build()
.Run();
}
}

下面在“~/wwwroot/img/”目录和“~/doc”目录下分别创建一个名为index.html的默认页面,并且在该.html文件的主体部分指定一段简短的文字(This is an index page!)。在应用启动之后,可以利用浏览器访问这两个目录对应的URL(“http://localhost:5000/img/”和“http://localhost:5000/documents/”),下图显示的就是这个默认页面的内容。

必须在注册StaticFileMiddleware中间件和DirectoryBrowserMiddleware中间件之前注册DefaultFilesMiddleware中间件,否则它无法发挥作用。这是因为DirectoryBrowserMiddleware中间件和DefaultFilesMiddleware中间件处理的均是针对目录的请求,如果先注册DirectoryBrowserMiddleware中间件,那么显示的总是目录的结构;如果先注册用于显示默认页面的DefaultFilesMiddleware中间件,那么在默认页面不存在的情况下它会将请求分发给后续中间件,而DirectoryBrowserMiddleware中间件会接收请求的处理并将当前目录的结构呈现出来。

要先于StaticFileMiddleware中间件之前注册DefaultFilesMiddleware中间件是因为后者是通过采用URL重写的方式实现的,也就是说,这个中间件会将针对目录的请求改写成针对默认页面的请求,而最终针对默认页面的请求还需要依赖StaticFileMiddleware中间件来完成。DefaultFilesMiddleware中间件在默认情况下总是以约定的名称(default.htm、default.html、index.htm和index.html)在当前请求的目录下定位默认页面。如果作为默认页面的文件没有采用这样的约定命名(如我们将默认页面命名为readme.html),就需要按照如下方式显式指定默认页面的文件名。

public class Program
{
public static void Main()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var fileProvider = new PhysicalFileProvider(path);
var fileOptions = new StaticFileOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var diretoryOptions = new DirectoryBrowserOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var defaultOptions1 = new DefaultFilesOptions();
var defaultOptions2 = new DefaultFilesOptions
{
RequestPath = "/documents",
FileProvider = fileProvider,
}; defaultOptions1.DefaultFileNames.Add("readme.html");
defaultOptions2.DefaultFileNames.Add("readme.html");
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app
.UseDefaultFiles(defaultOptions1)
.UseDefaultFiles(defaultOptions2)
.UseStaticFiles()
.UseStaticFiles(fileOptions)
.UseDirectoryBrowser()
.UseDirectoryBrowser(diretoryOptions)))
.Build()
.Run();
}
}

四、映射媒体类型

通过上面演示的实例可以看出,浏览器能够准确地将请求的目标文件的内容正常呈现出来。对HTTP协议具有基本了解的读者应该都知道:响应文件能够在浏览器上被正常显示的基本前提是响应报文通过Content-Type报头携带的媒体类型必须与内容一致。我们的实例演示了针对两种文件类型的请求,一种是JPG文件,另一种是PDF文件,对应的媒体类型分别是image/jpg和application/pdf,那么用来处理静态文件请求的StaticFileMiddleware中间件是如何解析出对应的媒体类型的?

StaticFileMiddleware中间件针对媒体类型的解析是通过一个IContentTypeProvider对象来完成的,默认采用的是该接口的实现类型FileExtensionContentTypeProvider。顾名思义,FileExtensionContentTypeProvider根据文件的扩展命名来解析媒体类型。FileExtensionContentTypeProvider内部预定了数百种常用文件扩展名与对应媒体类型之间的映射关系,所以如果发布的静态文件具有标准的扩展名,那么StaticFileMiddleware中间件就能为对应的响应赋予正确的媒体类型。

如果某个文件的扩展名没有在预定义的映射之中,或者需要某个预定义的扩展名匹配不同的媒体类型,那么应该如何解决?同样是针对我们演示的这个实例,笔者将~/wwwroot/img/ dolphin1.jpg文件的扩展名改成.img,毫无疑问,StaticFileMiddleware中间件将无法为针对该文件的请求解析出正确的媒体类型。这个问题具有若干不同的解决方案,第一种方案就是按照如下方式让StaticFileMiddleware中间件支持不能识别的文件类型,并为它们设置一个默认的媒体类型。

public class Program
{
public static void Main()
{
var options = new StaticFileOptions
{
ServeUnknownFileTypes = true,
DefaultContentType = "image/jpg"
}; Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app.UseStaticFiles(options)))
.Build()
.Run();
}
}

上述解决方案只能设置一种默认媒体类型,如果具有多种需要映射成不同媒体类型的文件类型,采用这种方案就达不到目的,所以最根本的解决方案还是需要将不能识别的文件类型和对应的媒体类型进行映射。由于StaticFileMiddleware中间件使用的IContentTypeProvider对象是可以定制的,所以可以按照如下方式显式地为该中间件指定一个FileExtensionContentTypeProvider对象,然后将缺失的映射添加到这个对象上。

public class Program
{
public static void Main()
{
var contentTypeProvider = new FileExtensionContentTypeProvider();
contentTypeProvider.Mappings.Add(".img", "image/jpg");
var options = new StaticFileOptions
{
ContentTypeProvider = contentTypeProvider
};
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app.UseStaticFiles(options)))
.Build()
.Run();
}
}

ASP.NET Core静态文件中间件[1]: 搭建文件服务器
ASP.NET Core静态文件中间件[2]: 条件请求以提升性能
ASP.NET Core静态文件中间件[3]: 区间请求以提供部分内容
ASP.NET Core静态文件中间件[4]: StaticFileMiddleware
ASP.NET Core静态文件中间件[5]: DirectoryBrowserMiddleware & DefaultFilesMiddleware

ASP.NET Core静态文件中间件[1]: 搭建文件服务器的更多相关文章

  1. ASP.NET Core 静态文件 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 静态文件 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 静态文件 前几章节中,我们学习了 ASP.NET Core 的中间件 ...

  2. ASP.NET Core 静态文件

    静态文件(HTML,CSS,图片和Javascript之类的资源)会被ASP.NET Core应用直接提供给客户端. 静态文件通常位于网站根目录(web root) <content-root& ...

  3. 细说ASP.NET Core静态文件的缓存方式

    一.前言 我们在优化Web服务的时候,对于静态的资源文件,通常都是通过客户端缓存.服务器缓存.CDN缓存,这三种方式来缓解客户端对于Web服务器的连接请求压力的. 本文指在这三个方面,在ASP.NET ...

  4. ASP.NET Core静态文件处理源码探究

    前言     静态文件(如 HTML.CSS.图像和 JavaScript)等是Web程序的重要组成部分.传统的ASP.NET项目一般都是部署在IIS上,IIS是一个功能非常强大的服务器平台,可以直接 ...

  5. ASP.NET Core 静态文件及JS包管理器(npm, Bower)的使用

    在 ASP.NET Core 中添加静态文件 虽然ASP.NET主要大都做着后端的事情,但前端的一些静态文件也是很重要的.在ASP.NET Core中要启用静态文件,需要Microsoft.AspNe ...

  6. .Net Core静态文件中间件StaticFiles的使用

    以前,当我们的网站需要显示图片的时候,直接在网站目录下新建文件夹,把图片放在这个文件夹下,然后通过文件夹的路径就可以访问到. 但是在.net core中不可以这样,要通过中间件StaticFiles配 ...

  7. asp .net core 静态文件资源

    前言 对静态资源的简单的一个概况,在<重新整理.net core 计1400篇>系列后面会深入. 正文 我们在加入中间件是这样写的: app.UseStaticFiles(); 默认是给w ...

  8. 《ASP.NET Core 高性能系列》静态文件中间件

    一.概述 静态文件(如 HTML.CSS.图片和 JavaScript等文件)是 Web程序直接提供给客户端的直接加载的文件. 较比于程序动态交互的代码而言,其实原理都一样(走Http协议), ASP ...

  9. 15.ASP.NET Core 应用程序中的静态文件中间件

    在这篇文章中,我将向大家介绍,如何使用中间件组件来处理静态文件.这篇文章中,我们讨论下面几个问题: 在ASP.NET Core中,我们需要把静态文件存放在哪里? 在ASP.NET Core中 wwwr ...

随机推荐

  1. 6、Sping Boot消息

    1.消息概述 可通过消息服务中间件来提升系统异步通信.扩展解耦能力 消息服务中两个重要概念:消息代理(message broker)和目的地(destination)当消息发送者发送消息以后,将由消息 ...

  2. powershell过杀软工具-xencrypt

           在红队攻击中,绕杀软是一个比较常见的技术.对于绕过杀软的方法,有基于黑白名单的,有基于shellloader的,也有基于加密与混淆的.最近在发现了这样一款过杀软的工具,推荐给有缘人,嘻嘻 ...

  3. [i春秋]“百度杯”CTF比赛 十月场-Hash

    前言 涉及知识点:反序列化.代码执行.命令执行 题目来自:i春秋 hash  如果i春秋题目有问题可以登录榆林学院信息安全协会CTF平台使用 或者利用本文章提供的源码自主复现 [i春秋]"百 ...

  4. 自学linux——8.firewall-cmd的命令使用

    firewall-cmd使用方法 1.firewall-cmd的含义 firewall-cmd 是 firewalld的字符界面管理工具,firewalld是centos7之后版本的一大特性, 最大的 ...

  5. Java 虚拟机运行时数据区详解

    本文摘自深入理解 Java 虚拟机第三版 概述 Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟 ...

  6. Verilog单周期CPU(未完待续)

    单周期CPU:指令周期=CPU周期 Top模块作为数据通路 运算器中有ALU,通路寄存器(R1.R2.R3.R4),数据缓冲寄存器(鉴于书上的运算器只有R0)........... 此为ALU和通用寄 ...

  7. python核心高级学习总结2----------pdb的调试

    PDB调试 def getAverage(a,b): result =a+b print("result=%d"%result) return result a=100 b=200 ...

  8. moviepy音视频剪辑:视频基类VideoClip子类DataVideoClip、UpdatedVideoClip、ImageClip、ColorClip、TextClip类详解

    ☞ ░ 前往老猿Python博文目录 ░ 一.概述 在<moviepy音视频剪辑:moviepy中的剪辑相关类及关系>介绍了剪辑相关类及关系,其中VideoClip有多个直接子类和间接子类 ...

  9. moviepy音视频剪辑:音视频的加载和输出

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 一.概述 在本地进行音视频处理时,首先要从视频文件 ...

  10. PyQt(Python+Qt)学习随笔:QMainWindow的addDockWidget方法增加QDockWidget停靠窗到主窗口

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 DockWidget除了放在QMainWindow窗口内外,也可以放在 ...