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.15k stars 9.92k forks source link

.NET 8 migration from WASM to Blazor Web App - environment not set correctly #53832

Open Stra1ghter opened 7 months ago

Stra1ghter commented 7 months ago

Is there an existing issue for this?

Describe the bug

I have followed the migration guide from Blazor WebAssembly to Blazor Web App. After migrating, IWebAssemblyHostEnvironment always returned that I'm running the Production environment.

From what I have tested, I can see that < .NET 8 the response to blazor.boot.json always returned a Blazor-Environment header. With the changes described in the migration guide, this is no longer the case and the client cannot determine its environment.

I propose:

My team is totally dissatisfied with the options in the Blazor environment docs. Because they have big disadvantages:

Expected Behavior

IWebAssemblyHostEnvironment should return the correct environment after following the migration guide.

Steps To Reproduce

Create a Blazor WebAssembly app that uses IWebAssemblyHostEnvironment on the client and follow the migration guide to a Blazor Web App.

Exceptions (if any)

No response

.NET Version

8

Anything else?

No response

Stra1ghter commented 7 months ago

Setting the environment after src="_framework/blazor.web.js" works, but I would highly emphasize to improve the documentation there.

As discussed above it's also a heuristic and no real solution for our team.

javiercn commented 7 months ago

image

image

image

Just fired up a new version of the template and this seems to work correctly, can you provide a minimal repro project?

ghost commented 7 months ago

Hi @Stra1ghter. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

guardrex commented 7 months ago

BTW @Stra1ghter ... The Environments article was revised for 8.0. Take a look at the guidance there ...

https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/environments?view=aspnetcore-8.0

... especially this section ...

https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/environments?view=aspnetcore-8.0#read-the-environment-client-side-in-a-blazor-web-app

I've opened an issue to add a cross-link that article in that section of the migration article. I don't know if what we have there thus far will resolve the problem(s) that you're facing here, but I'll keep an πŸ‘οΈ on this issue for further doc update opportunities. For now, at least the cross-link will be present in the migration guidance.

Stra1ghter commented 6 months ago

Sorry for the delay.

@javiercn Here is a minimal repo. The main branch is the .NET 6 WASM template that is correctly displaying the Hosting environment client-side. In the update-branch I followed your migration guide and the environment is always displayed as "Production".

As stated in the issue: Some middleware of the .NET 6 template (guess UseBlazorFrameworkFiles) provides the Blazor-Environment as a header to blazor.boot.json. This is removed during migration to a Blazor Web App. Hence, the IWebAssemblyHostEnvironment defaults to Production.

Before migration: image After migration: image

Stra1ghter commented 6 months ago

@guardrex Sorry for the delay. I have read the blazor environment doc, I have even linked the article in my report above! However

  1. Even if your specific section reads "Read the environment client-side in a Blazor Web App", it creates a server-side component. At least from my "newbie" .NET 8 Blazor knowledge that was not usable in the client project itself. And prerendering etc. is not discussed in the migration guide itself.

  2. The other means discussed in the article such as this is a heuristic and no real solution (setting the environment via URL). I don't like this approach at all, URLs can change.
    I also want to note that I find the src {BLAZOR SCRIPT} in that example rather confusing. Why not name it specifically _framework/blazor.web.js as discussed in the migration guide?

guardrex commented 6 months ago

it creates a server-side component

That section explains how to inject IWebAssemblyHostEnvironment into interactive WASM or Auto components, which can cause failures in those components when the service doesn't exist on the server during prerendering or before the Blazor bundle downloads. I think where you stated "it" in your last remark you were referring to your sample app (and interactive Server components), but I didn't think that your opening post clarified what type of components you were having trouble with. Of course, that section won't help if you discovered a real πŸͺ² or if we're lacking some piece of important information in the guidance. I'm πŸ‘‚ here for what the investigation uncovers.

it is just a heuristic based on the URL

That's just an example for a very specific scenario, which is called out in the heading, via startup configuration.

... and this isn't the primary article for setting the environment in a BWA. As the guidance in the opening remarks indicates ...

For general guidance on setting the environment for a Blazor Web App, see Use multiple environments in ASP.NET Core.

I'll highlight that line, possibly in its own new section, because BWA render mode coverage in the introduction that was added for 8.0's release now crowds out that critical point and makes it seem less important. You set the environment for a BWA using the same approaches as you would for any ASP.NET Core app. Again, that might not have any bearing on the issue here, but I'll update the article to make that clearer.

src {BLAZOR SCRIPT} in that example rather confusing. Why not name it specifically _framework/blazor.web.js as discussed in the migration guide?

That migration guide section is specific for a particular type of Blazor app migration scenario ... hosted WASM to BWA. Other places in reference docs must cater to additional types of Blazor apps that don't use the same script or have the same script location, so a placeholder is used with a cross-link to a section that indicates the script name is and where the script is found. Although the reference docs can version the exact names and locations into place, we have a weak documentation versioning system given the amount of churn in the framework, and this approach of calling out the script name and location in a separate section with links to it from around the reference docs is a compromise to make the docs manageable to maintain.

guardrex commented 6 months ago

BTW @Stra1ghter .... It looks like your app isn't fully migrated ... you must follow Step 1 ...

Follow the guidance in the first three sections of this article:

... and update the target framework and package reference versions.

Stra1ghter commented 6 months ago

@guardrex I understand your points, especially the {BLAZOR SCRIPT} point.

The IWebAssemblyHostEnvironment injecting does work for the new template using interactive server and client components, but not after following the migration guide. (There the service does already exist, but defaults to production as outlined above.)

.... It looks like your app isn't fully migrated ... you must follow Step 1 ...

What are you referring to?

guardrex commented 6 months ago

There the service does already exist

The service won't be available in the server app after full migration.

Step 1 is to update the target framework and the package references. Your repro app is on 6.x, so those need to be updated to 8.x. For example in the server project ...

- <TargetFramework>net6.0</TargetFramework>
+ <TargetFramework>net8.0</TargetFramework>

... and ...

- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.26" />
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.1" />

Same thing for the .Client project.

BTW ... You're probably are already aware of the roll-forward behavior for minor and patch versions, but the latest template uses 8.0.1, so I showed that instead of 8.0.0, which will work, too.

I just discovered a pair of steps to add to the migration doc. There are two MS Build props to add to the .Client project ...

<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>

There's a final item to address with your repro app that I don't think is causing a problem: Remove the Microsoft.AspNetCore.Components.WebAssembly.DevServer package reference from the .Client project. I don't think that's causing a problem here, but it really shouldn't be there.

I'll get a PR in now to address these additional steps and ping you on it.

Stra1ghter commented 6 months ago

@guardrex I don't think you've read my comment.

The main branch is the .NET 6 WASM template that is correctly displaying the Hosting environment client-side. In the update-branch I followed your migration guide and the environment is always displayed as "Production".

The update branch is and has been updated 1:1 according to your migration guide as far as I can tell.

guardrex commented 6 months ago

Oh ... sorry about that. I have like ... 1,000 things going on here 😩 at the moment ... I'm working down a large backload of issues. I (obviously) merely clicked your repro link without giving it a second thought.

Anyway ... it's good that we had the discussion thus far. Although the problem isn't fixed yet, we did update the migration steps with important content.

I'll take a look at the correct repro solution πŸ™ˆ in a minute after I deal with another πŸ”₯ in docs πŸš’πŸš“πŸš‘.

Stra1ghter commented 6 months ago

No worries.

I think it really comes down to the Blazor-Environment header for blazor.boot.json. That has worked wonderfully before, like a "battiers-included" approach and is just lacking a similar alternative (e.g. a middleware) when migrating to a BWA.

guardrex commented 6 months ago

This will now flip back to βš”οΈ Sir @javiercn of .NET βš”οΈ ... with a note on some strange behavior that I saw while working with the repro app. The behavior ... Production env using your code, @javiercn, in the .Client project ... does occur with this converted app. Simply changing the environment in the server project's launch settings to some value other than "Development" unexpectedly throws a nasty exception that breaks the app ...

MONO_WASM: Failed to load config file ./blazor.boot.json SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data Ne/<@https://localhost:7077/_framework/dotnet.js:3:26598 dotnet.js:3:918 Uncaught (in promise) Error: Failed to load config file ./blazor.boot.json SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data Ne https://localhost:7077/_framework/dotnet.js:3 5 dotnet.js:3:26598 Uncaught (in promise) Error: Failed to start platform. Reason: Error: Failed to load config file ./blazor.boot.json SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data ei https://localhost:7077/_framework/blazor.web.js:1

Stra1ghter commented 6 months ago

@guardrex I have identified another missing piece in the migration guide. One has to adjust the link-href for the isolated css from {App}.Client.styles.css to {App}.Server.styles.css.

guardrex commented 6 months ago

Yes ... I see going from ...

https://github.com/dotnet/aspnetcore/blob/v7.0.16/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/wwwroot/index.html#L13

... to ...

https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/App.razor#L12

I'll make that update right now. Thanks for the comment! πŸ‘

Stra1ghter commented 6 months ago

@guardrex perfect.

One other minor thing I have just noticed (it's just about wording). Step 11 of the WASM migration guide mentions that one has to position

app.UseAntiforgery immediately after the call to app.UseRouting

After two sentences it explains to move app.UseAntiforgery after app.UseAuthentication and app.UseAuthorization.

It reads a little strange that it should immediately follow UseRouting, but then later it should basically immediately follow UseAuthorization (if it exists).

It is a bit nit-picky, but I would remove the "immediately" wording. (It caused me to miss the last sentence of that paragraph during one upgrade.)

Stra1ghter commented 4 months ago

@guardrex are there any news regarding the environment setup for Blazor with the upgraded .NET 8 setups?

guardrex commented 4 months ago

This issue is marked for investigation/resolution at Preview 7 of 9.0, which I think will be the August preview release.

The guidance that we're still maintaining without additional updates at this time (they may tell me to change it later) is at ...

https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/environments?view=aspnetcore-8.0#read-the-environment-client-side-in-a-blazor-web-app

... and the whole article is referenced in the Migration Guide here ...

https://learn.microsoft.com/en-us/aspnet/core/migration/70-80?view=aspnetcore-8.0&tabs=visual-studio#update-service-and-endpoint-option-configuration

Until I receive further information, that's all I'll have on it in the docs at the moment. I'm πŸ‘‚ here for further developments.

philippedurocher commented 4 months ago

I have exactly the same problem! I upgraded from 7.x to 8.x, followed the upgrade guide, implemented the ServerHostEnvironment class as explained in the guide, but I still have 'Production' as the environment name.

What I did to work around it, I consider it a hack and I don't really like it: I added 'Environment' as a key in the client's appsettings.json and implemented ServerHostEnvironment in the client, with an adjustment to read the value of the key in the client's appsettings.json. Then, I change the value at deployment time with the pipeline.

Is that a valid workaround?