AgileConfig的UI使用react重写快完成了。上次搞定了基于jwt的登录模式(AntDesign Pro + .NET Core 实现基于JWT的登录认证),但是还有点问题。现在使用react重写后,agileconfig成了个确确实实的前后端分离项目。那么其实部署的话要分2个站点部署,把前端build完的静态内容部署在一个网站,把server端也部署在一个站点。然后修改前端的baseURL让spa的api请求都指向server的网站。

这样做也不是不行,但是这不符合AgileConfig的精神,那就是简单。asp.net core程序本身其实就是一个http服务器,所以完全可以把spa网站使用它来承载。这样只需要部署一个站点就可以同时跑spa跟后端server了。

其实最简单的办法就是把build完的文件全部丢wwwroot文件夹下面。然后访问:

http://localhost:5000/index.html

但是这样我们的入口是index.html,这样看起来比较别扭,不够友好。而且这些文件直接丢在wwwroot的根目录下,会跟网站其他js、css等内容混合在一起,也很混乱。

那么下面我们就要解决这两个文件,我们要达到的目的有2个:

  1. spa的入口path友好,比如http://localhost:5000/ui
  2. spa静态文件存放的目录独立,比如存放在wwwroot/ui文件夹下,或者别的什么目录下。

要实现以上内容只需要一个自定义中间件就可以了。

wwwroot\ui

wwwroot\ui



我们把build完的静态文件全部复制到wwwroot\ui文件夹内,以跟其他静态资源进行区分。当然你也可以放在任意目录下,只要是能读取到就可以。

ReactUIMiddleware

namespace AgileConfig.Server.Apisite.UIExtension
{
public class ReactUIMiddleware
{
private static Dictionary<string, string> _contentTypes = new Dictionary<string, string>
{
{".html", "text/html; charset=utf-8"},
{".css", "text/css; charset=utf-8"},
{".js", "application/javascript"},
{".png", "image/png"},
{".svg", "image/svg+xml"},
{ ".json","application/json;charset=utf-8"},
{ ".ico","image/x-icon"}
};
private static ConcurrentDictionary<string, byte[]> _staticFilesCache = new ConcurrentDictionary<string, byte[]>();
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public ReactUIMiddleware(
RequestDelegate next,
ILoggerFactory loggerFactory
)
{
_next = next;
_logger = loggerFactory.
CreateLogger<ReactUIMiddleware>();
} private bool ShouldHandleUIRequest(HttpContext context)
{
return context.Request.Path.HasValue && context.Request.Path.Value.Equals("/ui", StringComparison.OrdinalIgnoreCase);
} private bool ShouldHandleUIStaticFilesRequest(HttpContext context)
{
//请求的的Referer为 0.0.0.0/ui ,以此为依据判断是否是reactui需要的静态文件
if (context.Request.Path.HasValue && context.Request.Path.Value.Contains("."))
{
context.Request.Headers.TryGetValue("Referer", out StringValues refererValues);
if (refererValues.Any())
{
var refererValue = refererValues.First();
if (refererValue.EndsWith("/ui", StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
} return false;
} public async Task Invoke(HttpContext context)
{
const string uiDirectory = "wwwroot/ui";
//handle /ui request
var filePath = "";
if (ShouldHandleUIRequest(context))
{
filePath = uiDirectory + "/index.html";
}
//handle static files that Referer = xxx/ui
if (ShouldHandleUIStaticFilesRequest(context))
{
filePath = uiDirectory + context.Request.Path;
} if (string.IsNullOrEmpty(filePath))
{
await _next(context);
}
else
{
//output the file bytes if (!File.Exists(filePath))
{
context.Response.StatusCode = 404;
return;
} context.Response.OnStarting(() =>
{
var extType = Path.GetExtension(filePath);
if (_contentTypes.TryGetValue(extType, out string contentType))
{
context.Response.ContentType = contentType;
}
return Task.CompletedTask;
}); await context.Response.StartAsync(); byte[] fileData = null;
if (_staticFilesCache.TryGetValue(filePath, out byte[] outfileData))
{
fileData = outfileData;
}
else
{
fileData = await File.ReadAllBytesAsync(filePath);
_staticFilesCache.TryAdd(filePath, fileData);
}
await context.Response.BodyWriter.WriteAsync(fileData); return;
}
}
}
}

大概解释下这个中间件的思路。这个中间件的逻辑大概是分量部分。

1.拦截请求的路径为/ui的请求,直接从ui文件夹读取index.html静态文件的内容然后输出出去,这就相当于直接访问/index.html。但是这样的路径形式看起来更加友好。

2.拦截react spa需要的静态资源文件,比如css文件,js文件等。这里比较麻烦,因为spa拉静态文件的时候path是直接从网站root开始的,比如http://localhost:5000/xxx.js,那么怎么区分出来这个文件是react spa需要的呢?我们判断一下请求的Referer头部,如果Referer的path是/ui,那么就说明是react spa需要的静态资源,同样从ui文件夹去读取。

这里还需要给每个response设置指定的contentType不然浏览器无法准确识别资源。

   public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseMiddleware<ExceptionHandlerMiddleware>();
}
app.UseMiddleware<ReactUIMiddleware>(); ...
... }

在Startup类的Configure方法内使用这个中间件。这样我们的改造就差不多了。

运行一下



访问下http://localhost:5000/ui 可以看到spa成功加载进来了。

总结

为了能让asp.net core承载react spa应用,我们使用一个中间件进行拦截。当访问对应path的时候从本地文件夹内读取静态资源返回给浏览器,从而完成spa所需要资源的加载。这次使用react spa来演示,其实换成任何spa应用都是一样的操作。

代码在这:ReactUIMiddleware

关注我的公众号一起玩转技术

ASP.NET .Core 集成 React SPA 应用的更多相关文章

  1. ABP官方文档翻译 6.2.1 ASP.NET Core集成

    ASP.NET Core 介绍 迁移到ASP.NET Core? 启动模板 配置 启动类 模块配置 控制器 应用服务作为控制器 过滤器 授权过滤器 审计Action过滤器 校验过滤器 工作单元Acti ...

  2. asp.net core 集成 log4net 日志框架

    asp.net core 集成 log4net 日志框架 Intro 在 asp.net core 中有些日志我们可能想输出到数据库或文件或elasticsearch等,如果不自己去实现一个 Logg ...

  3. [Abp 源码分析]十七、ASP.NET Core 集成

    0. 简介 整个 Abp 框架最为核心的除了 Abp 库之外,其次就是 Abp.AspNetCore 库了.虽然 Abp 本身是可以用于控制台程序的,不过那样的话 Abp 就基本没什么用,还是需要集合 ...

  4. Asp.Net Core 集成 Hangfire 配置使用 Redis 存储

    Hangfire 官方支持 MSSQL 与 Redis(Hangfire.Pro.Redis) 两种 ,由于我的数据库是 MYSQL ,粗略查询了一下文档,现在对 .NET Core 支持的并不够好, ...

  5. asp.net core集成MongoDB

    0.目录 整体架构目录:ASP.NET Core分布式项目实战-目录 一.前言及MongoDB的介绍 最近在整合自己的框架,顺便把MongoDBD的最简单CRUD重构一下作为组件化集成到asp.net ...

  6. asp.net core集成CAP(分布式事务总线)

    一.前言 感谢杨晓东大佬为社区贡献的CAP开源项目,传送门在此:.NET Core 事件总线,分布式事务解决方案:CAP 以及 如何在你的项目中集成 CAP[手把手视频教程],之前也在工作中遇到分布式 ...

  7. asp.net core 集成JWT(一)

    [什么是JWT] JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. JWT的官网地址:https://jwt.io/ 通俗地来讲,JWT是能代表用户身份的令牌,可以使用JWT ...

  8. asp.net core 集成JWT(二)token的强制失效,基于策略模式细化api权限

    [前言] 上一篇我们介绍了什么是JWT,以及如何在asp.net core api项目中集成JWT权限认证.传送门:https://www.cnblogs.com/7tiny/p/11012035.h ...

  9. asp.net core 集成 Prometheus

    asp.net core 集成 prometheus Intro Prometheus 是一个开源的现代化,云原生的系统监控框架,并且可以轻松的集成 PushGateway, AlertManager ...

随机推荐

  1. Linux 驱动框架---platform驱动框架

    Linux系统的驱动框架主要就是三个主要部分组成,驱动.总线.设备.现在常见的嵌入式SOC已经不是单纯的CPU的概念了,它们都会在片上集成很多外设电路,这些外设都挂接在SOC内部的总线上,不同与IIC ...

  2. free video tutorial of Deep Learning

    free video tutorial of Deep Learning AI 深度学习/ 机器学习/人工智能 Deep Learning With Deep Learning – a form of ...

  3. Angular 2 for 2017 web full stack development

    1 1 1 Angular 2 for 2017 web full stack development 1 1 https://angular2.xgqfrms.xyz/ https://ng2-he ...

  4. 基本ILS面的评估

    一.定义与用途 基本ILS面是ICAO DOC8168飞行程序设计规范中提到的一种限制面. 它相当于附件14中代码为3或4的精密进近跑道所规定的障碍物限制面的子集. 包含:进近面(分为两部分).过渡面 ...

  5. 教你玩转CSS 居中

    1.元素居中对齐 要水平居中对齐一个元素(如 <div>), 可以使用 margin: auto;. 设置到元素的宽度将防止它溢出到容器的边缘. 元素通过指定宽度,并将两边的空外边距平均分 ...

  6. Angular性能优化实践——巧用第三方组件和懒加载技术

    应该有很多人都抱怨过 Angular 应用的性能问题.其实,在搭建Angular项目时,通过使用打包.懒加载.变化检测策略和缓存技术,再辅助第三方组件,便可有效提升项目性能. 为了帮助开发者深入理解和 ...

  7. ubuntu ARM换国内源和国内源安装ROS

    ubuntu arm换国内源: https://www.cnblogs.com/yongy1030/p/10315569.html 国内源安装ROS: https://blog.csdn.net/ch ...

  8. 23_MySQL单行和多行子查询语法规则(重点)

    本节涉及SQL语句: -- 如何用子查询查找FORD和MARTIN两个人的同事? 1 WHERE子查询 SELECT ename FROM t_emp WHERE deptno IN (SELECT ...

  9. 用Python实现一个“百度翻译”

    import requests import json s = input("请输入你要翻译的内容:") headers = {"User-Agent":&qu ...

  10. Java自学第6期——Collection、Map、迭代器、泛型、可变参数、集合工具类、集合数据结构、Debug

    集合:集合是java中提供的一种容器,可以用来存储多个数据. 集合和数组既然都是容器,它们有啥区别呢? 数组的长度是固定的.集合的长度是可变的. 数组中存储的是同一类型的元素,可以存储基本数据类型值. ...