利用一个ASP.NET Core应用来发布静态文件

虽然ASP.NET Core是一款“动态”的Web服务端框架,但是在很多情况下都需要处理针对静态文件的请求,最为常见的就是这对JavaScript脚本文件、CSS样式文件和图片文件的请求。针对不同格式的静态文件请求的处理,ASP.NET Core为我们提供了三个中间件,它们将是本系列文章论述的重点。不过在针对对它们展开介绍之前,我们照理通过一些简单的实例来体验一下如何在一个ASP.NET Core应用中发布静态文件。[本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、以Web的形式读取文件
二、浏览目录内容
三、显示默认页面
四、映射媒体类型

一、以Web的形式读取文件

我们创建的演示实例是一个简单的ASP.NET Core控制台应用,它具有如下图所示的项目结构。我们可以看到在默认作为WebRoot的目录(wwwroot)下,我们将JavaScript脚本文件、CSS样式文件和图片文件存放到对应的子目录(js、css和img)下,我们将把这个目录的所有文件以Web的形式发布出来,客户端可以访问相应的URL来获取这些文件。

针对静态文件的请求是通过一个名为StaticFileMiddleware的中间件来实现的,这个中间件类型定义在NuGet包“Microsoft.AspNetCore.StaticFiles”中,所以我们需要预先按照这个NuGet包。整个应用只包含如下所示的这几行代码,StaticFileMiddleware这个中间件的注册是通过调用ApplicationBuilder的扩展方法UseStaticFiles来完成的。

   1: public class Program
   2: {

   3:     public static void Main()

   4:     {

   5:         new WebHostBuilder()

   6:             .UseContentRoot(Directory.GetCurrentDirectory())

   7:             .UseKestrel()

   8:             .Configure(app => app.UseStaticFiles())

   9:             .Build()

  10:             .Run();

  11:     }

  12: }

除了注册必需的StaticFileMiddleware中间件之外,我们还调用了WebHostBuilder的UseContentRoot方法将当前项目的根目录作为ContentRoot目录。我们知道ASP.NET Core应用具有两个重要的根目录,它们分别是ContentRoot和WebRoot,后者也是对外发布的静态文件默认使用的根目录。由于WebRoot目录的默认路径就是“{contentroot}/wwwroot”,所示上面这段程序就是将项目中的这个wwwroot目录下的所有静态文件发布出来。

当这个程序运行之后,我们就可以通过向对应URL发送HTTP请求的方式来获取某个的文件,这个URL由文件相当于wwwroot目录的路径来决定。比如JPG文件“~/wwwroot/img/dophin1.jpg”对应的URL为“http://
localhost:5000/img/dophin1.jpg”。我们直接利用浏览器访问这个URL,目标图片会直接显示出来。

上面我们通过一个简单的实例将WebRoot所在目录下的所有静态文件直接发布出来。如果我们需要发布的静态文件存储在其他目录下呢?依旧是演示的这个应用,现在我们将一些文档存储在如下图所示的“~/doc/”目录下并以Web的形式发布出来,我们的程序又该如何编写呢?

我们知道ASP.NET Core应用大部分情况下都是利用一个FileProvider对象来读取文件的,它在处理针对静态文件的请求是也不例外。对于我们调用ApplicationBuilder的扩展方法UseStaticFiles方法注册的这个类型为StaticFileMiddleware的中间件,其内部具有一个FileProvider和请求路径的映射关系。如果调用UseStaticFiles方法没有指定任何的参数,那么这个映射关系的请求路径就是应用的基地址(PathBase),而FileProvider自然就是指向WebRoot目录的PhysicalFileProvider。

上述的这个需求可以通过显式注册这个映射的方式来实现,为此我们在现有程序的基础上额外添加了一次针对UseStaticFiles方法的调用,并通过指定的参数(是一个StaticFileOptions对象)显式指定了采用的FileProvider(针对“~/doc/”的PhysicalFileProvider)和请求路径(“/documents”)。

   1: public class Program
   2: {

   3:     public static void Main()

   4:     {

   5:         string contentRoot = Directory.GetCurrentDirectory();

   6:         new WebHostBuilder()

   7:             .UseContentRoot(contentRoot)

   8:             .UseKestrel()

   9:             .Configure(app => app

  10:                 .UseStaticFiles()

  11:                 .UseStaticFiles(new StaticFileOptions {

  12:                      FileProvider = new PhysicalFileProvider(Path.Combine(contentRoot, "doc")),

  13:                      RequestPath = "/documents"

  14:                     }))

  15:             .Build()

  16:             .Run();

  17:     }

  18: }

按照上面这段程序指定的映射关系,对于存储在“~/doc/”目录下的这个PDF文件(“checklist.pdf”),发布在Web上的URL为“http://localhost:5000/documents/checklist.pdf”。当我们在浏览器上请求这个地址时,该PDF文件的内容将会按照如下图所示的形式显示在浏览器上。

二、浏览目录内容

注册的StaticFileMiddleware中间件只会处理针对某个具体静态文件的额请求,如果我们向针对某个目录的URL发送HTTP请求(比如“http://localhost:5000/img/”),得到的将是一个状态为404的响应。不过我们可以通过注册另一个名为DirectoryBrowserMiddleware的中间件来显示请求目录的内容。具体来说,这个中间件会返回一个HTML页面,请求目录下的所有文件将以表格的形式包含在这个页面中。对于我们演示的这个应用来说,我们可以按照如下的方式调用UseDirectoryBrowser方法来注册这个DirectoryBrowserMiddleware中间件。

   1: public class Program
   2: {

   3:     public static void Main()

   4:     {

   5:         string contentRoot = Directory.GetCurrentDirectory();

   6:         IFileProvider fileProvider = new PhysicalFileProvider(

   7:             Path.Combine(contentRoot, "doc"));

   8:         new WebHostBuilder()

   9:             .UseContentRoot(contentRoot)

  10:             .UseKestrel()

  11:             .Configure(app => app

  12:                 .UseStaticFiles()

  13:                 .UseStaticFiles(new StaticFileOptions {

  14:                     FileProvider = fileProvider,

  15:                     RequestPath = "/documents"

  16:                 })

  17:                 .UseDirectoryBrowser()

  18:                 .UseDirectoryBrowser(new DirectoryBrowserOptions {

  19:                         FileProvider = fileProvider,

  20:                         RequestPath = "/documents"

  21:                 }))

  22:             .Build()

  23:             .Run();

  24:     }

  25: }

当上面这个应用启动之后,如果我们利用浏览器向针对某个目录的URL(比如“http://localhost:5000/”或者“http://localhost:5000/img/”),目标目录的内容(包括子目录和文件)将会以下图所示的形式显示在一个表格中。不仅仅如此,子目录和文件均会显示为链接,指向目标目录或者文件的URL。

三、显示默认页面

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

   1: public class Program
   2: {

   3:     public static void Main()

   4:     {

   5:         string contentRoot = Directory.GetCurrentDirectory();

   6:         IFileProvider fileProvider = new PhysicalFileProvider(Path.Combine(contentRoot, "doc"));

   7:  

   8:         new WebHostBuilder()

   9:             .UseContentRoot(contentRoot)

  10:             .UseKestrel()

  11:             .Configure(app => app

  12:                 .UseDefaultFiles()

  13:                 .UseDefaultFiles(new DefaultFilesOptions{

  14:                     RequestPath = "/documents",

  15:                     FileProvider = fileProvider,

  16:                 })

  17:                 .UseStaticFiles()

  18:                 .UseStaticFiles(new StaticFileOptions

  19:                 {

  20:                     FileProvider = fileProvider,

  21:                     RequestPath = "/documents"

  22:                 })

  23:                 .UseDirectoryBrowser()

  24:                 .UseDirectoryBrowser(new DirectoryBrowserOptions

  25:                 {

  26:                     FileProvider = fileProvider,

  27:                     RequestPath = "/documents"

  28:                 }))

  29:             .Build()

  30:             .Run();

  31:     }

  32: }

现在我们在“~/wwwroot/img/”目录下创建一个名为index.htm的默认页面,现在利用浏览器访问这个目录对应的URL(“http://localhost:5000/img/”),显示就时这个页面的内容。

我们必须在注册StaticFileMiddleware和DirectoryBrowserMiddleware之前注册DefaultFilesMiddleware,否则它起不了任何作用。由于DirectoryBrowserMiddleware和DefaultFilesMiddleware这两个中间件处理的均是针对目录的请求,如果DirectoryBrowserMiddleware先被注册,那么显示的总是目录的内容。若DefaultFilesMiddleware先被注册,在默认页面不存在情况下回显示目录的内容。至于为什么要先于StaticFileMiddleware之前注册DefaultFilesMiddleware,则是因为后者是通过采用URL重写的方式实现的,也就是说这个中间件会将针对目录的请求改写成针对默认页面的请求,而最终针对默认页面的请求还得依赖StaticFileMiddleware完成。

DefaultFilesMiddleware中间件在默认情况下总是以约定的名称(default.htm、default.html、index.htm或者index.html)在当前请求的目录下定位默认页面。如果我们希望作为默认页面的文件不能按照这样的约定命名(比如readme.htm),我们需要按照如下的方式显式指定默认页面的文件名。

   1: public class Program
   2: {

   3:     public static void Main()

   4:     {

   5:         string contentRoot = Directory.GetCurrentDirectory();

   6:         IFileProvider fileProvider = new PhysicalFileProvider(Path.Combine(contentRoot, "doc"));

   7:  

   8:         DefaultFilesOptions options1 = new DefaultFilesOptions();

   9:         DefaultFilesOptions options2 = new DefaultFilesOptions{

  10:             RequestPath = "/documents",

  11:             FileProvider = fileProvider

  12:         };

  13:         options1.DefaultFileNames.Add("readme.htm");

  14:         options2.DefaultFileNames.Add("readme.htm");

  15:  

  16:         new WebHostBuilder()

  17:             .UseContentRoot(contentRoot)

  18:             .UseKestrel()

  19:             .Configure(app => app

  20:                 .UseDefaultFiles(options1)

  21:                 .UseDefaultFiles(options2)

  22:                 .UseStaticFiles()

  23:                 .UseStaticFiles(new StaticFileOptions{

  24:                     FileProvider = fileProvider,

  25:                     RequestPath = "/documents"

  26:                 })

  27:                 .UseDirectoryBrowser()

  28:                 .UseDirectoryBrowser(new DirectoryBrowserOptions{

  29:                     FileProvider = fileProvider,

  30:                     RequestPath = "/documents"

  31:                 }))

  32:         .Build()

  33:         .Run();

  34:     }

  35: }

四、映射媒体类型

通过上面演示的实例可以看出,浏览器能够正确的将请求的目标文件的内容正常的呈现出来。对HTTP协议具有基本了解的人都应该知道,响应的文件能够在支持的浏览器上呈现具有一个基本的前提,那就是响应消息通过Content-Type报头携带的媒体类型必须与内容一致。我们的实例演示了针对两种类型文件的请求,一种是JPG文件,另一种是PDF文件,对应的媒体类型分别是“image/jpg”和“application/pdf”,那么StaticFileMiddleware是如何正确解析出正确的媒体类型的呢?

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

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

   1: public class Program
   2: {

   3:     public static void Main()

   4:     { 

   5:         new WebHostBuilder()

   6:             .UseContentRoot(Directory.GetCurrentDirectory();)

   7:             .UseKestrel()

   8:             .Configure(app => app.UseStaticFiles(new StaticFileOptions {

   9:                  ServeUnknownFileTypes  = true,

  10:                  DefaultContentType     = "image/jpg"

  11:              }))

  12:             .Build()

  13:             .Run();

  14:     }

  15: }

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

   1: public class Program
   2: {

   3:     public static void Main()

   4:     {

   5:         FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();

   6:         contentTypeProvider.Mappings.Add(".img", "image/jpg");

   7:  

   8:         new WebHostBuilder()

   9:             .UseContentRoot(Directory.GetCurrentDirectory())

  10:             .UseKestrel()

  11:             .Configure(app => app.UseStaticFiles(new StaticFileOptions{

  12:                 ContentTypeProvider = contentTypeProvider

  13:             }))

  14:             .Build()

  15:             .Run();

  16:     }

  17: }

时间: 2024-11-05 02:36:33

利用一个ASP.NET Core应用来发布静态文件的相关文章

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

原文:<ASP.NET Core 高性能系列>静态文件中间件 一.概述 静态文件(如 HTML.CSS.图片和 JavaScript等文件)是 Web程序直接提供给客户端的直接加载的文件. 较比于程序动态交互的代码而言,其实原理都一样(走Http协议), ASP.NET Core中需要进行一些配置才能提供这些文件. 二.wwwroot 静态文件存储在项目的 Web 程序的 {ContentRoot}/wwwroot目录下,但可通过 UseWebRoot 方法更改路径 . Web 应用程序项目的

ASP.NET Core Razor生成Html静态文件

一.前言 最近做项目的时候,使用Util进行开发,使用Razor写前端页面.初次使用感觉还是不大习惯,之前都是前后端分离的方式开发的,但是使用Util封装后的Angular后,感觉开发效率还是杠杠滴. 二.问题 在发布代码的时候,Webpack打包异常,提示是缺少了某些Html文件,我看了下相应的目录,发现目录缺少了部分Html文件,然后就问了何镇汐大大,给出的解决方案是,每个页面都需要访问一下才能生成相应的Html静态文件.这时候就产生了疑虑,是否有一种方式能获取所有路由,然后只需访问一次即可

Web的形式发布静态文件

Web的形式发布静态文件 虽然ASP.NET Core是一款"动态"的Web服务端框架,但是在很多情况下都需要处理针对静态文件的请求,最为常见的就是这对JavaScript脚本文件.CSS样式文件和图片文件的请求.针对不同格式的静态文件请求的处理,ASP.NET Core为我们提供了三个中间件,它们将是本系列文章论述的重点.不过在针对对它们展开介绍之前,我们照理通过一些简单的实例来体验一下如何在一个ASP.NET Core应用中发布静态文件.[本文已经同步到<ASP.NET Co

使用Visual Studio Code创建第一个ASP.NET Core应用程序

全文翻译自:Your First ASP.NET Core Application on a Mac Using Visual Studio Code 这篇文章将向你展示如何在Mac上写出你的第一个ASP.NET Core应用程序. 本文内容: 搭建开发环境 使用Yeoman搭建应用程序 使用Visual Studio Core开发ASP.NET应用程序 使用Kestrel在本地运行这个应用程序 将这个应用程序发布至Azure 学习资源 搭建开发环境 在你的开发机上下载并安装.Net Core和

Kubernetes初探[1]:部署你的第一个ASP.NET Core应用到k8s集群

转自:https://www.cnblogs.com/RainingNight/p/first-aspnetcore-app-in-k8s.html Kubernetes简介 Kubernetes是Google基于Borg开源的容器编排调度引擎,作为CNCF(Cloud Native Computing Foundation)最重要的组件之一,它的目标不仅仅是一个编排系统,而是提供一个规范,可以让你来描述集群的架构,定义服务的最终状态,Kubernetes可以帮你将系统自动得达到和维持在这个状态

ASP.NET Core 2.1发布/部署到Ubuntu并配置Nginx反向代理实现ip访问

一.准备 我用的是Ubuntu服务器器 [Ubuntu 18.04 x64] 和终端管理工具[Xshell] 二.安装 在服务器上安装.NET Core 三.部署程序 1.创建实例程序 可以直接使用.NET Core 的命令创建一个ASP.NET Core 示例网站应用程序,创建目录 /home/myuser/firstapp,执行命令: dotnet new mvc 接着,发布刚才创建的ASP.NET Core 网站发网站目录,所以,我们先创建一个网站发布目录:/var/www/firstap

记一个ASP.Net Core配置文件问题

最近排查一个ASP.Net Core项目的Bug,用LogInformation()记录一些运行日志,本地测试日志记录正常,然后发到RC环境测试,结果发现死活没有日志信息. 首先想到就是LogLevel设置有问题.检查了基础的配置文件(appsettings.json)没有问题,而RC环境的配置文件(appsettings.RC.json)未配置Logging节点,也就不会覆盖. 1 "Logging": { 2 "IncludeScopes": false, 3

从零写一个Asp.net core手脚架(模型验证)

一个asp.net core项目,一定包含了各种的实体,在RESTful api里面,有很多的参数传递,不建立实体则大量的参数需要自定验证正确性,并且Action上面会写的密密麻麻的参数 在asp.net 2.0的时候,就推出了ModelState,顾名思义,这个是模型状态,用于验证实体对象的 如何模型验证 用法是在需要验证的地方打上继承了ValidationAttribute的特性,比如常见的RequiredAttribute,这个是验证对象是否存在 /// <summary> /// Ad

ASP.NET Core 导入导出Excel xlsx 文件

ASP.NET Core 使用EPPlus.Core导入导出Excel xlsx 文件,EPPlus.Core支持Excel 2007/2010 xlsx文件导入导出,可以运行在Windows, Linux和Mac. EPPlus.Core 是基于EPPlus 更改而来,在Linux 下需要安装libgdiplus . EPPlus:http://epplus.codeplex.com/ EPPlus.Core:https://github.com/VahidN/EPPlus.Core 下面在A