ASP.NET Core 集成 React SPA應(yīng)用的步驟
AgileConfig的UI使用react重寫快完成了。上次搞定了基于jwt的登錄模式(AntDesign Pro + .NET Core 實(shí)現(xiàn)基于JWT的登錄認(rèn)證),但是還有點(diǎn)問題?,F(xiàn)在使用react重寫后,agileconfig成了個(gè)確確實(shí)實(shí)的前后端分離項(xiàng)目。那么其實(shí)部署的話要分2個(gè)站點(diǎn)部署,把前端build完的靜態(tài)內(nèi)容部署在一個(gè)網(wǎng)站,把server端也部署在一個(gè)站點(diǎn)。然后修改前端的baseURL讓spa的api請(qǐng)求都指向server的網(wǎng)站。
這樣做也不是不行,但是這不符合AgileConfig的精神,那就是簡(jiǎn)單。asp.net core程序本身其實(shí)就是一個(gè)http服務(wù)器,所以完全可以把spa網(wǎng)站使用它來承載。這樣只需要部署一個(gè)站點(diǎn)就可以同時(shí)跑spa跟后端server了。
其實(shí)最簡(jiǎn)單的辦法就是把build完的文件全部丟wwwroot文件夾下面。然后訪問:
http://localhost:5000/index.html
但是這樣我們的入口是index.html,這樣看起來比較別扭,不夠友好。而且這些文件直接丟在wwwroot的根目錄下,會(huì)跟網(wǎng)站其他js、css等內(nèi)容混合在一起,也很混亂。
那么下面我們就要解決這兩個(gè)文件,我們要達(dá)到的目的有2個(gè):
- spa的入口path友好,比如http://localhost:5000/ui
- spa靜態(tài)文件存放的目錄獨(dú)立,比如存放在wwwroot/ui文件夾下,或者別的什么目錄下。
要實(shí)現(xiàn)以上內(nèi)容只需要一個(gè)自定義中間件就可以了。
wwwroot\ui
wwwroot\ui

我們把build完的靜態(tài)文件全部復(fù)制到wwwroot\ui文件夾內(nèi),以跟其他靜態(tài)資源進(jìn)行區(qū)分。當(dāng)然你也可以放在任意目錄下,只要是能讀取到就可以。
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)
{
//請(qǐng)求的的Referer為 0.0.0.0/ui ,以此為依據(jù)判斷是否是reactui需要的靜態(tài)文件
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;
}
}
}
}
大概解釋下這個(gè)中間件的思路。這個(gè)中間件的邏輯大概是分量部分。
1.攔截請(qǐng)求的路徑為/ui的請(qǐng)求,直接從ui文件夾讀取index.html靜態(tài)文件的內(nèi)容然后輸出出去,這就相當(dāng)于直接訪問/index.html。但是這樣的路徑形式看起來更加友好。
2.攔截react spa需要的靜態(tài)資源文件,比如css文件,js文件等。這里比較麻煩,因?yàn)閟pa拉靜態(tài)文件的時(shí)候path是直接從網(wǎng)站root開始的,比如http://localhost:5000/xxx.js,那么怎么區(qū)分出來這個(gè)文件是react spa需要的呢?我們判斷一下請(qǐng)求的Referer頭部,如果Referer的path是/ui,那么就說明是react spa需要的靜態(tài)資源,同樣從ui文件夾去讀取。
這里還需要給每個(gè)response設(shè)置指定的contentType不然瀏覽器無法準(zhǔn)確識(shí)別資源。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseMiddleware<ExceptionHandlerMiddleware>();
}
app.UseMiddleware<ReactUIMiddleware>();
...
...
}
在Startup類的Configure方法內(nèi)使用這個(gè)中間件。這樣我們的改造就差不多了。
運(yùn)行一下

訪問下http://localhost:5000/ui 可以看到spa成功加載進(jìn)來了。
總結(jié)
為了能讓asp.net core承載react spa應(yīng)用,我們使用一個(gè)中間件進(jìn)行攔截。當(dāng)訪問對(duì)應(yīng)path的時(shí)候從本地文件夾內(nèi)讀取靜態(tài)資源返回給瀏覽器,從而完成spa所需要資源的加載。這次使用react spa來演示,其實(shí)換成任何spa應(yīng)用都是一樣的操作。
代碼在這:ReactUIMiddleware
以上就是ASP.NET Core 集成 React SPA應(yīng)用的步驟的詳細(xì)內(nèi)容,更多關(guān)于ASP.NET Core 集成 React SPA的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
.Net筆記:System.IO之windows文件操作的深入分析
本篇文章是對(duì).Net中windows文件操作的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
.NET中防止Access數(shù)據(jù)庫(kù)下載
.NET中防止Access數(shù)據(jù)庫(kù)下載...2006-09-09
asp.net實(shí)現(xiàn)word文檔在線預(yù)覽功能的方法
這篇文章主要介紹了asp.net實(shí)現(xiàn)word文檔在線預(yù)覽功能的方法,可實(shí)現(xiàn)office文檔轉(zhuǎn)html,再在瀏覽器里面在線瀏覽,是非常實(shí)用的技巧,需要的朋友可以參考下2014-11-11
ASP.Net?Core讀取配置文件的三種方法小結(jié)
ASP.NET?Core支持多種格式的配置文件,如JSON、XML、INI等,本文就來介紹一下ASP.Net?Core讀取配置文件的三種方法,感興趣的可以了解一下2024-02-02
ASP.NET設(shè)計(jì)網(wǎng)絡(luò)硬盤之查看文件夾實(shí)現(xiàn)代碼
下面要介紹的實(shí)例包括網(wǎng)上硬盤的許多功能,將一步步為大家進(jìn)行介紹。首先創(chuàng)建工程實(shí)例,然后進(jìn)行主界面的設(shè)計(jì),最后對(duì)各個(gè)功能的實(shí)現(xiàn)分別進(jìn)行介紹2012-10-10
DataTable數(shù)據(jù)導(dǎo)出成Excel文件的小例子
DataTable數(shù)據(jù)導(dǎo)出成Excel文件的小例子,需要的朋友可以參考一下2013-04-04

