Search code examples
angularasp.net-coresingle-page-applicationangular7asp-net-core-spa-services

Angular 7 with .net core 2.1 - Launching for specific page


I have a .NET Core 2.1 website with Angular 7 sitting inside /ClientApp.

I am using Microsoft.AspNetCore.SpaServices.Extensions

If I go to a page e.g. localhost:5001/pagedoesnotexist/index then for some reason the Angular app kicks in.

Question - how can I change the configuration so that ASP.NET Core does not try to redirect to the Angular app if I go to a page that does not exist?
I want it to simply return a standard 404 upon incorrect web request. I want a specific page e.g. /admin/dashboard to serve the Angular app only

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
...
services.AddSpaStaticFiles(configuration =>
{
   configuration.RootPath = "ClientApp/dist";
});
...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)

{
...
app.UseSpaStaticFiles();
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501

spa.Options.SourcePath = "ClientApp";


if (env.IsDevelopment())
{
  spa.UseAngularCliServer(npmScript: "start");
}
});
...
}

While this is my first Angular app it seems to be working fine so far and the issue is inside of a development environment.

Thanks.

UPDATE 1

To clarify a little further:

PASS: I want localhost:5001/ = 404

PASS: I want localhost:5001/angtastic/ to serve the app

FAIL: I get repeated info?t=[random numbers] from zone.js (socksjs-node/info) (it works without the below code targetting the sub folder so perhaps I am doing something wrong). My guess is that the sockets or something to do with webpack (under the hood of angular cli) is having trouble with handling the application within this sub-folder.

I have a partial solution that comes with a problem:

app.Map(new PathString("/angtastic"), angtastic =>
            {
                angtastic.UseSpa(spa =>
                {
                    spa.Options.SourcePath = "ClientApp";

                    // To learn more about options for serving an Angular SPA from ASP.NET Core,
                    // see https://go.microsoft.com/fwlink/?linkid=864501

                    if (env.IsDevelopment())
                    {
                        spa.UseAngularCliServer(npmScript: "start --app=angtastic --base-href=/angtastic/ --serve-path=/angtastic/ ");
                    }
                });
             });

Also, changed the index.html to have a BaseURL of .

Here is my Angular.json

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "angtastic": {
      "root": "src",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {
        "@schematics/angular:component": {
          "styleext": "scss"
        }
      },
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/angtastic",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "angtastic:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "angtastic:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "angtastic:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.spec.json",
            "karmaConfig": "src/karma.conf.js",
            "styles": [
              "clientapp/src/styles.scss"
            ],
            "scripts": [],
            "assets": [
              "clientapp/src/favicon.ico",
              "clientapp/src/assets"
            ]
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "src/tsconfig.app.json",
              "src/tsconfig.spec.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        }
      }
    },
    "angtastic-e2e": {
      "root": "e2e/",
      "projectType": "application",
      "prefix": "",
      "architect": {
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "angtastic:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "angtastic:serve:production"
            }
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": "e2e/tsconfig.e2e.json",
            "exclude": [
              "**/node_modules/**"
            ]
          }
        }
      }
    }
  },
  "defaultProject": "angtastic"
}

Solution

  • How to achieve subfolder/subdirectory specific angular app on a .net core application with an angular 7 app contained within the ClientApp directory of a .net core 2.1 application (run using the "dotnet watch run" command) where "angtastic" is the name of your project and sub-folder:

    1. Add the above app.Map(newPathString("/angtastic"), angtastic => ... then use spa.UseAngularCliServer(npmScript: "start") or the UseProxyToSpaDevelopmentServer as appropriate for your project.
    2. In startup don't add any parameters to the line spa.UseAngularCliServer(npmScript: "start");
    3. Package.json inside my ClientApp folder is simply "start": "ng serve" as the parameters have been placed into the angular.json configuration file for the Angular-CLI
    4. How I solved the info?t=XXX issue above, In the "serve" section of angular.json add the following:

      "browserTarget": "angtastic:build",

      "baseHref": "/angtastic/",

      "publicHost": "",

      "servePath" : "/"

    You could probably pass these as options if you prefer in package.json against the start command but I prefer to keep things located in angular.json but remember that servePath would be --serve-path="/"

    1. Check that your angular.json has "root" : "src" and "sourceRoot": "src"
    2. Run the project from the root (above ClientApp) with "dotnet watch run" and open a browser to a secure version of localhost:5001/angtastic
    3. Confirm you have been successful by opening dev tools. If you don't see a stream of 404s from zonejs then things are looking good. I see only one info?t=xxxx (200 code) along with a xhr_streaming?t=XXXX (also 200).

    The publicHost is the real essence of this answer as it instructs (I think) the webpack live reloading to the correct port. This answer may work for other platforms but keep in mind you may need to replace the port with the one your app is using e.g. 3000/3001.

    Hope this helps someone! First app here and so far it's a struggle.

    Dan.