让Blazor Server模式支持IE11和老版Edge浏览器
起因
Blazor分(Client,或者叫WebAssembly)和Server两种模式,所以在Blazor Client在IE 11浏览器上运行是不可能的,是因为微软对IE 11的支持已经改变为补丁更新了,已经主推新版Edge(基本Chromium).所以本文主要支持IE 11和老版Edge也是要通过插件进行支持的.*如果使用Blazor,一定要使用.Net 5,在.Net 5对Blazor进行不少优化,性能提高了不少,如果.Net 6出来了,则优先使用.Net 6.从github看.Net 和 asp.net core最近提交的源码还在对Blazor进行大量的改进.







在官方文档 https://docs.microsoft.com/zh-cn/aspnet/core/blazor/supported-platforms?view=aspnetcore-3.1,可以看到Blazor支持的浏览器.

Blazor Server默认模版新建的项目.在IE 11看看效果
因为Blazor项目模版默认在调试开启IIS Express,在Asp.Net Core中IIS Express调试的时候,个人觉得没什么用,主要是在调试的时候笔记本的风扇呼呼的响,所以基本在建好项目之后,都会手动在Properties/launchSettings.json配置中进行删除.
启动项目,在IE 11中,查看运行效果,样式没什么问题(Blazor中使用UI是bootstrap)
在Counter Component(组件,和WebForm时代的控件差不多,可以看看 学习Asp.Net Core Blazor ),Counter组件功能是没法使用的,按钮单击之后,并没有进行计数(这一块是WebSocket进行通信的)
在IE 11中,要像正常使用,还是需要第三方插件进行兼容支持.
引入BlazorPolyfill.Server组件,兼容IE 11
BlazorPolyfill.Server是以Asp.Net Core中间件进行IE 11的功能支持.该组件代码是开源的,源码地址: https://github.com/Daddoon/Blazor.Polyfill
在NuGet管理器中,搜索 BlazorPolyfill.Server ,并进行安装,依赖有点多,安装有点慢.这里主要是在.Net 5使用,和在.Net Core 3.1的时候不同,是否加载blazor.polyfill.min.js在中间件处理
查看所依赖的库

在安装BlazorPolyfill.Server之后,在项目代码中注册该中间件和使用该中间件.
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
//注册BlazorPolyfill
services.AddBlazorPolyfill();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
//启用BlazorPolyfill,一定要在静态文件中间件之前启用,启用BlazorPolyfill中间件对blazor.polyfill.min.js和blazor.server.js文件请求,进行了拦截处理
app.UseBlazorPolyfill();
//
//ForceES5Fallback默认为false,会自动判断浏览器是否支持es 5,如果该浏览器不支持的es5,则需要加载blazor.polyfill.min.js文件,如果该浏览器支持es 5则把blazor.polyfill.min.js请求返回返回_fakeBlazorPolyfill={}
//app.UseBlazorPolyfill((options) =>
//{
// options.ForceES5Fallback = true;
//});
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
在_Host.cshtml中
@page "/"
@namespace BlazorServerApp.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BlazorServerApp</title>
<base href="~/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />
<link href="BlazorServerApp.styles.css" rel="stylesheet" />
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" />
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
@*引入支持IE 11的blazor.polyfill.min.js脚本文件,该脚本文件是嵌入BlazorPolyfill.Server.dll的资源中 *@
<script src="_framework/blazor.polyfill.min.js"></script>
<script src="_framework/blazor.server.js"></script>
</body>
</html>
对比在新版Edge和IE 11浏览器中,blazor.polyfill.min.js请求响应大小:
BlazorPolyfill.Server中间件源码
看看BlazorPolyfill.Server目录:
public static IApplicationBuilder UseBlazorPolyfill(
this IApplicationBuilder builder, BlazorPolyfillOptions options)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
if (options.ForceES5Fallback)
{
HttpRequestExtensions.ForceES5FallbackFlag();
}
InitReact(builder);
//This is a kind of hack for caching the data at boot
//It's better to prevent anything at first request by caching instead of
//making the user wait the file generation
builder.Use((context, next) =>
{
if (!IsBlazorPolyfillLibCached())
{
//Avoiding first client to await the file generation
CacheBlazorPolyfillLib();
}
//Normal behavior
return next();
});
builder.MapWhen(ctx =>
ctx.Request.BrowserNeedES5Fallback()
&& ctx.Request.Path.StartsWithSegments("/_framework")
&& ctx.Request.Path.StartsWithSegments("/_framework/blazor.server.js"),
subBuilder =>
{
subBuilder.Run(async (context) =>
{
var fileContent = GetPatchedBlazorServerFile();
await HttpRequestManager.ManageRequest(context, fileContent);
});
});
//拦截blazor.polyfill.js或者blazor.polyfill.min.js请求
//Should return the resource file if IE11
builder.MapWhen(ctx =>
ctx.Request.Path.StartsWithSegments("/_framework")
&& (
ctx.Request.Path.StartsWithSegments("/_framework/blazor.polyfill.js")
|| ctx.Request.Path.StartsWithSegments("/_framework/blazor.polyfill.min.js")
),
subBuilder =>
{
subBuilder.Run(async (context) =>
{
//Eval if the requested file is the minified version or not
bool isMinified = context.Request.Path.StartsWithSegments("/_framework/blazor.polyfill.min.js");
//判断是否兼容ie11
var fileContent = GetIE11BlazorPolyfill(context.Request.BrowserNeedES5Fallback(), isMinified);
//将blazor.polyfill.min.js进行响应
await HttpRequestManager.ManageRequest(context, fileContent);
});
});
return builder;
}
private static FileContentReference _ie11Polyfill = null;
private static FileContentReference _ie11PolyfillMin = null;
/// <summary>
/// Used to return an empty file but hashed with some dummy valid values in order to generate a usable Hash/ETag for browser caching
/// </summary>
private static FileContentReference _fakeie11Polyfill = null;
private static FileContentReference GetIE11BlazorPolyfill(bool isIE11, bool isMinified)
{
if (!isIE11)
{
if (_fakeie11Polyfill == null)
{
string fakeContent = "var _fakeBlazorPolyfill = { };";
//Computing ETag. Should be computed last !
string Etag = EtagGenerator.GenerateEtagFromString(fakeContent);
//Computing Build time for the Last-Modified Http Header
DateTime buildTime = GetBlazorPolyfillServerBuildDate();
_fakeie11Polyfill = new FileContentReference()
{
Value = fakeContent,
ETag = Etag,
LastModified = buildTime,
ContentLength = System.Text.UTF8Encoding.UTF8.GetByteCount(fakeContent).ToString(CultureInfo.InvariantCulture)
};
}
return _fakeie11Polyfill;
}
else
{
if (isMinified)
{
if (_ie11PolyfillMin == null)
{
//获取Blazor.Polyfill.Server程序集
var assembly = GetBlazorPolyfillAssembly();
var resources = assembly.GetManifestResourceNames();
var resourceName = resources.Single(str => str.EndsWith("blazor.polyfill.min.js"));
//从程序集的资源中获取blazor.polyfill.min.js文件,并读取到string中
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
string js = reader.ReadToEnd();
//Computing ETag. Should be computed last !
string Etag = EtagGenerator.GenerateEtagFromString(js);
//Computing Build time for the Last-Modified Http Header
DateTime buildTime = GetBlazorPolyfillServerBuildDate();
_ie11PolyfillMin = new FileContentReference()
{
Value = js,
ETag = Etag,
LastModified = buildTime,
ContentLength = System.Text.UTF8Encoding.UTF8.GetByteCount(js).ToString(CultureInfo.InvariantCulture)
};
}
}
return _ie11PolyfillMin;
}
else
{
if (_ie11Polyfill == null)
{
var assembly = GetBlazorPolyfillAssembly();
var resources = assembly.GetManifestResourceNames();
var resourceName = resources.Single(str => str.EndsWith("blazor.polyfill.js"));
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
string js = reader.ReadToEnd();
//Computing ETag. Should be computed last !
string Etag = EtagGenerator.GenerateEtagFromString(js);
//Computing Build time for the Last-Modified Http Header
DateTime buildTime = GetBlazorPolyfillServerBuildDate();
_ie11Polyfill = new FileContentReference()
{
Value = js,
ETag = Etag,
LastModified = buildTime,
ContentLength = System.Text.UTF8Encoding.UTF8.GetByteCount(js).ToString(CultureInfo.InvariantCulture)
};
}
}
return _ie11Polyfill;
}
}
}
秋风
2021-01-09