Client Side Deep Links with WebpackDevMiddleware 404s - vue.js

I am using the WebpackDevMiddleware for Development builds to serve up a Vue.js application that uses client-side routing. The SPA application is served up from the root url just fine, but if I attempt to use any client-side deep links, I get a 404.
Note running as Production works as expected.
What I want:
http://locahost/ - serve up the vue app.
http://localhost/overlays/chat - serve up the vue app.
http://localhost/api/* - serve up the api routes handled server side.
There is a minimum viable reproduction of the problem in this repository. You can run it using vscode debugging as Development environment where the bug happens. There is also a script /scripts/local-production that will build and run as Production environment, where it works as expected.
Relevant portions of my Startup.cs looks like this:
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// In production, the Vue files will be served
// from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = Configuration["Client"];
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//set up default mvc routing
app.UseMvc(routes =>
{
routes.MapRoute("default", "api/{controller=Home}/{action=Index}/{id?}");
});
//setup spa routing for both dev and prod
if (env.IsDevelopment())
{
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true,
ProjectPath = Path.Combine(env.ContentRootPath, Configuration["ClientProjectPath"]),
ConfigFile = Path.Combine(env.ContentRootPath, Configuration["ClientProjectConfigPath"])
});
}
else
{
app.UseWhen(context => !context.Request.Path.Value.StartsWith("/api"),
builder => {
app.UseSpaStaticFiles();
app.UseSpa(spa => {
spa.Options.DefaultPage = "/index.html";
});
app.UseMvc(routes => {
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Fallback", action = "Index" });
});
});
}
}
}

I was able to get around this using the status code pages middleware to handle all status codes and re-execute using the root path. This will cause the spa app to be served up for all status codes in the 400-599 range which is not quite what I want but gets me working again at least.
//setup spa routing for both dev and prod
if (env.IsDevelopment())
{
//force client side deep links to render the spa on 404s
app.UseStatusCodePagesWithReExecute("/");
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true,
ProjectPath = Path.Combine(env.ContentRootPath, Configuration["ClientProjectPath"]),
ConfigFile = Path.Combine(env.ContentRootPath, Configuration["ClientProjectConfigPath"])
});
}
Hopefully, this will help someone in the future that might be bumping up against this issue.

Related

Blazor Redirection on IIS swagger

I have a .NET 5 blazor WASM (with core server) solution.
I added swagger (nswag) like this:
public class Startup {
public void ConfigureServices(IServiceCollection services) {
services.AddControllersWithViews();
services.AddRazorPages();
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
services.AddSwaggerDocument(); //SWAGGER
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
if(env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
app.UseWebAssemblyDebugging();
}
else {
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
// Register the Swagger generator and the Swagger UI middlewares
app.UseOpenApi();
app.UseSwaggerUi3();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
}
}
When I debug the appliation with IIS-Express and enter the address https://localhost:12234/swagger the swagger UI is displayed correctly.
But after deployment to IIS every address loads the blazor UI with "Sorry there is nothing at this address" instead of the swagger UI.
When I use an old IE (not able to run wasm) I get at least a title from swagger - so swagger is there on the server, but some "magic redirection" forces index.html to be loaded - no matter what I do.
By the way - I can call controller methods and a curl .../swagger/v1/swagger.json also works as expected.
How can I tell the app to accept URLs from the address line without redirection to index.html?
I use PWA and https in my project.
I found the solution.
There is a service-worker.published.js as a "subfile" in wwwroot/service-worker.js.
And there is code like this:
async function onFetch(event) {
let cachedResponse = null;
if (event.request.method === 'GET') {
// For all navigation requests, try to serve index.html from cache
// If you need some URLs to be server-rendered, edit the following check to exclude those URLs
const shouldServeIndexHtml = event.request.mode === 'navigate';
const request = shouldServeIndexHtml ? 'index.html' : event.request;
const cache = await caches.open(cacheName);
cachedResponse = await cache.match(request);
}
return cachedResponse || fetch(event.request);
}
After a little change everthing works fine now:
async function onFetch(event) {
let cachedResponse = null;
if (event.request.method === 'GET') {
// For all navigation requests, try to serve index.html from cache
// If you need some URLs to be server-rendered, edit the following check to exclude those URLs
const shouldServeIndexHtml = event.request.mode === 'navigate' && !event.request.url.includes('/swagger') && !event.request.url.includes('/api/');
const request = shouldServeIndexHtml ? 'index.html' : event.request;
const cache = await caches.open(cacheName);
cachedResponse = await cache.match(request);
}
return cachedResponse || fetch(event.request);
}
Adding two more conditions to shouldServeIndexHtml solved the problem.
const shouldServeIndexHtml = event.request.mode === 'navigate' && !event.request.url.includes('/swagger') && !event.request.url.includes('/api/');

How to connect a script?

If I understand correctly, then when going to the page
https://localhost:44389/Page1/Index, the text should move ...
I follow the link, but nothing happens. ...
I see a page with the text test.
The text does not move.
Code "ScriptTest.js", "ScriptJS.js".
var divxPos = 0;
window.onload = function () {
this.runCode()
}
function runCode() {
var test = document.getElementById("");
test.style.left = divxPos++ + 'px';
setTimeout(() => runCode(), 50);
}
Code \Page1\Index.cshtml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src ="~/ScriptJS.js"> </script>
#*<script src ="~/JS/ScriptTest.js"> </script>*#
</head>
<body>
<p id ="testElement" style="position:absolute">test</p>
</body>
</html>
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace WebAppCore
{
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.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
app.UseDefaultFiles();
app.UseStaticFiles();
}
}
}
Code Page1Controller.cs
using Microsoft.AspNetCore.Mvc;
namespace WebAppCore.Controllers
{
public class Page1Controller : Controller
{
public ActionResult Index()
{
return View();
}
}
}
Project structure
Update-1
I changed the code Startup.cs.
I created the file ScriptJS2.js.
I am starting a debug.
Result: the code does not work.
I changed the code for the file ScriptJS2.js.
I launched debug.
Result: the code does not work.
I see in the file ScriptJS2.js the code from step 2
Questions.
1. How to make the script work?
How to make the new code appear in the ScriptJS2.js file?
Code Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Code ScriptJS2.js (step 2)
var divxPos = 0;
window.onload = function () {
var test = document.getElementById("testElement");
test && runCode(test);
}
function runCode(element) {
element.style.left = divxPos++ + 'px';
setTimeout(() => runCode(element), 50);
}
Code ScriptJS2.js (step 5)
var divxPos = 0;
window.onload = function () {
this.runCode()
}
function runCode() {
var test = document.getElementById("testElement");
test.style.left = divxPos++ + 'px';
setTimeout(() => runCode(), 50);
}
Picture-1
Picture-2
Picture-3
Update-2
Console
1. The connection used to load resources from https://localhost:44389 used TLS 1.0 or TLS 1.1, which are deprecated and will be disabled in the future. Once disabled, users will be prevented from loading these resources. The server should enable TLS 1.2 or later. See https://www.chromestatus.com/feature/5654791610957824 for more information.
2:44389/favicon.ico:1
2. Failed to load resource: the server responded with a status of 404 (Not Found)
3. DevTools failed to parse SourceMap: chrome-extension://gighmmpiobklfepjocnamgkkbiglidom/include.preload.js.map
4. DevTools failed to parse SourceMap: chrome-extension://gighmmpiobklfepjocnamgkkbiglidom/include.postload.js.map
Picture-1
Update 3
I created an ASP.NET Core Visual Studio MVC template application.
The code is working.
The text test moves around the page.
But!
I want to get an application in which there is nothing superfluous.
I want the application to have only:
  - controller, Index.cshtml, ScriptJS2.js
Question.
1. What extra files, folders can I delete from the application that I created in Update-3?
Picture-1
Picture-2
You need to use var test = document.getElementById("testElement") instead.
Besides, the most important thing is that static file middleware is required to be placed before the endpoint routing middleware, change your startup.cs to
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}

ASP.Net core 3.0 (3.1) set PathBase for single page application

I have the following asp.net core spa application configured (react-redux template)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UsePathBase(new PathString("/foo"));
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
I’d like to set pathBase for application, but app.UsePathBase(new PathString("/foo")) just ignored. On 2.2 it perfectly worked. Automatically modified index.html and all static files were moved to relative path. But on 3.0 (3.1) static + generated files are placed on root.
Generated files on .Net Core 2.2
Generated files on .Net Core 3.0
Does anyone have any ideas for solving it? Or may be some examples of Startup.cs with working pathBase?
Usually, app.UsePathBase(new PathString("/foo")); is used because the reverse proxy cuts off some prefix and causes ASP.NET Core app doesn't realize the virtual app path is start with /foo. In your scenario, if you don't have a reverse proxy that rewrite the prefix to empty string, you don't need app.UsePathBase(...).
Instead, if you your spa runs on a subpath, you could setup a middleware that branches the /foo.
Finally, you might want to add a property of homepage in your package.json so that it will generate the <base url="/foo/"/> when publishing. Or as an alternative, you could update the <base url=""> in ClientApp/public/index.html manually.
In short, add a "homepage": "/foo/" in your package.json
"private": true,
"homepage": "/foo/",
"dependencies": {
...
}
And setup a /foo branch to make SPA runs under that path:
string spaPath = "/foo";
app.Map(spaPath,appBuilder =>{
appBuilder.UseSpa(spa =>
{
spa.Options.DefaultPage = spaPath+"/index.html";
spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions{
RequestPath = spaPath,
};
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
});
Don't use app.UsePathBase(new PathString("/foo")) unless you understand you do want to override the path base for all routes.

How to cache static files in ASP.NET Core?

I can't seem to enable caching of static files in ASP.NET Core 2.2. I have the following in my Configure:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
app.UseCors(...);
}
else {
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseSignalR(routes => { routes.MapHub<NotifyHub>("/..."); });
app.UseResponseCompression();
app.UseStaticFiles();
app.UseSpaStaticFiles(new StaticFileOptions() {
OnPrepareResponse = (ctx) => {
ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public, max-age=31557600"; // cache for 1 year
}
});
app.UseMvc();
app.UseSpa(spa => {
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment()) {
spa.UseVueCli(npmScript: "serve", port: 8080);
}
});
}
When I try and Audit the production site on HTTPS using chrome I keep getting "Serve static assets with an efficient cache policy":
In the network tab there is no mention of caching in the headers, when I press F5 it seems everything is served from disk cache. But, how can I be sure my caching setting is working if the audit is showing its not?
This is working in ASP.NET Core 2.2 to 3.1:
I know this is a bit similar to Fredrik's answer but you don't have to type literal strings in order to get the cache control header
app.UseStaticFiles(new StaticFileOptions()
{
HttpsCompression = Microsoft.AspNetCore.Http.Features.HttpsCompressionMode.Compress,
OnPrepareResponse = (context) =>
{
var headers = context.Context.Response.GetTypedHeaders();
headers.CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(30)
};
}
});
I do not know what UseSpaStaticFiles is but you can add cache options in UseStaticFiles. You have missed to set an Expires header.
// Use static files
app.UseStaticFiles(new StaticFileOptions {
OnPrepareResponse = ctx =>
{
// Cache static files for 30 days
ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=2592000");
ctx.Context.Response.Headers.Append("Expires", DateTime.UtcNow.AddDays(30).ToString("R", CultureInfo.InvariantCulture));
}
});
Beware that you also need a way to invalidate cache when you make changes to static files.
I have written a blog post about this: Minify and cache static files in ASP.NET Core

asp core 2.1 angular 6 iis 8 unable to find css js files

I have an asp.net core Angular 6 application that works fine locally in visual studio or if i run this command on the iis server: dotnet .\CTI_Admin.dll
When I try to connect via the IIS webserver all of my css and js files receive this error on an IIS 8 server.
Failed to load resource: the server responded with a status of 404 (Not Found).
Statup.cs
using AspNet.Security.OpenIdConnect.Primitives;
using AutoMapper;
using CTI_Admin.Authorization;
using CTI_Admin.Helpers;
using CTI_Admin.ViewModels;
using DAL;
using DAL.Core;
using DAL.Core.Interfaces;
using DAL.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using OpenIddict.Abstractions;
using Swashbuckle.AspNetCore.Swagger;
using System;
using System.IO;
using AppPermissions = DAL.Core.ApplicationPermissions;
namespace CTI_Admin
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"], b => b.MigrationsAssembly("CTI_Admin"));
options.UseOpenIddict();
});
// add identity
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Configure Identity options and password complexity here
services.Configure<IdentityOptions>(options =>
{
// User settings
options.User.RequireUniqueEmail = true;
// //// Password settings
// //options.Password.RequireDigit = true;
// //options.Password.RequiredLength = 8;
// //options.Password.RequireNonAlphanumeric = false;
// //options.Password.RequireUppercase = true;
// //options.Password.RequireLowercase = false;
// //// Lockout settings
// //options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
// //options.Lockout.MaxFailedAccessAttempts = 10;
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
// Register the OpenIddict services.
services.AddOpenIddict()
.AddCore(options =>
{
options.UseEntityFrameworkCore().UseDbContext<ApplicationDbContext>();
})
.AddServer(options =>
{
options.UseMvc();
options.EnableTokenEndpoint("/connect/token");
options.AllowPasswordFlow();
options.AllowRefreshTokenFlow();
options.AcceptAnonymousClients();
options.DisableHttpsRequirement(); // Note: Comment this out in production
options.RegisterScopes(
OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Phone,
OpenIdConnectConstants.Scopes.Profile,
OpenIdConnectConstants.Scopes.OfflineAccess,
OpenIddictConstants.Scopes.Roles);
// options.UseRollingTokens(); //Uncomment to renew refresh tokens on every refreshToken request
// Note: to use JWT access tokens instead of the default encrypted format, the following lines are required:
// options.UseJsonWebTokens();
})
.AddValidation(); //Only compatible with the default token format. For JWT tokens, use the Microsoft JWT bearer handler.
// Add cors
services.AddCors();
// Add framework services.
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
//Todo: ***Using DataAnnotations for validation until Swashbuckle supports FluentValidation***
//services.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
//.AddJsonOptions(opts =>
//{
// opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
//});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "CTI_Admin API", Version = "v1" });
c.OperationFilter<AuthorizeCheckOperationFilter>();
c.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "password",
TokenUrl = "/connect/token",
Description = "Note: Leave client_id and client_secret blank"
});
});
services.AddAuthorization(options =>
{
options.AddPolicy(Authorization.Policies.ViewAllUsersPolicy, policy => policy.RequireClaim(CustomClaimTypes.Permission, AppPermissions.ViewUsers));
options.AddPolicy(Authorization.Policies.ManageAllUsersPolicy, policy => policy.RequireClaim(CustomClaimTypes.Permission, AppPermissions.ManageUsers));
options.AddPolicy(Authorization.Policies.ViewAllRolesPolicy, policy => policy.RequireClaim(CustomClaimTypes.Permission, AppPermissions.ViewRoles));
options.AddPolicy(Authorization.Policies.ViewRoleByRoleNamePolicy, policy => policy.Requirements.Add(new ViewRoleAuthorizationRequirement()));
options.AddPolicy(Authorization.Policies.ManageAllRolesPolicy, policy => policy.RequireClaim(CustomClaimTypes.Permission, AppPermissions.ManageRoles));
options.AddPolicy(Authorization.Policies.AssignAllowedRolesPolicy, policy => policy.Requirements.Add(new AssignRolesAuthorizationRequirement()));
});
Mapper.Initialize(cfg =>
{
cfg.AddProfile<AutoMapperProfile>();
});
// Configurations
services.Configure<SmtpConfig>(Configuration.GetSection("SmtpConfig"));
// Business Services
services.AddScoped<IEmailSender, EmailSender>();
// Repositories
services.AddScoped<IUnitOfWork, HttpUnitOfWork>();
services.AddScoped<IAccountManager, AccountManager>();
// Auth Handlers
services.AddSingleton<IAuthorizationHandler, ViewUserAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, ManageUserAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, ViewRoleAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, AssignRolesAuthorizationHandler>();
// DB Creation and Seeding
services.AddTransient<IDatabaseInitializer, DatabaseInitializer>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug(LogLevel.Warning);
loggerFactory.AddFile(Configuration.GetSection("Logging"));
Utilities.ConfigureLogger(loggerFactory);
EmailTemplates.Initialize(env);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
//Configure Cors
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseAuthentication();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.DocumentTitle = "Swagger UI - Quick Application";
c.SwaggerEndpoint("/swagger/v1/swagger.json", "CTI_Admin API V1");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
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");
spa.Options.StartupTimeout = TimeSpan.FromSeconds(60); // Increase the timeout if angular app is taking longer to startup
//spa.UseProxyToSpaDevelopmentServer("http://localhost:4200"); // Use this instead to use the angular cli server
}
});
}
}
}
You need to follow the steps:
Bundle your Angular application using ng build --prod
Ensure your IIS Hosting bundle is installed https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/?view=aspnetcore-2.1
Finally publish the website in a folder and the deploy to hosting server
For detailed instructions: https://code-maze.com/net-core-web-development-part16/#windowsserver
The way i resolved this was by adding --base-href to
ng build --extract-css --base-href /CTI_Admin/ClientApp/dist
However, my JS files are not loading even though i get a status code 200. The new error I receive is
scripts.c070f8cfd43553c493e4.js:1 Uncaught SyntaxError: Unexpected token <
Its basically returning the index.html code.
UPDATE:
This is the proper build script when you application is setup under site in IIS
ng build --prod --extract-css --base-href /CTI_Admin/