Tim
Tim

Reputation: 7431

Serve Angular spa pathed off the root

I'm looking to upgrade an existing asp.net 4.5 mvc site which has two angular applications to an asp.netcore 2 mvc site with potentially two spa's. using Aspnet Javascriptservices with the new angular cli template.

Ideally i want to host two spa's one at http://mysite/member and the other at http://mysite/admin

I've started with just one members and i initially used <base href="/members/" /> in the index.html (but then swapped to use the baseHref" property in the.angular-cli.json (giving the same results)) which half works. Locally when debugging the app it serves pages and navigates as expected but in the console i can see zone.js errors.

enter image description here

If i open a new tab and paste in

http://localhost:50930/**members**/sockjs-node/info?t=1518084503138

then i get what looks like a correct response

{"websocket":true,"origins":["*:*"],"cookie_needed":false,"entropy":2082738399}

If i deploy this to Azure app service (production) then navigating to

https://mysite.azurewebsites.net/members then the spa doesn't load at all and it seems that the bundled js has the index.html which is loading it inside. enter image description here

Has anyone managed to use the angular spa template as an application served off the route of an MVC site? I created a ticket on the repo but i suspect its beyond the scope of the project https://github.com/aspnet/JavaScriptServices/issues/1518

Also created a repo to demonstrate what i am trying to achieve https://github.com/tbertenshaw/MultiSpa

Upvotes: 11

Views: 2466

Answers (3)

Chris C
Chris C

Reputation: 11

Tim, did you ever get this working? Im currently trying to get an Aurelia app hosted from a sub-path within a .NET Core MVC app. I am almost there, the main difficulty has been in creating an MVC and Webpack config that works in both production and development.

My sample project is based off the new 2.1 style Angular SPA template which uses the 'UseAngularCliServer' or 'UseProxyToSpaDevelopmentServer' middleware when in dev mode and serves the bundled files from the 'dist' dir when in production mode.

UPDATE: I have published a working repo for this scenario, no sockjs-node errors, also works with HMR, please see the thread here: https://github.com/aspnet/JavaScriptServices/issues/1518

It works in both Development and Production modes.

Upvotes: 0

BinaryPatrick
BinaryPatrick

Reputation: 512

I have used this before in my apps. In a later version I added caching to make it faster, but it always works regardless of where it's deployed. It makes moving files between servers or even just folders very easy, especially when we move between dev, test, and prod servers.

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace Management.Middleware
{
    public class AngularDefaultRouteMiddleware
    {
        private readonly RequestDelegate _next;

        public AngularDefaultRouteMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Path == "/")
            {
                await context.Response.WriteAsync(await GetAngularSpa(context.Request.PathBase));
            }
            else
            {
                await _next(context);
                if (context.Response.StatusCode == 404 && !context.Response.HasStarted)
                {
                    await context.Response.WriteAsync(await GetAngularSpa(context.Request.PathBase));
                }
            }
        }

        private async Task<string> GetAngularSpa(string pathBase)
        {
            string html = await System.IO.File.ReadAllTextAsync($"{Environment.CurrentDirectory}\\wwwroot\\index.html");
            return html.Replace("<base href='/'>", $"<base href='{pathBase}'>");
        }
    }

    public static class AngularDefaultRouteMiddlewareExtensions
    {
        public static IApplicationBuilder UseAngularDefaultRoute(this IApplicationBuilder app)
        {
            return app.UseMiddleware<AngularDefaultRouteMiddleware>();
        }
    }
}

Then you can just call this middleware from Startup.cs with app.UseAngularDefaultRoute();

Upvotes: 0

Andi
Andi

Reputation: 83

You have to configure the webserver to always return the index.html page of your angular pwa.

Default, the server returns an error 404, because for /members there is no file present. But you want angular to handle the routes and therefore you have to always serve the index.html instead of an 404 error page.

This documentation explains this further explicitly for the aspnet JavaScriptServices: https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#routing-helper-mapspafallbackroute

Upvotes: 1

Related Questions