Reputation: 339
I am having a problem deploying my Node.js to the IIS, in my web.config
:
> <handlers>
> <add name="iisnode-ng-universal" path="dist/server.js" verb="*" modules="iisnode" />
> </handlers>
<rule name="ng-universal" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{HTTP_HOST}" pattern="test" />
</conditions>
<action type="Rewrite" url="dist/server.js" />
</rule>
I am getting this error:
This how my web files tree looks:
I tried to give full permission to IIS_IUSRS
and IUSR
but it does not seem to work. Like this post suggested:
iisnode encountered an error when processing the request
Upvotes: 9
Views: 13472
Reputation: 14892
If you want to run your angular application with server-side rendering:
1-Create a new project:
ng new anguniversal
2-Enable Server-side rendering (SSR)
ng add @nguniversal/express-engine
npm run build:ssr
Now you can see the dist folder in your project folder.
Create and open the Web.config
file(in the angular-universal folder) and add the code to it.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="main.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="DynamicContent">
<match url="/*" />
<action type="Rewrite" url="main.js"/>
</rule>
<rule name="StaticContent" stopProcessing="true">
<match url="([\S]+[.](jpg|jpeg|gif|css|png|js|ts|cscc|less|ico|html|map|svg))" />
<action type="None" />
</rule>
</rules>
</rewrite>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" />
<remove fileExtension=".svg" />
<remove fileExtension=".eot" />
<remove fileExtension=".ttf" />
<remove fileExtension=".woff" />
<remove fileExtension=".woff2" />
<remove fileExtension=".otf" />
<mimeMap fileExtension=".ttf" mimeType="application/octet-stream" />
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
<mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
<mimeMap fileExtension=".woff" mimeType="application/x-woff" />
<mimeMap fileExtension=".woff2" mimeType="application/x-woff" />
<mimeMap fileExtension=".otf" mimeType="application/otf" />
</staticContent>
</system.webServer>
</configuration>
Install IIS Node & URL Rewrite on the server.
In the IIS, go to Application Pools . for the Website you created (angular-universal). Then change the .NET CLR Version to No Managed Code.
Done, you can check the result.
Upvotes: 6
Reputation: 313
After upgrading my Angular 8 project to 9, and by that I mean that I upgraded practically everything to the latest, several things happened.
1.- webpack.config.js
file was renamed as .bak so it seems that using webpack is not the way anymore, and indeed if I try to re-activate the webpack build nothing happens, it creates the build but the script doesn't run. Running with node server.js
goes in and out without saying anything at all.
2.- Now the dist folder has no .js file in the root and the browser & server builds' folders are there like before, but even editing the web.config
file to instruct the iisnode to run server/main.js
instead of server.js
throws 500 all the time no matters what I do.
3.- The only thing that works at first is runnign locally
npm run dev:ssr
Which means that the reference to the view in the browser build is there but it runs like it should run from the root asking for dist/browser.
I have modified the build path and server build path in angular.json
to create the main.js file in the root of ./dist folder.
angular.json (outputPath are modified)
architect: {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/browser",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "tsconfig.app.json",
...
},
...
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist",
"main": "server.ts",
"tsConfig": "tsconfig.server.json"
},
...
},
...
}
Well, here comes the problem. The browser build generates ./dist/browser without problems and the server build indeed put the main.js file directly in ./dist but somehow it decided to delete the ./dist/browser folder and when running node main.js (web.config
pointing to main.js
) it can't find ./browser/index.html and of course not because the server build process managed to delete the browser folder.
The hacky solution, run a third build (the browser build again)
I hope this can be solved but to be honest this doesn't bother me at all, a computer runs the build, not me, but you know, it could be better and quicker if the server build proces doesn't delete de browser build folder.
Now it is deliciously deployed to Azure App Service directly from AppVeyor every time I deploy the selected build, no problems what soever.
package.json (Note the extra production build after the server build)
{
...
"scripts": {
...
"dev:ssr": "ng run Angular-Universal:serve-ssr",
"serve:ssr": "node dist/main.js",
"build:ssr": "npm run build:universal && ng run Angular-Universal:build:production",
"start:ssr": "npm run build:ssr && npm run serve:ssr",
"build:universal": "ng run Angular-Universal:build:production && ng run Angular-Universal:server:production",
"prerender": "ng run Angular-Universal:prerender"
},
...
}
Note that build:ssr
script is doing both builds and then the browser build again.
Also note that serve:ssr
script was modified to run dist/main.js
Finally the server.ts file had to be modified to request the view from ./browser/index.html or ./dist/browser/index.html depending on the folder structure of the target system. When deployed to Azure App Service main.js requests browser/index.html but if the project is served with npm run dev:ssr
or node main.js
in my local environment with runs on macOS it requests dist/browser/index.html. Anyways a simple if can jump this obstacle.
server.ts (Changes on the app function)
// The Express app is exported so that it can be used by serverless Functions.
export function app() {
const server = express();
let distFolder: string;
const path1 = join(process.cwd(), 'browser'); // Running from the deployed version (production)
const path2 = join(process.cwd(), 'dist/browser'); // Running from the source (macOS local development)
if (fs.existsSync(path1)) {
distFolder = path1;
} else if (fs.existsSync(path2)) {
distFolder = path2;
} else {
return null;
}
console.log('distFolder:', distFolder);
const indexHtml = fs.existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
Just in case you were wondering what about copying the web.config
file to the root of the dist
folder, well, since I am using AppVeyor for CI/CD I have modified the artifact creation command to include web.config
(from the repo source files directly into the .7z file)
And why not to include my web.config
?
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<httpRuntime enableVersionHeader="false" />
</system.web>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Strict-Transport-Security" value="max-age=31536000"/>
<add name="X-Content-Type-Options" value="nosniff" />
<add name="X-Frame-Options" value="DENY" />
<add name="X-XSS-Protection" value="1; mode=block" />
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>
<webSocket enabled="false" />
<handlers>
<!-- Indicates that the main.js file is a node.js site to be handled by the iisnode module -->
<add name="iisnode" path="main.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="HTTP to HTTPS redirect" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="off" ignoreCase="true" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
</rule>
<!-- Do not interfere with requests for node-inspector debugging -->
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^main.js\/debug[\/]?" />
</rule>
<!-- All other URLs are mapped to the node.js site entry point -->
<rule name="DynamicContent">
<match url="^(?!.*login).*$"></match>
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
</conditions>
<action type="Rewrite" url="main.js"/>
</rule>
</rules>
<outboundRules>
<rule name="Add Strict-Transport-Security when HTTPS" enabled="true">
<match serverVariable="RESPONSE_Strict_Transport_Security" pattern=".*" />
<conditions>
<add input="{HTTPS}" pattern="on" ignoreCase="true" />
</conditions>
<action type="Rewrite" value="max-age=31536000" />
</rule>
</outboundRules>
</rewrite>
<!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<!-- Make sure error responses are left untouched -->
<httpErrors existingResponse="PassThrough" />
<!-- Restart the server if any of these files change -->
<iisnode watchedFiles="web.config;*.js;browser/*.*" />
</system.webServer>
</configuration>
I hope this could help others in my situation or at least useful for debate. No one likes to be scratching the head trying to make a successful deployment to an Azure App Service after upgrading to version 9. Please let me know if something can be improved, perhaps a build parameter to avoid browser build folder removal or whatever. Cheers.
Upvotes: 3
Reputation: 1487
The following flow worked for me after 2 days of trial and error (Angular 8)
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"outDir": "../out-tsc/app-server",
"module": "commonjs"
},
"files": [
"src/main.server.ts" -- old
"main.server.ts" -- use this
],
"angularCompilerOptions": {
"entryModule": "./app/app.server.module#AppServerModule"
}
}
And compiled using npm run build:ssr && npm run serve:ssr
Follow the answer 1 of this Post and Install iisnode from https://github.com/tjanczuk/iisnode (refer this guy's tutorial if required https://www.youtube.com/watch?v=JUYCDnqR8p0)
website_root:.
│ server.js
│ web.config
│
├───dist
│ └───browser
│ 3rdpartylicenses.txt
│ favicon.ico
│ index.html
│ main-es2015.da6c4688eda738c0ae37.js
│ main-es5.da6c4688eda738c0ae37.js
│ polyfills-es2015.0ef207fb7b4761464817.js
│ polyfills-es5.2b9ce34c123ac007a8ba.js
│ runtime-es2015.e8a2810b3b08d6a1b6aa.js
│ runtime-es5.e8a2810b3b08d6a1b6aa.js
│ styles.09e2c710755c8867a460.css
│
└───server
main.js
Deploy to Azure (inspired from this Link's answer by **r3plica)**
Only difference is an additional 'copy files' task moving the server folder to root dir
Note
If things do not work always use node to point at server.js this
node <path to server.js>
This tells you the most basic things w you have gotten wrong
Upvotes: 0
Reputation: 301
can you try the below code.. it works for me
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="DynamicContent">
<match url="/*" />
<action type="Rewrite" url="server.js"/>
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Your server.js can be outside the dist folder.
Upvotes: 2