23:静态文件中间件:前后端分离开发合并部署骚操作(下)-开发实战
.NET Core开发实战前文传送门:
- 第1课:课程介绍
- 第2课:内容综述
- 第3课:.NET Core的现状、未来以及环境搭建
- 第4课:Startup:掌握ASP.NET Core的启动过程
- 第5课:依赖注入:良好架构的起点(上)
- 第5课:依赖注入:良好架构的起点(中)
- 第5课:依赖注入:良好架构的起点(下)
- 第6课:作用域与对象释放行为(上)
- 第6课:作用域与对象释放行为(下)
- 第7课:用Autofac增强容器能力(上)
- 第7课:用Autofac增强容器能力(下)
- 第8课:配置框架:让服务无缝适应各种环境
- 第9课:命令行配置提供程序
- 第10课:环境变量配置提供程序
- 第11课:文件配置提供程序
- 第12课:配置变更监听
- 第13课:配置绑定:使用强类型对象承载配置数据
- 第14课:自定义配置数据源:低成本实现定制化配置方案
- 第15课:选项框架:服务组件集成配置的最佳实践
- 第16课:选项数据热更新:让服务感知配置的变化
- 第17课:为选项数据添加验证:避免错误配置的应用接收用户流量
- 第18课:日志框架:聊聊记日志的最佳姿势(上)
- 第18课:日志框架:聊聊记日志的最佳姿势(下)
- 第19课:日志作用域:解决不同请求之间的日志干扰
- 第20课:结构化日志组件Serilog:记录对查询分析友好的日志
- 第21课:中间件:掌控请求处理过程的关键(上)
- 第21课:中间件:掌控请求处理过程的关键(下)
- 第22课:异常处理中间件:区分真异常与逻辑异常(上)
- 第22课:异常处理中间件:区分真异常与逻辑异常(下)
- 第23课:静态文件中间件:前后端分离开发合并部署骚操作(上)
23 | 静态文件中间件:前后端分离开发合并部署骚操作
这里还有一个比较特殊的用法
一般情况下,我们前后端分离的架构,前端会编译成一个 index.html 文件和若干个 CSS 文件和 JavaScript 和图片文件
CSS 文件和 JavaScript 和图片文件一般会部署在 CDN 服务器上,这个 index 文件就需要我们建立一个宿主来 host 它
并且前端的一般路由的话,我们现在都会用 HTML5 的 History 的路由模式
这个时候前端就会对后端有一个特殊的诉求,除了 API 的请求以外,其他的请求的响应都应该是 index.html 这个静态文件
要达到这个目的,我们可以借助我们的中间件的执行原理来实现
首先假设我们的 index.html 就是我们前端编译好的静态文件,我们放置在 wwwroot 下面,前端编译的任何文件都放在 wwwroot 下面
然后我们再做一件事件就是 UseStaticFiles,我们把目录访问整个去掉
//services.AddDirectoryBrowser();
首先映射静态文件
app.UseStaticFiles();
静态文件映射出来之后实际上还有一个诉求,就是当我们访问其他特殊的页面地址的时候,比如说 /order/get 这样子的页面的时候,也应该响应我们的静态文件
这个时候我们可以把这样一段逻辑加入进来
// 判断我们当前的请求是否满足条件 app.MapWhen(context => { // 如果我们的请求不是以 API 开头的请求 return !context.Request.Path.Value.StartsWith("/api"); }, appBuilder => { // 如果满足条件,我就走我下面这一段中间件的逻辑 var option = new RewriteOptions(); // 重写为 /index.html option.AddRewrite(".*", "/index.html", true); appBuilder.UseRewriter(option); // 重写完之后再使用我们的静态文件中间件 appBuilder.UseStaticFiles(); });
这样子可以达到一个效果就是我们访问任意的非 API 目录的时候,我们都可以得到 index.html
启动程序
https://localhost:5001/api/weatherforecast
可以正常访问
API 的请求我们都是让它通过的,不是 API 的时候才会拦截
这个时候如果访问
https://localhost:5001/order
会发现获得的是静态文件
如果说静态文件是存在的,这个时候实际上会响应原有的静态文件,比如说访问
https://localhost:5001/a/index.html
这样子就可以发现我们能让静态文件的目录正常工作,并且能将其他的我们需要的地址都重定向到 index.html
当然这里还有另外一种写法,就是不用 UseRewriter 的方式,而是用 Run 的方式,也是就用断路器的方式
// 判断我们当前的请求是否满足条件 app.MapWhen(context => { // 如果我们的请求不是以 API 开头的请求 return !context.Request.Path.Value.StartsWith("/api"); }, appBuilder => { //// 如果满足条件,我就走我下面这一段中间件的逻辑 //var option = new RewriteOptions(); //// 重写为 /index.html //option.AddRewrite(".*", "/index.html", true); //appBuilder.UseRewriter(option); //// 重写完之后再使用我们的静态文件中间件 //appBuilder.UseStaticFiles(); appBuilder.Run(async c => { // 读取静态文件,并且输出给我们的 Response var file = env.WebRootFileProvider.GetFileInfo("index.html"); c.Response.ContentType = "text/html"; using (var fileStream = new FileStream(file.PhysicalPath, FileMode.Open, FileAccess.Read)) { await StreamCopyOperation.CopyToAsync(fileStream, c.Response.Body, null, BufferSize, c.RequestAborted); } }); });
这种写法有一个缺点就是,没办法像静态文件中间件那样,输出正确的 Http 请求头
对比一下两种方式的输出的请求头的不同
启动程序,访问
https://localhost:5001/order
打开调试工具,可以看到对 order 的我们的响应头就只有 4 个

其他的静态文件,响应头会多出来 etag,data,last-modified

这些的话就是我们关于 HTTP 缓存可以用到的头,所以说我们还是推荐使用上面这种方式,静态中间件的方式,而不是自己输出文件的方式

原文出处:微信公众号【郑子铭 DotNet NB】
原文链接:https://mp.weixin.qq.com/s/kL-p0DtkO0yOGjNjRVjxRQ
本文观点不代表Dotnet9立场,转载请联系原作者。