3

I am using VS2019 Preview. I have created a "server-hosted" Blazor application using the latest Blazor extension (16.0.19227). This is the variant that contains 3 separate projects...

  • MyApp.Client
  • MyApp.Server
  • MyApp.Shared

I can debug this by making MyApp.Server the active project and all works fine but I'm struggling to publish/deploy this to Azure. I have tried the following...

  • Right-click on MyApp.Server in Solution-Explorer
  • Choose "Publish"
  • Go through the wizard to create a new publish profile
  • Change the deployment mode to "self-contained"
  • Hit publish

At this point I get an error during deployment...

CSC(0,0): Error CS0006: Metadata file 'D:\work\Applications\Web\MyApp.Client\bin\Release\netstandard2.0\win-x86\MyApp.Client.dll' could not be found

This appears to be because the "Target Runtime" in the web-deploy profile is set to win-x86. The client application is actually being built as

"D:\work\Applications\Web\MyApp.Client\bin\Release\netstandard2.0\MyApp.Client.dll"

(without the additional win-x86 subfolder) so the deployment process seems to be making an incorrect assumption about the paths used by the build process. There's no way in the publish dialog to specify a blank/don't care target runtime.

Is there a workaround for this or perhaps I am using the wrong approach for deployment?

There is some official documentation but it's not very helpful.

Update It seems that the deployment is using the output path of the Client project and then just appending netstandard2.0{Target Runtime} to it so changing the output path in the Client project is not enough to work around the issue.

Update 2 Removing the RuntimeIdentifier tag in the publish profile by editing the xml simply results in deploy-time error stating that an empty RuntimeIdentifier is incompatible with a self-contained deployment. Unfortunately the self-contained deployment is necessary because Azure does not yet host .net core 3 directly.

NeilMacMullen
  • 3,339
  • 2
  • 20
  • 22
  • 1
    Is there any current reason that you need to publish as 'self-contained'? I also currently have errors using that. However publishing in 'Framework-Dependent' mode (Target runtime: portable) seems to work ok publishing to an azure appservice. (I get a warning about unsupported, but it still works). Edit: Bearing in mind that at the time of writing, blazor is still in preview – App Pack Jun 11 '19 at 13:20
  • I _assumed_ I needed to use self-contained because the server project is targeting "netcoreapp3.0" and this doesn't exist on the server. In the publish dialog "netcoreapp3.0" is the only supported Target Framework and "portable" is not an option in the Target Runtime dropdown. Do you definitely see these options for a server-hosted Blazor app? – NeilMacMullen Jun 11 '19 at 13:34

3 Answers3

4

because Azure does not yet host .net core 3 directly.

But it does.

In the Azure Portal, go to your WebApp after deployment (or create one beforehand).

Go to Extensions and click Add [+] and select ASP.NET Core 3 (x86 for the free hosting).

Also go to Settings, General and enable WebSockets, they're Off by default.


Temporary:

Note that Preview-6 is not available as an extension, so either use Preview-5 or deploy as self-contained.

H H
  • 263,252
  • 30
  • 330
  • 514
  • Thanks Henk - these are indeed the missing steps! Just for completeness... The Extension is "ASP.NET Core 3.0 (x86) Runtime" (in case you don't see it under "Core") and the WebSockets setting is under Settings/Configuration/General Settings/Platform Settings. – NeilMacMullen Jun 11 '19 at 14:01
  • Yes, I had to go by memory. – H H Jun 11 '19 at 14:05
  • Thanks @HenkHolterman but I get an error trying to add this extension: "`Conflict (HTTP Status Code: 409)`" – Quango Jun 14 '19 at 07:53
  • No clue here. Post a separate question, document it well. – H H Jun 14 '19 at 07:59
2

Couldnt put a picture in the comment, so I thought i'd show it here. This is my current publish wizard.

enter image description here

Just did it with a brand new project via new project -> Asp.net core web application -> blazor (Asp.net core hosted) built and published fine to azure app service fine.

App Pack
  • 1,512
  • 10
  • 9
  • Thanks AP - actually marked Henks reply as the "answer" but this was useful also. – NeilMacMullen Jun 11 '19 at 14:02
  • I agree, Henk's answer was better. Actually did that on mine too now to make sure it's running properly. – App Pack Jun 11 '19 at 14:04
  • Getting a 404 on my app despite it working in Preview 5 - looks like the `dotnet publish` is either broken or the Blazor build is. The content isn't set up. Can't run publish from VS2019 either (that is broken too) – Quango Jun 14 '19 at 07:52
  • Do you publish BlazorTest.Server or BlazorTest.Client? – mko Nov 05 '19 at 18:02
0

My answer is:

  • Configure the publish profile to "Self-contain" deployment mode.
  • Edit all .csproj files to change <TargetFramework>...</TargetFramework> node name to <TargetFrameworks>...</TargetFrameworks>. (see also: https://stackoverflow.com/a/42855070 )
  • Fix the web root folder path string at runtime in Startup class like below.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json.Serialization;
using System.IO;
using System.Linq;

namespace BlazorHostedOnAzure.Server
{
    public class Startup
    {
        // 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.AddMvc().AddNewtonsoftJson();
            services.AddResponseCompression(opts =>
            {
                opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
                    new[] { "application/octet-stream" });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseResponseCompression();

            // ---- APPEND PART.1 BEGIN ----
            var clientBlazorWebRootPath = default(string);
            // ---- APPEND PART.1 END ----

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBlazorDebugging();
            }

            // ---- APPEND PART.2 BEGIN ----
            else
            {
                if (env.WebRootPath != null)
                {
                    var pathOfIndex = Path.Combine(env.WebRootPath, "index.html");
                    var pathOfContent = Path.Combine(env.WebRootPath, "_content");
                    if (!File.Exists(pathOfIndex) && Directory.Exists(pathOfContent))
                    {
                        clientBlazorWebRootPath = Directory.GetDirectories(pathOfContent).FirstOrDefault();
                        if (clientBlazorWebRootPath != null)
                        {
                            env.WebRootPath = clientBlazorWebRootPath;
                        }
                    }
                }
            }
            // ---- APPEND PART.2 END ----

            app.UseClientSideBlazorFiles<Client.Startup>();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapDefaultControllerRoute();
                endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
            });

            // ---- APPEND PART.3 BEGIN ----
            if (clientBlazorWebRootPath != null)
            {
                app.UseStaticFiles(new StaticFileOptions
                {
                    FileProvider = new PhysicalFileProvider(clientBlazorWebRootPath)
                });
            }
            // ---- APPEND PART.3 BEGIN ----
        }
    }
}

I published my sample code and README on the GitHub my repository.

jsakamoto
  • 146
  • 2
  • 6