一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - ASP.NET教程 - .Net Core中間件之靜態文件(StaticFiles)示例詳解

.Net Core中間件之靜態文件(StaticFiles)示例詳解

2020-06-02 13:37MicroHeart! ASP.NET教程

這篇文章主要給大家介紹了關于.Net Core中間件之靜態文件(StaticFiles)的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起看看吧

一、介紹

靜態文件(static files),諸如 HTML、CSS、圖片和 JavaScript 之類的資源會被 ASP.NET Core 應用直接提供給客戶端。

在介紹靜態文件中間件之前,先介紹 ContentRoot和WebRoot概念。

ContentRoot:指web的項目的文件夾,包括bin和webroot文件夾。

WebRoot:一般指ContentRoot路徑下的wwwroot文件夾。

介紹這個兩個概念是因為靜態資源文件一般存放在WebRoot路徑下,也就是wwwroot。下面為這兩個路徑的配置,如下所示:

?
1
2
3
4
5
6
7
8
9
10
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文件夾。

?
1
2
3
4
5
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 {
  app.UseStaticFiles();
  app.UseMvc();
 }

在wwwroot文件夾下放一個名稱為1.txt的測試文本,然后通過地址訪問。

.Net Core中間件之靜態文件(StaticFiles)示例詳解

這種有一個缺點,暴露這個文件的路徑在wwwroot下。

2.2 指定請求地址

?
1
2
3
4
5
6
7
8
9
10
11
12
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文件夾中。

.Net Core中間件之靜態文件(StaticFiles)示例詳解

下面通過例子看一下靜態文件的緩存,如果你想做這個例子,別忘記先清空緩存。

.Net Core中間件之靜態文件(StaticFiles)示例詳解

(第一次請求)

.Net Core中間件之靜態文件(StaticFiles)示例詳解

(第二次請求 文件相對第一次請求沒有修改的情況)

.Net Core中間件之靜態文件(StaticFiles)示例詳解

(第三次請求 文件相對第一次請求有修改的情況)

三、源碼分析

源碼在https://github.com/aspnet/StaticFiles,這個項目還包含有其他中間件。既然是中間件最重要的就是參數為HttpContext的Invoke方法了,因為每一個請求都要經過其處理,然后再交給下一個中間件處理。

下面為處理流程。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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);
  }

添加響應頭的方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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,
   });
  }

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
  }

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:https://www.cnblogs.com/MicroHeart/p/9604214.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: xxnx日本免费护士 | 性啪啪chinese东北女人 | 美女扒开胸罩露出奶了无遮挡免费 | 高清女主播一区二区三区 | 91久久偷偷做嫩草影院电 | 国产农村一一级特黄毛片 | 成人观看免费大片在线观看 | 天天综合色天天综合色sb | xxxxxx日本处大片免费看 | 国产精品www视频免费看 | 黄a 大片a v 永久免费 | 污樱桃视频 | 成人午夜毛片 | 青青热久免费精品视频精品 | 无码一区国产欧美在线资源 | 特级毛片全部免费播放器 | 毛片一级免费 | www.99精品视频在线播放 | 日本一区二区三区久久精品 | 亚洲成人网页 | 久久re热在线视频精99 | 国产综合成人亚洲区 | 97影院网| 99久久久久久久 | 亚洲精品老司机福利在线播放 | 阿 好深 快点 老师受不了 | 国产欧美一区二区精品性色99 | 青青在线观看视频 | 亚洲天天做夜夜做天天欢 | 色综合久久98天天综合 | 日韩亚洲欧美一区二区三区 | 国产精品香蕉一区二区三区 | 激情亚洲天堂 | 虎四免费入口 | 美女污视频 | 脱了白丝校花的内裤猛烈进入 | 狠狠的撞击发泄h | 日本五级床片全都免费播放 | 精品亚洲麻豆1区2区3区 | 大团圆6全文在线阅读 | 亚洲成aⅴ人片在线 |