LostBeard / BlazorWASMSIMDDetectExample

This .Net 8 Blazor WASM project demonstrates a way of detecting SIMD support and using it if available.
https://lostbeard.github.io/BlazorWASMSIMDDetectExample/
MIT License
12 stars 0 forks source link

Something Wrong with Blazor Webassembly Hosted on Asp.Net Core #1

Closed MarkSky closed 10 months ago

MarkSky commented 10 months ago

My Blazor webassembly web is hosted on Asp.Net Core, and I upgrade it from .NET 7 to .NET8.

When the browser request the .dat files in the _frameworkCompat folder will get HTTP 404.

LostBeard commented 10 months ago

Please provide a repo to your project that has the issue or steps to reproduce the issue.

LostBeard commented 10 months ago

I decided to run some tests based on the information you have given.

I created a fresh .Net 8 Hosted Blazor WASM app using the release version of .Net 8 and got it working with SIMD detection. The template I used was 'Blazor Web App' with WebAssembly interactivity, which is the new adaptation of an ASP.Net Hosted Blazor WASM app in .Net 8 (formally known as Blazor United.)

I am not sure if it is quite the same as migrating from .Net 7 hosted but I think I found a fix for your issue.

Issue

For some reason, 'globalization' files cannot be served from the _frameworkCompat/ folder even when they exist there.

Fix

In the Blazor.start config loadBootResource method, check if the resource type === 'globalization' and if it does, make sure it loads from _framework/ and not _frameworkCompat/. See my example below.

Blazor Web App App.razor (in server project)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
    <link rel="stylesheet" href="app.css" />
    <link rel="stylesheet" href="Blazor8FinalTest.styles.css" />
    <link rel="icon" type="image/png" href="favicon.png" />
    <HeadOutlet />
</head>
<body>
    <Routes />
    <!-- autostart is set to false so we can detect SIMD support and load the appropriate build -->
    <script src="_framework/blazor.web.js" autostart="false"></script>

    <!--
        WASM Feature Detect - from GoogleChromeLabs
        CDN UMD Version: https://unpkg.com/wasm-feature-detect/dist/umd/index.js
        Repo: https://github.com/GoogleChromeLabs/wasm-feature-detect
    -->
    <script webworker-enabled src="wasm-feature-detect.1.6.1.js"></script>
    <!--
        The below script tag is used to detect SIMD support on the running device and load the appropriate build
        If SIMD is not supported it loads _frameworkCompat/ instead of _framework/
    -->
    <script webworker-enabled>
        (async () => {
            var url = new URL(location.href);
            let verboseStart = url.searchParams.get('verboseStart') === '1';
            var forceCompatMode = url.searchParams.get('forceCompatMode') === '1';
            var supportsSimd = await wasmFeatureDetect.simd();
            if (verboseStart) console.log('supportsSimd', supportsSimd);
            // compat mode build could be built without wasm exception support if needed and detected here
            var supportsExceptions = await wasmFeatureDetect.exceptions();
            if (verboseStart) console.log('supportsExceptions', supportsExceptions);
            var useCompatMode = !supportsSimd;
            if (forceCompatMode) {
                if (verboseStart) console.log('forceCompatMode', forceCompatMode);
                useCompatMode = true;
            }
            if (verboseStart) console.log('useCompatMode', useCompatMode);
            // Blazor United (.Net 8 Blazor Web App) Blazor.start settings are slightly different than Blazor WebAssembly (Blazor WebAssembly Standalone App)
            var getRuntimeType = function () {
                for (var script of document.scripts) {
                    if (script.src.indexOf('_framework/blazor.web.js') !== -1) return 'united';
                    if (script.src.indexOf('_framework/blazor.webassembly.js') !== -1) return 'wasm';
                }
                return '';
            }
            var runtimeType = getRuntimeType();
            // customize the resource loader for the runtime that is loaded
            // https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/startup?view=aspnetcore-8.0#load-boot-resources
            var webAssemblyConfig = {
                loadBootResource: function (type, name, defaultUri, integrity) {
                    if (verboseStart) console.log(`Loading: '${type}', '${name}', '${defaultUri}', '${integrity}'`);
                    let useCompatThisResource = useCompatMode && type !== 'globalization';
                    let newUrl = useCompatThisResource ? defaultUri.replace('_framework/', '_frameworkCompat/') : defaultUri.replace('_frameworkCompat/', '_framework/');
                    return newUrl;
                },
            };
            if (runtimeType === 'wasm') {
                // Blazor WebAssembly Standalone App
                Blazor.start(webAssemblyConfig);
            } else if (runtimeType === 'united') {
                // Blazor Web App (formally Blazor United)
                Blazor.start({ webAssembly: webAssemblyConfig });
            } else {
                // Fallback supports both known Blazor WASM runtimes
                // Modified loader that will work with both United and WASM runtimes (doesn't require detection)
                webAssemblyConfig.webAssembly = webAssemblyConfig;
                Blazor.start(webAssemblyConfig);
            }
        })();
    </script>
</body>
</html>

There were a bunch of other little things to get it working that were different from a standalone Blazor WASM app but this is the issue you asked about that I came across as well. Let me know if you have any more problems and I will try to help.

LostBeard commented 10 months ago

Here is the project I created for the test BlazorWebAppSIMDDetectExample

MarkSky commented 10 months ago

Please provide a repo to your project that has the issue or steps to reproduce the issue.

I just follow the rule to update my code. But only one is different, I use the version 1.6.1 of wasm-feature-detect.js.

MarkSky commented 10 months ago

I decided to run some tests based on the information you have given.

I created a fresh .Net 8 Hosted Blazor WASM app using the release version of .Net 8 and got it working with SIMD detection. The template I used was 'Blazor Web App' with WebAssembly interactivity, which is the new adaptation of an ASP.Net Hosted Blazor WASM app in .Net 8 (formally known as Blazor United.)

I am not sure if it is quite the same as migrating from .Net 7 hosted but I think I found a fix for your issue.

Issue

For some reason, 'globalization' files cannot be served from the _frameworkCompat/ folder even when they exist there.

Fix

In the Blazor.start config loadBootResource method, check if the resource type === 'globalization' and if it does, make sure it loads from _framework/ and not _frameworkCompat/. See my example below.

Blazor Web App App.razor (in server project)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
    <link rel="stylesheet" href="app.css" />
    <link rel="stylesheet" href="Blazor8FinalTest.styles.css" />
    <link rel="icon" type="image/png" href="favicon.png" />
    <HeadOutlet />
</head>
<body>
    <Routes />
    <!-- autostart is set to false so we can detect SIMD support and load the appropriate build -->
    <script src="_framework/blazor.web.js" autostart="false"></script>

    <!--
        WASM Feature Detect - from GoogleChromeLabs
        CDN UMD Version: https://unpkg.com/wasm-feature-detect/dist/umd/index.js
        Repo: https://github.com/GoogleChromeLabs/wasm-feature-detect
    -->
    <script webworker-enabled src="wasm-feature-detect.1.6.1.js"></script>
    <!--
        The below script tag is used to detect SIMD support on the running device and load the appropriate build
        If SIMD is not supported it loads _frameworkCompat/ instead of _framework/
    -->
    <script webworker-enabled>
        (async () => {
            var url = new URL(location.href);
            let verboseStart = url.searchParams.get('verboseStart') === '1';
            var forceCompatMode = url.searchParams.get('forceCompatMode') === '1';
            var supportsSimd = await wasmFeatureDetect.simd();
            if (verboseStart) console.log('supportsSimd', supportsSimd);
            // compat mode build could be built without wasm exception support if needed and detected here
            var supportsExceptions = await wasmFeatureDetect.exceptions();
            if (verboseStart) console.log('supportsExceptions', supportsExceptions);
            var useCompatMode = !supportsSimd;
            if (forceCompatMode) {
                if (verboseStart) console.log('forceCompatMode', forceCompatMode);
                useCompatMode = true;
            }
            if (verboseStart) console.log('useCompatMode', useCompatMode);
            // Blazor United (.Net 8 Blazor Web App) Blazor.start settings are slightly different than Blazor WebAssembly (Blazor WebAssembly Standalone App)
            var getRuntimeType = function () {
                for (var script of document.scripts) {
                    if (script.src.indexOf('_framework/blazor.web.js') !== -1) return 'united';
                    if (script.src.indexOf('_framework/blazor.webassembly.js') !== -1) return 'wasm';
                }
                return '';
            }
            var runtimeType = getRuntimeType();
            // customize the resource loader for the runtime that is loaded
            // https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/startup?view=aspnetcore-8.0#load-boot-resources
            var webAssemblyConfig = {
                loadBootResource: function (type, name, defaultUri, integrity) {
                    if (verboseStart) console.log(`Loading: '${type}', '${name}', '${defaultUri}', '${integrity}'`);
                    let useCompatThisResource = useCompatMode && type !== 'globalization';
                    let newUrl = useCompatThisResource ? defaultUri.replace('_framework/', '_frameworkCompat/') : defaultUri.replace('_frameworkCompat/', '_framework/');
                    return newUrl;
                },
            };
            if (runtimeType === 'wasm') {
                // Blazor WebAssembly Standalone App
                Blazor.start(webAssemblyConfig);
            } else if (runtimeType === 'united') {
                // Blazor Web App (formally Blazor United)
                Blazor.start({ webAssembly: webAssemblyConfig });
            } else {
                // Fallback supports both known Blazor WASM runtimes
                // Modified loader that will work with both United and WASM runtimes (doesn't require detection)
                webAssemblyConfig.webAssembly = webAssemblyConfig;
                Blazor.start(webAssemblyConfig);
            }
        })();
    </script>
</body>
</html>

There were a bunch of other little things to get it working that were different from a standalone Blazor WASM app but this is the issue you asked about that I came across as well. Let me know if you have any more problems and I will try to help.

I used this codes, and I run the web on iPhone 7 Pluse whitch iOS is 15.8.

I got some errors:

截圖 2023-11-24 下午3 37 15
LostBeard commented 10 months ago

Without seeing any of your code, I cannot help any further.

MarkSky commented 9 months ago

BlazorWASMSIMDNet7ToNet8Test

I have creatred a repository.

LostBeard commented 9 months ago

I am trying to figure out why ASP.Net Core server will not serve "icudt.dat" from _frameworkCompat because that is the root cause of the problem.

LostBeard commented 9 months ago

The issue is caused by ASP.Net Cores default UseStaticFiles configuration which does not serve unknown file types. The solution is to modify your Server Program.cs to enable the serving of .dat files and therefore icudt.dat from the _frameworkCompat folder.

In your Server Program.cs

Replace app.UseStaticFiles(); with either of the 2 below options.

// Enable serving of files with .dat
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".dat"] = "application/octet-stream";
app.UseStaticFiles(new StaticFileOptions
{
    ContentTypeProvider = provider
});

Or

// Enable serving of files with any extension
app.UseStaticFiles(new StaticFileOptions
{ 
    ServeUnknownFileTypes = true,
    DefaultContentType = "application/octet-stream"
});
LostBeard commented 9 months ago

I updated the repos and the README for this repo to better detail how to handle ASP.Net Core hosted projects. ASP.Net Core Hosted