ASP.NET Core 集成 React SPA应用的步骤

目录
  • wwwroot\ui
  • ReactUIMiddleware
  • 运行一下
  • 总结

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应用的步骤的详细内容,更多关于ASP.NET Core 集成 React SPA的资料请关注我们其它相关文章!

(0)

相关推荐

  • asp.net core集成kindeditor实现图片上传功能

    本文为大家分享了asp.net core 如何集成kindeditor并实现图片上传功能的具体方法,供大家参考,具体内容如下 准备工作 1.visual studio 2015 update3开发环境 2.net core 1.0.1 及以上版本 目录 新建asp.net core web项目 下载kindeditor 增加图片上传控制器 配置kindeditor参数 代码下载 新建asp.net core web项目 新建一个asp.net core项目,这里命名为kindeditor 选中w

  • ASP.NET Core与NLog集成的完整步骤

    前言 一直很喜欢 NLog 的简洁和扩展性,所以准备将 ASP.NET Core 提供的默认日志提供程序替换成 NLog. NLog 是一个跨平台的 .Net 日志组件. NLog 遵从 BSD license,即允许商业应用且完全开放源代码.任何人都可以免费使用并对其进行测试,然后通过邮件列表反馈问题以及建议. 下面话不多说了,来一起看看详细的介绍吧. 步骤 1 在项目的project.json中添加依赖NLog.Extensions.Logging: "dependencies":

  • Asp.Net Core利用xUnit进行主机级别的网络集成测试详解

    前言 在开发 Asp.Net Core 应用程序的过程中,我们常常需要对业务代码编写单元测试,这种方法既快速又有效,利用单元测试做代码覆盖测试,也是非常必要的事情:但是,但我们需要对系统进行集成测试的时候,需要启动服务主机,利用浏览器或者Postman 等网络工具对接口进行集成测试,这就非常的不方便,同时浪费了大量的时间在重复启动应用程序上:今天要介绍就是如何在不启动应用程序的情况下,对 Asp.Net Core WebApi 项目进行网络集成测试. 一.建立项目 1.1 首先我们建立两个项目,

  • asp.net core 使用 TestServer 来做集成测试的方法

    Intro 之前我的项目里的集成测试是随机一个端口,每次都真实的启动一个 WebServer,之前也有看到过微软文档上 TestServer 的介绍,当时没仔细看过以为差不多就没用,一直是启动了一个真正的 WebServer 去跑集成测试的,上次分享 Xunit.DependencyInjection 改造测试项目的时候,写的烂代码被大师看到了之后, 大师建议用 TestServer 来做集成测试,使用 TestServer 不会真正的占用端口号,不会出现权限问题,于是扒了扒 TestServe

  • asp.net core集成JWT的步骤记录

    [什么是JWT] JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. JWT的官网地址:https://jwt.io/ 通俗地来讲,JWT是能代表用户身份的令牌,可以使用JWT令牌在api接口中校验用户的身份以确认用户是否有访问api的权限. JWT中包含了身份认证必须的参数以及用户自定义的参数,JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名. [什么时候应该使用JSON Web令牌?] 授权:这是使用JWT的最常见方案.一旦用户登录

  • 在ASP.NET Core Mvc集成MarkDown的方法

    这几天在做文章编辑,首先就想到了markdown,它比其它的都要新,而且很好用,相对于其它的html编辑器,好久不更新,要好得多,哦~对了我现在已经用上新版的Edge了,经过很多朋友测试,性能比谷歌浏览器都要好很多,并且资源消耗也相对来说小. 一.前提 好吧,言归正传,你首先需要下载MarkDown的相关样式脚本资源,下载完毕之后拖放你的ASP.NET Core Mvc 项目中的wwwroot中. 二.初始化 在页面中我们理所当然需要引用css 脚本资源,随后调用它的初始化方法. <script

  • asp.net core集成CKEditor实现图片上传功能的示例代码

    背景 本文为大家分享了asp.net core 如何集成CKEditor ,并实现图片上传功能的具体方法,供大家参考,具体内容如下. 准备工作 1.visual studio 2019 开发环境 2.net core 2.0 及以上版本 实现方法 1.新建asp.net core web项目 2.下载CKEditor 这里我们新建了一个系统自带的样本项目,去 CKEditor官网下载一个版本,解压后拷贝大wwwroot中 3.增加图片上传控制器 @using CompanyName.Projec

  • 如何在Asp.Net Core中集成Refit

    在很多时候我们在不同的服务之间需要通过HttpClient进行及时通讯,在我们的代码中我们会创建自己的HttpClient对象然后去跨领域额进行数据的交互,但是往往由于一个项目有多个人开发所以在开发中没有人经常会因为不同的业务请求去写不同的代码,然后就会造成各种风格的HttpClient的跨域请求,最重要的是由于每个人对HttpClient的理解程度不同所以写出来的代码可能质量上会有参差不齐,即使代码能够达到要求往往也显得非常臃肿,重复高我们在正式介绍Refit这个项目之前,我们来看看我们在项目

  • asp.net core 2.0 webapi集成signalr(实例讲解)

    在博客园也很多年了,一直未曾分享过什么东西,也没有写过博客,但自己也是汲取着博客园的知识成长的: 这两天想着不能这么无私,最近.NET CORE貌似挺流行的,闲来无事也自己搞了个asp.net core signalr 博客园里面也有人在.net core 2.0下面集成了signalr,但是是集成在同一个项目里面的,但是大家都知道我们很多的项目都是分离的: 而且signalr涉及到连接数和内存资源的占用问题,如果都集成在一个项目里面当访问量多大的时候容易造成网站访问缓慢,具体原因就不多说了 所

  • 如何在Asp.Net Core中集成ABP Dapper

    在实际的项目中,除了集成ABP框架的EntityFrameworkCore以外,在有些特定的场景下不可避免地会使用一些SQL查询语句,一方面是由于现在的EntityFrameworkCore2.X有些问题没有解决,另外一方面是基于性能方面的考虑,在了解本篇内容之前,首先还是来看看官方文档来给出的说明. 按照官方的介绍整体可以分为下面的步骤:1 安装依赖包.2 添加DependsOn属性标签.3 Entity to Table Mapping. 4 Usage 通过上面的4个步骤我们就能够正常在A

  • ASP.NET Core+Docker+Jenkins实现持续集成的完整实例

    前言 在前后端分离开发的项目当中为了避免重复构建发布,我们需要部署一个持续发布环境,而目前的开发环境服务器都是基于 CentOS 的,因此每次在本地发布之后还需要打包,上传,部署,十分繁琐.故这里采用了比较成熟的Jenkins 作为持续部署环境. 方法如下: 1.新建一个ASP.NET Core MVC应用程序: 需要勾选Docker支持 2.将其上传到git: 3.建立Jenkins任务 (1)选择"新建任务",输入任务名称,选择"自由风格项目",然后点击确定:

  • asp.net core集成MongoDB的完整步骤

    一.前言及MongoDB的介绍 最近在整合自己的框架,顺便把MongoDBD的最简单CRUD重构一下作为组件化集成到asp.net core项目中,当然此篇文章中没有讲解mongodb的集群部署,等有机会分享一下. 首先,我们在MongoDB的官方文档中看到,MongoDb的2.4以上的For .Net的驱动是支持.Net Core 2.0的. 针对MongoDB,我想大家应该不陌生,没有用过也有听过. 1.mongodb是什么? MongoDB是一个基于分布式文件存储的数据库,为web应用提供

随机推荐