.Net Core中间件之静态文件(StaticFiles)示例详解

一、介绍

静态文件(static files),诸如 HTML、CSS、图片和 JavaScript 之类的资源会被 ASP.NET Core 应用直接提供给客户端。

在介绍静态文件中间件之前,先介绍 ContentRoot和WebRoot概念。

ContentRoot:指web的项目的文件夹,包括bin和webroot文件夹。

WebRoot:一般指ContentRoot路径下的wwwroot文件夹。

介绍这个两个概念是因为静态资源文件一般存放在WebRoot路径下,也就是wwwroot。下面为这两个路径的配置,如下所示:

public static void Main(string[] args)
  {var host = new WebHostBuilder()
      .UseKestrel()
      .UseStartup<Startup>()
      .UseContentRoot(Directory.GetCurrentDirectory())
      .UseWebRoot(Directory.GetCurrentDirectory() + @"\wwwroot\")
      .UseEnvironment(EnvironmentName.Development)
      .Build();
   host.Run();
  }

上面的代码将ContentRoot路径和WebRoot路径都配置了,其实只需配置ContentRoot路径,WebRoot默认为ContentRoot路径下的wwwroot文件夹路径。

在了解静态文件中间件前,还需要了解HTTP中关于静态文件缓存的机制。跟静态文件相关的HTTP头部主要有Etag和If-None-Match。

下面为访问静态文件服务器端和客户端的流程:

1、客户端第一次向客户端请求一个静态文件。

2、服务器收到客户端访问静态文件的请求,服务器端会根据静态文件最后的修改时间和文件内容的长度生成一个Hash值,并将这个值放到请求头ETag中。

3、客户端第二次发起同一个请求时,因为之前请求过此文件,所以本地会有缓存。在请求时会在请求头中加上If-Nono-Match,其值为服务器返回的ETag的值。

4、服务器端比对发送的来的If-None-Match的值和本地计算的ETag的值是否相同。如果相同,返回304状态码,客户端继续使用本地缓存。如果不相同,返回200状态码,客户端重新解析服务器返回的数据,不使用本地缓存。

具体看下面例子。

二、简单使用

2.1 最简单的使用

最简单的使用就是在Configure中加入下面一句话,然后将静态文件放到webRoot的路径下,我没有修改webRoot指定的路径,所以就是wwwroot文件夹。

 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  {
   app.UseStaticFiles();
   app.UseMvc();
  }

在wwwroot文件夹下放一个名称为1.txt的测试文本,然后通过地址访问。

这种有一个缺点,暴露这个文件的路径在wwwroot下。

2.2 指定请求地址

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  {
   app.UseMvc();

   app.UseStaticFiles(new StaticFileOptions()
   {
    FileProvider = new PhysicalFileProvider(@"C:\Users\Administrator\Desktop"),
    RequestPath = new PathString("/Static")
   });

   //app.UseStaticFiles("/Static");
  }

这种指定了静态文件存放的路径为:C:\Users\Administrator\Desktop,不是使用默认的wwwroot路径,就隐藏了文件的真实路径,并且需要在地址中加上static才能访问。

当然也可以不指明静态文件的路径,只写请求路径,如上面代码中的注释的例子。这样静态文件就必须存储到WebRoot对应的目录下了。如果WebRoot的目录对应的是wwwroot,静态文件就放到wwwroot文件夹中。

下面通过例子看一下静态文件的缓存,如果你想做这个例子,别忘记先清空缓存。

(第一次请求)

(第二次请求 文件相对第一次请求没有修改的情况)

(第三次请求 文件相对第一次请求有修改的情况)

三、源码分析

源码在https://github.com/aspnet/StaticFiles,这个项目还包含有其他中间件。既然是中间件最重要的就是参数为HttpContext的Invoke方法了,因为每一个请求都要经过其处理,然后再交给下一个中间件处理。

下面为处理流程。

public async Task Invoke(HttpContext context)
  {
   var fileContext = new StaticFileContext(context, _options, _matchUrl, _logger, _fileProvider, _contentTypeProvider);

   if (!fileContext.ValidateMethod())//静态文件的请求方式只能是Get或者Head
   {
    _logger.LogRequestMethodNotSupported(context.Request.Method);
   }       //判断请求的路径和配置的请求路径是否匹配。如请求路径为http://localhost:5000/static/1.txt        //配置为RequestPath = new PathString("/Static")      //则匹配,并将文件路径赋值给StaticFileContext中点的_subPath
   else if (!fileContext.ValidatePath())
   {
    _logger.LogPathMismatch(fileContext.SubPath);
   }       //通过获取要访问文件的扩展名,获取此文件对应的MIME类型,       //如果找到文件对应的MIME,返回True,并将MIME类型赋值给StaticFileContext中的_contextType       //没有找到返回False.
   else if (!fileContext.LookupContentType())
   {
    _logger.LogFileTypeNotSupported(fileContext.SubPath);
   }       //判断访问的文件是否存在。       //如果存在返回True,并根据文件的最后修改时间和文件的长度,生成Hash值,并将值赋值给_etag,也就是相应头中的Etag。       //如果不存在 返回False,进入下一个中间件中处理
   else if (!fileContext.LookupFileInfo())
   {
    _logger.LogFileNotFound(fileContext.SubPath);
   }
   else
   {
    fileContext.ComprehendRequestHeaders();          //根据StaticFileContext中的值,加上对应的相应头,并发送响应。具体调用方法在下面
    switch (fileContext.GetPreconditionState())
    {
     case StaticFileContext.PreconditionState.Unspecified:
     case StaticFileContext.PreconditionState.ShouldProcess:
      if (fileContext.IsHeadMethod)
      {
       await fileContext.SendStatusAsync(Constants.Status200Ok);
       return;
      }
      try
      {
       if (fileContext.IsRangeRequest)
       {
        await fileContext.SendRangeAsync();
        return;
       }
       await fileContext.SendAsync();
       _logger.LogFileServed(fileContext.SubPath, fileContext.PhysicalPath);
       return;
      }
      catch (FileNotFoundException)
      {
       context.Response.Clear();
      }
      break;
     case StaticFileContext.PreconditionState.NotModified:
      _logger.LogPathNotModified(fileContext.SubPath);
      await fileContext.SendStatusAsync(Constants.Status304NotModified);
      return;
     case StaticFileContext.PreconditionState.PreconditionFailed:
      _logger.LogPreconditionFailed(fileContext.SubPath);
      await fileContext.SendStatusAsync(Constants.Status412PreconditionFailed);
      return;
     default:
      var exception = new NotImplementedException(fileContext.GetPreconditionState().ToString());
      Debug.Fail(exception.ToString());
      throw exception;
    }
   }       //进入下一个中间件中处理
   await _next(context);
  }

添加响应头的方法:

public void ApplyResponseHeaders(int statusCode)
  {
   _response.StatusCode = statusCode;
   if (statusCode < 400)
   {
    if (!string.IsNullOrEmpty(_contentType))
    {
     _response.ContentType = _contentType;
    }          //设置响应头中最后修改时间、ETag和accept-ranges
    _responseHeaders.LastModified = _lastModified;
    _responseHeaders.ETag = _etag;
    _responseHeaders.Headers[HeaderNames.AcceptRanges] = "bytes";
   }
   if (statusCode == Constants.Status200Ok)
   {
    _response.ContentLength = _length;
   }
   _options.OnPrepareResponse(new StaticFileResponseContext()
   {
    Context = _context,
    File = _fileInfo,
   });
  }

校验文件是否修改的方法:

public bool LookupFileInfo()
  {
   _fileInfo = _fileProvider.GetFileInfo(_subPath.Value);
   if (_fileInfo.Exists)
   {
    _length = _fileInfo.Length;
    DateTimeOffset last = _fileInfo.LastModified;
    _lastModified = new DateTimeOffset(last.Year, last.Month, last.Day, last.Hour, last.Minute, last.Second, last.Offset).ToUniversalTime();
          //通过修改时间和文件长度,得到ETag的值
    long etagHash = _lastModified.ToFileTime() ^ _length;
    _etag = new EntityTagHeaderValue('\"' + Convert.ToString(etagHash, 16) + '\"');
   }
   return _fileInfo.Exists;
  }

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • .Net Core Cors中间件的深入讲解

    同源策略和资源跨域共享 1.同源策略 同源策略,它是由Netscape提出的一个著名的安全策略.现在所有支持JavaScript 的浏览器都会使用这个策略.所谓同源是指,域名,协议,端口相同. 1.1.目的 主要是为了保证用户信息的安全,防止网站窃取用户数据.假如没有同源策略,可能就会有下面这种情况的发生.用户访问两个网站A/B,并登录了A网站,A网站会在计算机本地存储Cookie或者Token等等,在访问B网站的时候,B网站就可以访问这些本地的存储信息,B网站可以使用用户的Cookie去登录A

  • 利用.net core实现反向代理中间件的方法

    最近在将一些项目的rest api迁移到.net core中,最开始是用的Nginx做反向代理,将已经完成切换的部分切入系统,如下图所示: 由于迁移过程中也在进行代码重构,需要经常比较频繁的测试,以保证能及时发现引入的问题.从而导致我们每迁移一部分都需要配置一次nginx的路由映射,保证迁移的功能能切入系统测试. 进行了一段时间后,发现经常配置Nginx一来比较麻烦,二来容易配错:便想将这个反向代理的功能放在.net core程序中去,实现如下的功能: Rest请求直接发往.net core程序

  • 浅谈ASP.NET Core 中间件详解及项目实战

    前言 本篇文章是我们在开发自己的项目中实际使用的,比较贴合实际应用,算是对中间件的一个深入使用了,不是简单的Hello World. 中间件(Middleware)的作用 我们知道,任何的一个web框架都是把http请求封装成一个管道,每一次的请求都是经过管道的一系列操作,最终到达我们写的代码中.那么中间件就是在应用程序管道中的一个组件,用来拦截请求过程进行一些其他处理和响应.中间件可以有很多个,每一个中间件都可以对管道中的请求进行拦截,它可以决定是否将请求转移给下一个中间件. asp.net

  • 详解ASP.NET Core 中间件之压缩、缓存

    前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是Microsoft.AspNetCore.ResponseCompression 和 Microsoft.AspNetCore.ResponseCaching , 下面让我们一起看看的功能以及如何去使用吧. Getting Started Microsoft.AspNetCore.ResponseCompression Microsoft.AspNetCo

  • ASP.NET Core 2.0 带初始参数的中间件问题及解决方法

    问题 如何在ASP.NET Core 2.0向中间件传入初始参数? 答案 在一个空项目中,创建一个POCO(Plain Old CLR Object)来保存中间件所需的参数: public class GreetingOptions { public string GreetAt { get; set; } public string GreetTo { get; set; } } 添加一个中间件: public class GreetingMiddleware { private readon

  • 详解在ASP.NET Core 中使用Cookie中间件

    在 http:// ASP.NET Core 中使用Cookie中间件 ASP.NET Core 提供了Cookie中间件来序列化用户主题到一个加密的Cookie中并且在后来的请求中校验这个Cookie,再现用户并且分配到HttpContext对象的User属性中.如果你想提供自己的登录方式和用户数据你可以使用Cookie中间件来实现独立的功能. 添加和配置 第一步是增加Cookie中间件到你的应用中.首先使用nuget增加Microsoft.AspNetCore.Authentication.

  • Asp.Net Core 通过中间件防止图片盗链的实例

    一.原理 要实现防盗链,我们就必须先理解盗链的实现原理,提到防盗链的实现原理就不得不从HTTP协议说起,在HTTP协议中,有一个表头字段叫referer,采用URL的格式来表示从哪儿链接到当前的网页或文件.换句话说,通过referer,网站可以检测目标网页访问的来源网页,如果是资源文件,则可以跟踪到显示它的网页地址.有了referer跟踪来源就好办了,这时就可以通过技术手段来进行处理,一旦检测到来源不是本站即进行阻止或者返回指定的页面.如果想对自己的网站进行防盗链保护,则需要针对不同的情况进行区

  • 浅谈ASP.NET Core中间件实现分布式 Session

    1.1. 中间件原理 1.1.1. 什么是中间件 中间件是段代码用于处理请求和响应,通常多个中间件链接起来形成管道,由每个中间件自己来决定是否要调用下一个中间件. 1.1.2. 中间件执行过程 举一个示例来演示中间件的执行过程(分别有三个中间件:日志记录.权限验证和路由):当请求进入应用程序时,执行执行日志记录的中间件,它记录请求属性并调用链中的下一个中间件权限验证,如果权限验证通过则将控制权传递给下一个中间件,不通过则设置401 HTTP代码并返回响应,响应传递给日志中间件进行返回. 1.1.

  • .Net Core中间件之静态文件(StaticFiles)示例详解

    一.介绍 静态文件(static files),诸如 HTML.CSS.图片和 JavaScript 之类的资源会被 ASP.NET Core 应用直接提供给客户端. 在介绍静态文件中间件之前,先介绍 ContentRoot和WebRoot概念. ContentRoot:指web的项目的文件夹,包括bin和webroot文件夹. WebRoot:一般指ContentRoot路径下的wwwroot文件夹. 介绍这个两个概念是因为静态资源文件一般存放在WebRoot路径下,也就是wwwroot.下面

  • ASP.NET Core中间件计算Http请求时间示例详解

    ASP.NET Core通过RequestDelegate这个委托类型来定义中间件 public delegate Task RequestDelegate(HttpContext context); 可将一个单独的请求委托并行指定为匿名方法(称为并行中间件),或在类中对其进行定义.可通过Use,或在Middleware类中配置要传递给委托执行的方法(参数类型HttpContext,返回值类型Task). public static IApplicationBuilder Use(this IA

  • .NetCore实现上传多文件的示例详解

    本章和大家分享的是.NetCore的MVC框架上传文件的示例,主要讲的内容有:form方式提交上传,ajax上传,ajax提交+上传进度效果,Task并行处理+ajax提交+上传进度,相信当你读完文章内容后能后好的收获,如果可以不妨点个赞:由于昨天电脑没电了,快要写完的内容没有保存,今天早上提前来公司从头开始重新,断电这情况的确让人很头痛啊,不过为了社区的分享环境,这也是值得的,不多说了来进入今天的正篇环节吧: form方式上传一组图片 先来看看咋们html的代码,这里先简单说下要上传文件必须要

  • C++中#include头文件的示例详解

    fstream是C++ STL中对文件操作的合集,包含了常用的所有文件操作.在C++中,所有的文件操作,都是以流(stream)的方式进行的,fstream也就是文件流file stream. 最常用的两种操作为: 1.插入器(<<) 向流输出数据.比如说打开了一个文件流fout,那么调用fout<<"Write to file"<<endl;就表示把字符串"Write to file"写入文件并换行. 2.析取器(>>

  • Go语言基础Json序列化反序列化及文件读写示例详解

    目录 概述 JSON序列化 结构体转JSON map转JSON 切片转JSON JSON反序列化 JSON转map JSON转结构体 JSON转切片 写JSON文件 map写入JSON文件 切片写入JSON文件 结构体写入JSON文件 读JSON文件 解码JSON文件为map 解码JSON文件为切片 解码JSON文件为结构体 示例 概述 JSON(JavaScript Object Notation,JavaScript对象表示法)是一种轻量级的.键值对的数据交换格式.结构由大括号'{}',中括

  • Node.js 应用探索文件解压缩示例详解

    目录 引言 compressing 解压 压缩 archiver adm-zip 压缩 解压缩 总结 引言 今天在使用 node 脚本对文件处理时,需要实现一个功能,要对一个 zip 压缩包解压出来,修改里面的文件后,重新打包成zip包.node 解压缩文件的场景在实际应用中还是比较常见,下面介绍几个用来解压缩文件的库和使用方法. compressing compressing 是一个使用起来方便.功能非常强大的node库,它可以对文件.文件夹进行解压或压缩,支持tar.gzip.tgz.zip

  • Django学习教程之静态文件的调用详解

    前言 静态文件是指 网站中的 js, css, 图片,视频等文件,本文主要给大家介绍了关于Django学习之静态文件调用的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 方法如下 1.settings.py 静态文件相关示例代码及说明: # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.8/howto/static-files/ STATIC_URL = '/st

  • Django 静态文件配置过程详解

    静态文件配置 概述: 静态文件交由Web服务器处理,Django本身不处理静态文件.简单的处理逻辑如下(以nginx为例): URI请求 --> 按照Web服务器里面的配置规则先处理,以nginx为例,主要求配置在nginx.conf里的location --> 如果是静态文件,则由nginx直接处理 --> 如果不是则交由Django处理,Django根据urls.py里面的规则进行匹配 以上是部署到Web服务器后的处理方式,为了便于开发,Django提供了在开发环境的对静态文件的处理

  • Rust 中的文件操作示例详解

    目录 文件路径 文件创建和删除 目录创建和删除 文件创建和删除 文件读取和写入 文件打开 文件读取 文件写入 相关资料 文件路径 想要打开或者创建一个文件,首先要指定文件的路径. Rust 中的路径操作是跨平台的,std::path 模块提供的了两个用于描述路径的类型: PathBuf – 具有所有权并且可被修改,类似于 String. Path – 路径切片,类似于 str. 示例: use std::path::Path; use std::path::PathBuf; fn main()

  • Django学习之静态文件与模板详解

    目录 前言 模板 存放目录 模板调用 模板语法 数据传递 元素引用 for语句 if语句 静态文件 存放目录 文件调用 总结 前言 我们已经配置完Django,今天就来学学静态文件与模板的放置使用. 模板 在上一章节中我们的视图函数test使用了HttpResponse返回一个字符串 作为我们第一个Django程序 但只有这些是远远不够的,你说要是别人看自己的网页就几行文字在那里,既不美观也无意义. 存放目录 我们平时看到的网页都是通过HTML向我们呈现的内容的,Django也一样.一个完整系统

随机推荐