Deploying an Angular SPA under a subfolder
Asked Answered
T

2

6

I have an ASP.net Core 2.1 Web API + Angular 6 web application created using Visual Studio 2017 Angular project template. Both the api and the Angular app are in the same web project. In development it runs inside IIS Express, while in production it uses IIS.

The app itself is working fine, but I'm having a hard time deploying it in a subfolder of an existing Web Site, configured as a new Web Application in IIS.

When I invoke the URLs as:

Angular is able to build the relative paths correctly and the application loads fine.

When I visit the URLs as (note the missing trailing slash):

IIS serves the index.html web page (why? I would expect a 404!), but then Angular can't compute correctly the relative paths to the JS/CSS resources.

I tried using the Microsoft.AspNetCore.Rewrite module to Redirect the ^$ path to /index.html and it works, but it also rewrites the originally working requests, giving an ugly flash in the address bar:

http://localhost:52000/AngularWebApp/ => http://localhost:52000/AngularWebApp/index.html => http://localhost:52000/AngularWebApp/#/dashboard

I tried to redirect to #/ but Chrome stops the requests with ERR_TOO_MANY_REDIRECTS.

Is there any solution to this problem? Please note that I would like a general solution which will allow me to publish the web app either under the subfolder or under its own domain without rebuilding the application.

Relevant snippets:

Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {
...
app.UseRewriter(new RewriteOptions()
   .AddRedirect(@"^$", "/index.html", (int)HttpStatusCode.MovedPermanently));

app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(...);
app.UseSpa(...);
...
}

index.html

<base href="./">

Thanks in advance for your help

Tonie answered 7/11, 2018 at 20:20 Comment(0)
T
4

After examining the code of the Redirect middleware at https://github.com/aspnet/BasicMiddleware/blob/268290a8b56fe44d0e03cbd69d78ba7a17b4f2c1/src/Microsoft.AspNetCore.Rewrite/Internal/RedirectRule.cs#L47 it is clear that the provided Redirect middleware does not allow redirecting the empty path ('') without also redirecting the root ('/') path.

However taking inspiration from that code it was easy to build a custom rewrite rule that does just what I need:

// Load the index.html page when the url is invoked without the ending '/'
// Avoids problems with Angular when loading http://domain/AngularWebApp instead of http://domain/AngularWebApp/
app.UseRewriter(new RewriteOptions().Add(rewriteContext =>
    {
        var request = rewriteContext.HttpContext.Request;
        if (request.Path == PathString.Empty)
        {
            rewriteContext.HttpContext.Response.Redirect(request.PathBase + '/', true);
            rewriteContext.Result = RuleResult.EndResponse;
        }
    }));

Now the Angular web app loads correctly either when deployed on its own root domain or under a subfolder, even if the user invokes it without the trailing slash character.

Tonie answered 9/11, 2018 at 14:52 Comment(2)
Changing the index.html to use <base href="./"> and adding your code to the Startup.cs instantly fixed it for me. The Angular community is happy with hardcoding the URL, which adds unnecessary time to deployment and maintenance.Bunt
Thanks. This helped me.Revenant
E
0

Set <base href="/AngularWebApp" /> and be sure dist angular files are in wwwroot/AngularWebApp folder.

In Startup you can do something like:

app.Use(async (context, next) => {
   await next();
   if (context.Response.StatusCode == 404 &&
      !Path.HasExtension(context.Request.Path.Value) &&
      context.Request.Path.Value.StartsWith("/AngularWebApp/")) {
         context.Request.Path = "/AngularWebApp/index.html";
         await next();
      }
});

Reference : https://medium.com/@levifuller/building-an-angular-application-with-asp-net-core-in-visual-studio-2017-visualized-f4b163830eaa

Esurient answered 7/11, 2018 at 21:6 Comment(1)
Unfortunately this kind of solution requires tailoring of the source for the specific deployment scenario, which is what I'm trying to avoid. See my response for a general solution. Thanks anyway.Tonie

© 2022 - 2024 — McMap. All rights reserved.