I recently had cause to put an Angular application a IIS sub application under an existing .Net website so effectively they share the same host url. It took me longer than it should have done – I made heavy weather of it. Some issues were just fiddly and took some working out. Some issues I had were the result of my own sub-optimal performance in this technical area (stupidity). It was Saturday afternoon so I’m allowed sub -optimal performance in my own time.
Tutorial
There is already a good tutorial here. So I’m not proposing to go through it step by step, Instead I’ll quickly outline the basic steps then point out where I went wrong. I suffer so you don’t have to.
Motivation
Just briefly – my motivation for doing this was to have Angular running under the same address as the API it was calling.
So
http://demo-api:4200/
Is called by an angular app under a virtual directory at
http://demo-api:4200/Angular
This is to avoid Cross Origin Scripting Issues in my own environment. I know there are other ways to do this – but they either
- Never worked
- Were too invasive to implement on the API side (for a spare time demo project)
- Did work then frustrating stopped working
So I bit the bullet and moved Angular under the API project where it is always going to work for me i.e. they use the same host url so are no longer cross origin.
Create the Sub Application
So very briefly – ceate a sub application under main application to get this configuration
Point virtual directory to Angular app and browse. Boom – It doesn’t work!
Error 1: Using a .Net App pool
First issue is this yellow screen of death
The exact error will vary but essentially the angular application complains that it is missing assemblies, handlers, modules etc… It wants to be a .Net application. It doesn’t need to be.
When setting up the new application the default will be a .Net integrated application pool
To stop it behaving like a .Net app – create a new app pool with .Net CLR version of ‘No Managed Code’
Assign this to the new angular application in IIS and the site is no longer a .Net wannbe. The YSOD is gone.
Error 2: Pointing to the wrong folder
No yellow screen of death, instead the application is entirely blank. This is where I was being particularly sub-optimal. So I pointed it to my angular application at
{MyAngularApplication}\src
This is never going to work. Angular builds into this directory by default
{MyAngularApplication}\dist
Point the sub application here. Why I thought this would magically work I’ve no idea. Although IIS had stopped behaving like a .Net site, I clearly carried on thinking it was one and pointed to the root folder as I would a .Net site. 15 minutes of my life working this out that I am never going to get back. Oh well.
Error 3: Correcting Angular script references
So run the app again and sadly it is still blank. This time if you look at chrome developer toolbar or similar you will see a lot of 404 errors
What’s going on here is that Angular is still behaving as if it is loading all scripts from the root website. To correct this
- Use base href element in the root index page i.e.
<head> <meta charset="utf-8"> <title>Title</title> <base href="/Angular"> </head>
1. Change the ng build command from
ng build
to
ng build --base-href /Angular --deploy-url /Angular/
This will point Angular to the right direction i.e. looking at the dist folder the script references have changed from
<script type="text/javascript" src="/main.bundle.js"></script>
to
<script type="text/javascript" src="/Angular/main.bundle.js"></script>
Which is correct for our sub application.
As a side note – I always forget these kind of command lines. To avoid the need for additional brain friction use the npm start command to run the ng build command with the extra parameters. So go to packages.json in the root directory of your angular app and change the npm start node under scripts to the build command i.e.
"scripts": { "ng": "ng", "start": "ng build --base-href /Angular --deploy-url /Angular/", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" },
Now to build the app use
npm start
And the build command runs with the correct params.
Error 4: Refreshing page causes 404
If you are using routing you will find that refreshing a page when on a route (i.e. demo-app/Angular/page1) will give a 404 error, To resolve this one
1. Install Url rewrite
2. Include a web.config page into your src folder with these url rewrite rules
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <directoryBrowse enabled="true" /> <rewrite> <rules> <rule name="AngularJS Routes" stopProcessing="true"> <match url=".*" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" /> </conditions> <action type="Rewrite" url="/Angular" /> </rule> </rules> </rewrite> </system.webServer> </configuration>
3. Include the web.config file in project assets. Go to .angular-cli.json and add web.config to the asset node like so
"apps": [ { "assets": [ "assets", "favicon.ico", "web.config" ], "index": "index.html", "main": "main.ts", .. rest of app settings }];
If you omit this step then the web.config won’t be copied over to the dist folder on build. If you add it to the dist folder manually then it will be deleted on build so you won’t be any further forward.
Once all these steps are done, refreshing on a route this will reissue the request up to the base url which will resolve correctly.
Finishing up
Now the angular app runs nicely under a sub application in IIS and my Cors problem is no longer an issue. There are a couple of disadvantages to running it this way
- Hot reloading of the application on rebuild doesn’t work
- Related to this – the application sometimes caches and a hard refresh of the browser (ctrl, F5) is sometimes needed to kick it back to life.
- When refreshing a page it will redirect back to the root so you lose whatever route you are on so
http://demo-app/angular/page1
Refreshed becomes
http://demo-app/angular
Which may or may not be what you want
These are probably resolvable but when I got my development environment to this point, I declared loudly to all with ears to hear – Job done!
Useful Links
https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/creating-rewrite-rules-for-the-url-rewrite-module
Url Rewriting module in IIS – official microsoft page
https://blogs.msdn.microsoft.com/webdev/2006/06/30/part-1-of-3-creating-sub-projects-in-iis-with-web-application-projects/
Setting in sub applications in IIS – detailed tutorial
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
Wiki page about CORS which was causing me all the problem in the first place
Thanks for the article! I’m fighting a similar battle and was looking around to see if I could get Hot Reloading to work with this sort of setup. Wondering if you have thought any more about it.
Thanks, this was very helpful. Followed the steps and saved myself much aggrevation!
Thank you so much mate, exactly what I needed. Was a massive help.
Hi, This was very helpful thanks. But how does this work with Logo images. I have deployed 2 applications for 2 testing teams, test1 and test2. The banner logo for example, is always blank because it wants to load the base URL, How do to change it so it uses base-url/test1 and base-url/test2?
Thanks in advance.
Hi, in step 4 where you mention about webconfig, where should this file be made?
in the angular project or in the parent project?
There are away our team does config is host api under angular app.
domain/api
But angular app has to configure to CLR 4.0 integrated to run API.
And it still works, can you explain the pros and cons of 2 approach:
– Host Angular under api
– Host api under Angular app
Hi,
I came across your article and it was helpful.
I am facing the issue as mentioned in “Finishing Up” Step 3 i.e. on refresh the path redirects to the virtual directory. Could you please 0how to resolve this for the child routes. E.g. /user/profile sticks to this path url only ?