dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.38k stars 10k forks source link

Multiple hosted Blazor apps get 404 error on blazor.webassembly.js #29179

Closed fuzl-llc closed 3 years ago

fuzl-llc commented 3 years ago

Describe the bug

I'm trying to create a Razor pages app with multiple Blazor "sub apps" at urls like /app1 and /app2. Everything works fine locally when debugging in Visual Studio but when I publish and try to run locally using the dotnet command (or when I try to host behind an Nginx reverse proxy) I get a 404 error for _framework/blazor.webassembly.js.

Thanks for any help you can offer.

To Reproduce

Below are my steps and the simplest possible repro is at the following public repo:

https://github.com/fuzl-llc/MultipleHostedBlazorApps

In Visual Studio 2019, created project MultipleHostedBlazorApps with default .NET 5.0 "Blazor WebAssembly App" template. Deselected "Configure for HTTPS" and checked "ASP.NET Core hosted." Checked in.

Added second client project called "Client2" with HTTPS and ASP.NET Core Hosted unchecked. The project, MultipleHostedBlazorApps.Client2, was put in a "Client2" folder next to the existing "Client" folder. Checked in.

Deleted all IIS references from all three project's launchSettings.json. Left server app to run on port 5000, updated client and client 2 to run on ports 5001 and 5002, respectively. In retrospect I don't think this matters as all projects load under the host app's port 5000...

Updated Blazor apps index.html files to have <base href="/app1/" /> and <base href="/app2/" /> for client and client2 (and matching page titles).

Started getting: Conflicting assets with the same path '/wwwroot/css/app.css' for content root paths error (but below actions fix it).

Changed each Blazor app's project file to include: <StaticWebAssetBasePath>app1</StaticWebAssetBasePath> (and <StaticWebAssetBasePath>app2</StaticWebAssetBasePath>) in the top property group under <TargetFramework>net5.0</TargetFramework>

In main host app's startup file, mapped both Blazor apps using a new extension method called "MapBlazorApp" which uses "MapWhen" to set up each Blazor app to be loaded at "sub app urls" of the main host app, at /app1 and /app2 of the main host app. This does things like UseBlazorFrameworkFiles passing in the new paths - see code for details.

Added an "index" Razor page to the host app with links to the two sub apps. Added links in both Blazor "sub apps" to each other and the host app main page, updated both app's index page title to show which app they are.

We can now run the app, see both apps working, and click between them.

Checked in.

This seems to be "success" as everything works beautifully when running locally in Visual Studio. The problems come now when I try to deploy... So, we right click and publish the host app. As part of this we change the default folder profile path from bin\Release\net5.0\publish\ to "C:\Temp\Multi." In that path we then see, under C:\Temp\Multi\wwwroot, folders like app1, app1_framework, app1app1, app1css which at first seems to make sense e.g. the prefix to distinguish framework files but the app1app1 folder seems a bit strange.

I can run the following successfully:

C:\Temp\Multi>dotnet MultipleHostedBlazorApps.Server.dll --urls http://localhost:5000

Everything appears great at first - I see the host app homepage with links to the two sub apps and when I click them I see the standard Blazor "Loading..." message but when I look in Chrome dev tools > Network, I see that http://localhost:5000/app1/_framework/blazor.webassembly.js gets a 404 "Not Found" error and the app never loads. The same thing happens for http://localhost:5000/app2/_framework/blazor.webassembly.js. All other static files seem fine, such as http://localhost:5000/app1/css/bootstrap/bootstrap.min.css and other .css files. If I search for blazor.webassembly.js I find it exists under app1_framework and app2_framework (as well as other versions with .br and .gz extensions appended).

Ultimately I'm trying to host this on Ubuntu via Nginx reverse proxy where I'd set up a config something like this:

server { listen 443 ssl; server_name test.com; ssl_certificate /etc/letsencrypt/live/test.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/test.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

location /
{
    proxy_pass         http://localhost:5000;
    proxy_http_version 1.1;
    proxy_set_header   Upgrade $http_upgrade;
    proxy_set_header   Connection keep-alive;
    proxy_set_header   Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $scheme;
}
# works except _framework/blazor.webassembly.js gets returned as the HTML of the root of the site:
location /app1 {
    root /var/www/test.com/wwwroot;
    try_files $uri $uri/ /app1/index.html;
}

location /app2 {
    root /var/www/test.com/wwwroot;
    try_files $uri $uri/ /app2/index.html;
}

} server { listen 80; server_name test.com; return 301 https://$host$request_uri; }

It seemed to work similarly where all static (html, css) files work but the blazor.webassembly.js file gives a 404.

How get I get this blazor.webassembly.js file to load properly using (1) the dotnet command locally and (2) hosting behind an Nginx reverse proxy?

Exceptions (if any)

Just a 404 error that blazor.webassembly.js cannot be found.

Further technical details

5.0.101

.NET SDK (reflecting any global.json): Version: 5.0.101 Commit: d05174dc5a

Runtime Environment: OS Name: Windows OS Version: 10.0.18363 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\5.0.101\

Host (useful for support): Version: 5.0.1 Commit: b02e13abab

.NET SDKs installed: 3.1.301 [C:\Program Files\dotnet\sdk] 3.1.400 [C:\Program Files\dotnet\sdk] 5.0.101 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.All 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.0-rc.1.20451.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

ejhg commented 3 years ago

Sanity check: What directory are you mapping to /var/www/test.com/wwwroot? Is it the wwwroot from your repo? If so, the _framework files won't be there. It needs to be the wwwroot from within the publish directory in your output bin. Never mind, I see you mention the files are present in the directory.

fuzl-llc commented 3 years ago

Thanks for the reply. I just saw you update your response as I was replying but it made me find some more info. The _framework files are not there where it is trying to load from. The issue seems like it might be in the publish step... if I take the app1_framework folder and copy it into the app1 folder and rename it to just _framework then that blazor.webassembly.js in it does get found. However, it there are two issues:

  1. I have a perfectly working app when I run in Visual Studio and then just do a standard "publish to folder" operation and it seems like it ought to just put things in the right place... I'm happy to learn if I'm doing something wrong but this part seems to maybe be a bug.
  2. Even though blazor.webassembly.js loads with these steps, we then get a new file requested that doesn't load: http://localhost:5000/app1/_framework/undefined

That "undefined" request just seems weird, again, like something is just wrong in the publish process.

ejhg commented 3 years ago

(FYI: I'm just a community developer like you. I don't use Visual Studio...)

I prefer to use the command line. Try this command, it will be much cleaner:

# To be sure: Clean up the bin and obj directories first.  On Linux it would be like this
rm -rf bin obj

# Run this (Should work on Windows too)
dotnet publish -c Release

# The output that you want will be within `/bin/Release/net5.0/publish/wwwroot`.  
# CAUTION: Do not use `/bin/Release/net5.0/wwwroot` (without `publish`) which 
# is also found within the `bin`.  The assemblies in the other directory are not fully 
# processed/trimmed.
fuzl-llc commented 3 years ago

Note: the publish process creates "app1_framework," "app1app1," "app1css," and "app1sample-data" as part of the publish. They are created alongside the "app1" folder. It seems like those are just "off" by a slash, i.e. "app1_framework" was placed in the same folder as "app1" but was really meant to be a "_framework" folder placed inside the "app1" folder. From the working app in Visual Studio, all I did was a standard right-click publish and those are the output locations that resulted.

Just saw your latest comment, thanks. My above is based on a Visual Studio publish but let me try your suggestion now...

ejhg commented 3 years ago

I wonder where the "app1" prefix is configured. Maybe it's configured within the *.csproj file? Check that file. If so, maybe you can simply add a slash to fix it.

ejhg commented 3 years ago

Yeah, I see it in your *.csproj file. Try changing

 <StaticWebAssetBasePath>app1</StaticWebAssetBasePath>

to

 <StaticWebAssetBasePath>app1/</StaticWebAssetBasePath>
fuzl-llc commented 3 years ago

Yeah, I've tried every combination of putting slashes before and after that and those all seem to cause the app to fail when I run/debug the app in Visual Studio. It seems to require slashes to exist or not exist in very specific ways for the "map Blazor files" stuff to work. I've got all those right so it builds and runs in VS, so not sure why it would then not create the correct build output.

fuzl-llc commented 3 years ago

Let me try that... I think I did at one point... but let me try again.

fuzl-llc commented 3 years ago

When I do that, it builds in VS but I get a 404 when I navigate to the app at /app1/.

ejhg commented 3 years ago

I haven't used StaticWebAssetBasePath myself, but I was frustrated with similar issues. I just opened and closed https://github.com/dotnet/aspnetcore/issues/29180 after learning about StaticWebAssetBasePath from you.

Maybe the whole thing is just buggy? [shrug]

Let's see what others say.

My recommendation is this: Focus on getting the directory structure right before bringing Nginx into the picture. Otherwise you will walk in circles. I personally test my local static directories with serve (https://www.npmjs.com/package/serve). I simply run serve [specify directory here] and the static website should just work. Make sure it works with the subdirectories you expect. Once you get that working, you can mess with Nginx, but then it's not a dotnet issue :)

ejhg commented 3 years ago

I get a 404 when I navigate to the app at /app1/

Try going to /app1/index.html. Sounds like your file server is not automatically mapping 404s to index.html. I think serve will handle that part for you.

fuzl-llc commented 3 years ago

If I do the publish command you mention above from the solution directory, it publishes and completes fine but there is no _framework folder anywhere to be found anywhere under bin\Release\net5.0\publish (nor any of the weird app_framework folders). It has app1 and app2 folders and those have some static (e.g. index.html and css files etc.) but none of the _framework stuff or actual dlls from the Blazor apps.

ejhg commented 3 years ago

You did include the -c Release part, right? Is there a Debug folder? Make sure we are not mixing those up. And to be triple sure, you did delete the folders before the build, right?

Look at the command line output. What directories is it writing files to? The command line never lies :)

fuzl-llc commented 3 years ago

Yes, I had the -c Release - copied directly from your comment. Yes, I deleted everything and did a "Clean solution" in VS. There is a debug folder but it has no "publish" in it - just BlazorDebugProxy and ref folders and no other files. Cleared it out again, there are no debug folders. Ran it again and confirmed command line output is to \Server\bin\Release\net5.0\publish\

fuzl-llc commented 3 years ago

Regarding getting it working locally, yes, I totally agree - my original post really comments on two issues. First is that I have it running in VS, then just publish and run with dotnet command locally using Kestrel, and that doesn't work due to the 404. I'm mainly focused on that now - the Nginx thing I suspect will just work once I get it working locally.

ejhg commented 3 years ago

Can you push your latest code to your repo? I'll try it on my side.

fuzl-llc commented 3 years ago

The code in the repo right now is the one that I think best demonstrates the issue. If you grab that and run it locally (in Visual Studio if you have a copy) it should work perfectly. If you then right click the "Server" project and publish it (or publish it using any of the methods we've discussed) and then try to run that locally with Kestrel using the dotnet command you should get the 404 error.

fuzl-llc commented 3 years ago

There seems to be a difference in the publishing though. If you do it in VS like me, you get the weird folder structure (e.g. an app1_framework folder right next to the app1 folder. If you do it like you suggested, you just get the app1 folder and nothing else _framework related is anywhere to be found.

ejhg commented 3 years ago

It seemed to work for me. Maybe you need to resync the file explorer within Visual Studio?

I don't think it made a difference, but I removed the DevServer dependency because you obviously don't need it for a static app.

> dotnet publish -c Release

Microsoft (R) Build Engine version 16.8.0+126527ff1 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  Restored /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Shared/MultipleHostedBlazorApps.Shared.csproj (in 84 ms).
  Restored /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Client2/MultipleHostedBlazorApps.Client2.csproj (in 199 ms).
  Restored /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Server/MultipleHostedBlazorApps.Server.csproj (in 198 ms).
  Restored /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Client/MultipleHostedBlazorApps.Client.csproj (in 200 ms).
  MultipleHostedBlazorApps.Shared -> /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Shared/bin/Release/net5.0/MultipleHostedBlazorApps.Shared.dll
  MultipleHostedBlazorApps.Shared -> /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Shared/bin/Release/net5.0/publish/
  MultipleHostedBlazorApps.Client -> /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Client/bin/Release/net5.0/MultipleHostedBlazorApps.Client.dll
  MultipleHostedBlazorApps.Client (Blazor output) -> /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Client/bin/Release/net5.0/wwwroot
  MultipleHostedBlazorApps.Client2 -> /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Client2/bin/Release/net5.0/MultipleHostedBlazorApps.Client2.dll
  MultipleHostedBlazorApps.Client2 (Blazor output) -> /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Client2/bin/Release/net5.0/wwwroot
  Optimizing assemblies for size, which may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink
  Optimizing assemblies for size, which may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink
  Compressing Blazor WebAssembly publish artifacts. This may take a while...
  Compressing Blazor WebAssembly publish artifacts. This may take a while...
  MultipleHostedBlazorApps.Client -> /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Client/bin/Release/net5.0/publish/
  MultipleHostedBlazorApps.Client2 -> /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Client2/bin/Release/net5.0/publish/
  MultipleHostedBlazorApps.Server -> /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Server/bin/Release/net5.0/MultipleHostedBlazorApps.Server.dll
  MultipleHostedBlazorApps.Server -> /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Server/bin/Release/net5.0/MultipleHostedBlazorApps.Server.Views.dll
  MultipleHostedBlazorApps.Server -> /Users/ejhg/sb1/MultipleHostedBlazorApps/MultipleHostedBlazorApps/Server/bin/Release/net5.0/publish/
Screen Shot 2021-01-09 at 22 48 02 Screen Shot 2021-01-09 at 22 48 36
fuzl-llc commented 3 years ago

So, you grabbed the code, changed to add the slash after app in StaticWebAssetBasePath, ran the publish command from the solution folder, and that's your output? And it builds and runs using Kestrel on the command line?

ejhg commented 3 years ago

(Sometimes when you modify files outside of Visual Studio, it retains and uses its in-memory version of the file, so it doesn't do what you think it's doing. And sometimes, when you generate build output very quickly, the file explorer is also out of date.)

Yes, that's my command line and my folder output. As you say:

Hopefully this helps.

Take a break, maybe you've been staring at the screen too much :)

fuzl-llc commented 3 years ago

Just cleaned the solution, rebooted, and ready to try your steps one more time... if that fails, I'll definitely take that break! I can't thank you enough for your time and help, it is REALLY appreciated.

Kestrel is Microsoft's local web server that we can use to run apps locally outside (or from within) Visual Studio. E.g. you can run a web app like this:

dotnet run MultipleHostedBlazorApps.Server.dll --urls http://localhost:5000

ejhg commented 3 years ago

I wish there was a way to instant message. I do see something weird going on. Will update.

fuzl-llc commented 3 years ago

Btw, I updated the above command to be http not https... I assume Kestrel works the same on Mac so I'd be interested if it works for you. I've only ever used a PC for dev.

fuzl-llc commented 3 years ago

Ok, now I'm totally disturbed. I did NOT make the change on lines 5 and 10 after my reboot and I just ran the "dotnet run" line above as a quick confirmation I had the command line correct before posting. IT built the project as its first step, then ran it, then I went to http://localhost:5000 and everything works, including loading both Blazor apps.

ejhg commented 3 years ago

Yeah, that's nice, but not sure if that solves your problem. I went through a similar situation. Basically, dotnet run runs some specialized MS server that knows how to serve this content. It's not a static file server by any means. If you need compatibility with a static file server, you need to get the publish (preferably) or build commands to work.

Maybe that's what VS was using in the background (ie, dotnet run)

(Still looking into something weird going on...)

fuzl-llc commented 3 years ago

I believe that "special" MS server is in fact Kestrel if I'm not mistaken... And in production I have Kestrel running behind an Nginx reverse proxy - their recommended approach.

fuzl-llc commented 3 years ago

I still may have an issue with Nginx and how all the proxy stuff works but like you said, getting it working locally is the first step. I don't know if this is a case of the magic reboot properties of PCs or if the build/publish process done by "dotnet run" is different (and correct) compared to the dotnet publish from the command line and/or Visual Studio's publish process.

fuzl-llc commented 3 years ago

Just as a future reference to anyone reading this... here is the MS guidance on hosting on Linux behind Nginx reverse proxy:

https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-5.0

ejhg commented 3 years ago

That's all superstition (get a mac) :)

I served the publish directory using a static file server (npm serve), and at least found one issue that's most likely a Blazor issue.

I personally think you should close this issue and open a new one with the following description after making the edits to your .csproj files. Also add this line to your csproj files <PublishTrimmed>false</PublishTrimmed> before pushing your code, otherwise that's the first thing they will object to. There may be other issues, but at least this way you can start narrowing down on specifics.

Issue Description

Running dotnet publish -c Release in the sample repo https://github.com/fuzl-llc/MultipleHostedBlazorApps produces a blazor.boot.json file in the publish directory with zero assembly references even with trimming disabled. This causes the entire WASM loading process to fail.

# /MultipleHostedBlazorApps/Client/bin/Release/net5.0/publish/blazor.boot.json
{
  "cacheBootResources": true,
  "config": [ ],
  "debugBuild": false,
  "entryAssembly": "MultipleHostedBlazorApps.Client",
  "icuDataMode": 0,
  "linkerEnabled": true,
  "resources": {
    "assembly": { },
    "lazyAssembly": null,
    "pdb": null,
    "runtime": { },
    "satelliteResources": null
  }
}

Good luck, sir!

fuzl-llc commented 3 years ago

I've go to go take care of some parental duties but will be back tonight or tomorrow. Just to document what seems to work for me at this point.

  1. Take the exact code that is currently in the repo (change set 633a2b7)
  2. I cleaned in VS and manually ensured all bin/obj folders are gone but it should be that way if code is fresh from the repo.
  3. From a Windows command line run from \MultipleHostedBlazorApps\Server, I ran: dotnet run MultipleHostedBlazorApps.Server.dll --urls http://localhost:5000
  4. I got a "Building..." message and then the normal Kestrel "info" output.
  5. I went to http://localhost:5000/ in a browser.
fuzl-llc commented 3 years ago

Looks like we commented at the same time on that one... I'd agree with your recommendation but I'm now getting it to work without those changes to the .csproj files. The steps I just entered above work perfectly. I'm going to experiment more and see if I can figure out the differences between the different publish methods and/or see if I can get everything running in production. If I can narrow it down, I'll take the recommendation to start a new clean issue. But if it still seems like this apparent "different publish methods get you different results" business remains, I think all this info may be relevant. We'll see...

Thanks again for all your help. I've been going crazy on my own with this all day and your help has been extremely helpful.

fuzl-llc commented 3 years ago

Note, the above gets the right files in the right folders in \MultipleHostedBlazorApps\MultipleHostedBlazorApps\Client\bin\Debug\net5.0\wwwroot\_framework but they are under Debug and there are no "publish" folders. Perhaps some dotnet run parameters could specify the environment too... though this idea of using dotnet run to get my published output is clearly silly but then again, it is the only thing getting me workable output right now.

ejhg commented 3 years ago

caution:

(Trust me, I've been hacking really hard with Blazor this week. The publish and deploy part was the first thing I tackled. WTBS, I didn't mess with multiple projects within a solution. That's a monolith from my point of view, and I personally would avoid that pattern. Microsoft loves monoliths... Highly opinionated Linux web dev here :) )

fuzl-llc commented 3 years ago

Good catch on the slash - I copied and pasted so it was there but apparently the slash in that slash underscore combo needs to be escaped. Just converted to a double slash and it shows now.

fuzl-llc commented 3 years ago

I spent some more time tonight but I'm giving up for the night. If I add the slash in the StaticWebAssetBasePath element (either forward or backward slash after app1 or app2), publishing the app seems to put files in the _framework folder properly but I can no longer run the app in visual studio as the /app1 and /app2 apps don't load. And if I try to use the published files behind Nginx they load but I get weird stuff related to that blazor.boot.json file you'd mentioned (seems to not load sometimes and sometimes it does but then I get a request to a file named "undefined" that never loads). Hopefully some sleep will help.

Maybe I can leave as is for dev and then for publishing, publish the Blazor apps separately and some how configure Nginx to just serve them from /app1 and /app2 under the main app.

fuzl-llc commented 3 years ago

As an aside, I also don't like monoliths. I actually had these as a main app i.e. app.com and then separate Blazor subdomain apps app1.app.com and app2.app.com. Then I had id.app.com with OIDC auth used by all the apps. I though it was very nice but started running into all kinds of cross domain issues and then read this:

https://leastprivilege.com/2020/03/31/spas-are-dead/

Very interesting read. I was running into some of the very issues mentioned due to cross domain request issues. Yes, I did a bunch of CORS stuff and could make stuff work for now but not sure about the future... I also wanted to experiment with the BFF (backend for frontend) architecture he mentions. It is interesting because having app.com/app1 and app.com/app2 seems like a monolith but if app.com is really a light razor pages app and has a YARP reverse proxy to a separate API project behind it, it starts to feel pretty well separated. And while app1 and app2 appear to be part of the monolithic parent app, actually they are separate VS projects and don't have any dependencies on the main app. I'm not even sure I've convinced myself yet but that's why I'm experimenting with it. It seems decoupled in some good ways in spite of sharing the same domain and appearing to be one big app. That appearance might actually be good - I kind of like that people know it is all "under" app.com so they know it is all from me and there are no redirects to subdomains. Anyway, I'd like to figure out how to get it working and then continue to evaluate how much I like the architecture.

ejhg commented 3 years ago

@fuzl-llc Agree, it's an interesting topic. CORS is indeed challenging, but one gets better debugging it and implementing it over time. How to drawing context boundaries around apps (and microservices) is an open ended topic in itself. My take is, in some cases it makes sense to split and it also sometimes makes sense to backtrack and combine. I always compromise. It all depends on the use case.

I would say that using a reverse proxy or load balancer to map each path like /app1 and /app2 to a different backend server host like app1.example.com and app2.example.com still counts as a decoupled architecture (ie, it's not a too much of a "monolith"). In this example you would have a total of 3 Nginx servers: 1 reverse proxy and 1 for each of the apps simply serving files. I think that nowadays it's more practical to use the load balancers offered by cloud providers instead of using Nginx as a reverse proxy. Some thought still has to go into how CORS will be implemented. The CORS problem never goes away :)

ejhg commented 3 years ago

@fuzl-llc I figured out the issue that I observed. Using the <StaticWebAssetBasePath> property with anything inside it completely breaks the blazor.boot.json in the publish directory. On any Blazor repo, try running dotnet publish -c Release with and without that line. Observe that when that line is present, the properties within blazor.boot.json are all blank. This is definitely an isolated bug that we should open an issue on.

Regarding your build issues, I think you may want to try running rm -rf bin obj every time you run dotnet publish. It sounds like you have some lag in the changes you make with respect to what the build produces. Maybe your IDE is not persisting some changes to disk when you run the build, so you are not really getting what you see. Does that make sense?

BrentCarter commented 3 years ago

That's great you isolated that. Definitely seems like a bug. On build issues, I consciously clear out those bin and obj folders every time I try to publish and it doesn't seem to make any difference.

I'm curious if you've ever run the repo code successfully on your end e.g. just grab the code and run in Visual Studio or VS Code. For me, I can run it as-is perfectly fine in Visual Studio. If I publish, seemingly any way, I can't get the published files to come out right. If I add the trailing slash like you've noted, then I can get the published files to look right but then I can no longer run the app successfully in VS. If you can both run/debug it in the IDE and publish it with that slash in there I'm going to be at a bit of a loss as to what is different on your machine and mine (understanding they are PC and a Mac, but even so the code would be the same). Have you run the code from the IDE? With or without the slash?

ejhg commented 3 years ago

I created this https://github.com/dotnet/aspnetcore/issues/29185

BrentCarter commented 3 years ago

Awesome, thanks. Having a short/clean one like that should be easier for them to address.

ejhg commented 3 years ago

I don't have VS. I use JetBrains Rider only for editing, and my builds all run on Linux inside Docker, so the CLI is my source of truth because that's what ultimately gets deployed.

My hypothesis is that VS is bypassing the whole publish process just like dotnet run. They both know how to serve the content, but not how to lay it out on the file system for a file server to serve... Hopefully others can provide a better answer.

Have a good night! :)

ejhg commented 3 years ago

https://github.com/dotnet/aspnetcore/issues/27776 may also fix this.

fuzl-llc commented 3 years ago

Yeah, it seems like the workaround there at least partially fixes this. The fix there is basically an automated way to do what I mention above about copying the app1_framework files to app1/_framework so everything is in the proper place. He does it as part of the build so that is very helpful. But I still get that request to /app1/_framework/undefined which seems to make the app not actually run. I've posted there to see if anyone else sees this behavior with the fix mentioned over there. If others have that issue over there, then this whole thread can probably just be marked as a duplicate and a single discussion continued there.

fuzl-llc commented 3 years ago

Although, actually, I just noticed that issue has already been marked "Closed" so not sure anyone is still paying much attention to it. Hmm. Hopefully 5.0.2 comes out soon and whatever the fixes they added for #27776 will fix everything... though it would be great to understand where this "undefined" request is coming from so maybe I can get a workaround going in the meantime. The request comes from blazor.webassembly.js but that file is minimized and/or obfuscated so it isn't easy to review what that file is actually doing by adding breakpoints.

mkArtakMSFT commented 3 years ago

Please try out the upcoming 5.0.2 release which will come out later this week and let us know if that resolves the issue.

fuzl-llc commented 3 years ago

Thanks @mkArtakMSFT. Version 5.0.2 came out today and while the _framework files are now put in the correct place upon publish, I'm still having the same issue with the request to "/app1/_framework/undefined" and the app hanging. Specific steps I'm taking coming soon...

fuzl-llc commented 3 years ago

Steps:

  1. Get the latest code from my sample repo at https://github.com/fuzl-llc/MultipleHostedBlazorApps
  2. If you already had the code, clean solution and/or delete all bin/obj folders.
  3. Open cmd window
  4. cd [your-path]\MultipleHostedBlazorApps\Server
  5. dotnet publish -c Debug
  6. Everything seems to publish fine, including putting _framework files in correct places.
  7. cd [your-path]\MultipleHostedBlazorApps\Server\bin\Debug\net5.0\publish
  8. dotnet MultipleHostedBlazorApps.Server.dll
  9. Point browser to http://localhost:5000 and click on "App1" link (or just point browser to http://localhost:5000/app1)
  10. Look in Chrome dev tools and notice in Console tab:

blazor.webassembly.js:1 Error parsing 'integrity' attribute ('undefined'). The hash algorithm must be one of 'sha256', 'sha384', or 'sha512', followed by a '-' character. (anonymous) @ blazor.webassembly.js:1 blazor.webassembly.js:1 Uncaught (in promise) Error: Content hash is required at e. (blazor.webassembly.js:1) at blazor.webassembly.js:1 at Object.next (blazor.webassembly.js:1) at blazor.webassembly.js:1 at new Promise () at r (blazor.webassembly.js:1) at e.loadResourceWithCaching (blazor.webassembly.js:1) at e.loadResource (blazor.webassembly.js:1) at blazor.webassembly.js:1 at l (blazor.webassembly.js:1) blazor.webassembly.js:1 Uncaught (in promise) Error: Content hash is required at e. (blazor.webassembly.js:1) at blazor.webassembly.js:1 at Object.next (blazor.webassembly.js:1) at blazor.webassembly.js:1 at new Promise () at r (blazor.webassembly.js:1) at e.loadResourceWithCaching (blazor.webassembly.js:1) at e.loadResource (blazor.webassembly.js:1) at blazor.webassembly.js:1 at l (blazor.webassembly.js:1) undefined:1

  1. Notice in Chrome dev tools Network tab that while blazor.webassembly.js and blazor.boot.json load properly there is a 404 for a request to http://localhost:5000/app1/_framework/undefined
  2. The Blazor app just hangs and never fully loads.
fuzl-llc commented 3 years ago

Note: this "Error parsing 'integrity' attribute ('undefined')" error seems to be the same error seen by @JonTvermose in Issue #27776 and @javiercn comments that 5.0.2 would likely fix it. I'll try a little longer in case I'm doing something wrong, but in my first attempts, it doesn't seem that 5.0.2 has fully solved the issue. Issue 27776 is closed so not sure if I should be commenting there or here - I don't want to create duplicates but also don't want to be commenting on a closed issue... please advise if we should keep this issue going or revive the other.