Angular app publish with IISNode on Smarterasp.net - node.js

My online host service smarterasp.net require some changes made to publish my angular compiled app (Angular v. 11.1.1) as a node js app. On their quickstart guide(https://www.smarterasp.net/support/kb/a1970/quick-start-node_js.aspx?KBSearchID=818443) they say: "We are Hosting node.js applications in IIS on Windows via IISNode, so you need to update listening port to use "process.env.PORT" in your code" and there's code given as an example:
HelloWorld Sample:
hello.js
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('Hello, world!');
}).listen(process.env.PORT);
web.config
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="hello.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="mysite">
<match url="/*" />
<action type="Rewrite" url="hello.js" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
"You do not have to run any command line with npm or node.exe to host nodejs with us." They say.
Now, the problem is: My compiled Angular app consists on many .js files, but the hello.js example given only has a string return res.end('Hello, world!');. My questions are:
What's the correct code to use in my case (instead of the hello.js) and where should I put this code? I already try to put it inside my index.html file, inside the header tags, inside a script, but the web browser console shows me the error (Uncaught ReferenceError: require is not defined).
Once I uploaded my test files, my website shows a black screen.
This is my attempt of script inside my index.html file:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>DummyTitle</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.write(req.url);
res.end();
}).listen(process.env.PORT);
</script>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght#300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body style="background-color: black;">
<app-root></app-root>
</body>
</html>
And this is my web.config file (following the angular guide about IIS (https://angular.io/guide/deployment)):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Angular Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/mainwebpage/index.html" />
</rule>
</rules>
</rewrite>
<handlers>
<add name="iisnode" path="*.js" verb="*" modules="iisnode" />
</handlers>
</system.webServer>
</configuration>
Has somebody found the same issue before? Help please!!

After a few days I figure out the quick start guide provided was wrong (https://www.smarterasp.net/support/kb/a1970/quick-start-node_js.aspx?KBSearchID=818443) I had absolutely no necesity to redirect my requests/responses to a server as suggested on the hello.js file. Probably because my angular app doesn't use server side rendering (SSR, Angular universal).
The solution was simply paste the compiled angular files at the designated folder and add a web.config. To my surprise I discover that the example of web.config file given on the official angular site (https://angular.io/guide/deployment#fallback-configuration-examples) was also wrong. Here's my web.config file, hoping it helps other poor souls looking for an inexistence documentation:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Angular Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="./index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>

If you have server.js file put the code "process.env.PORT" inside the server.js file.
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="server.js" verb="*" modules="iisnode" />
</handlers>
</system.webServer>
</configuration>
This handler registration allows the same web site to contain other *.js files (e.g. jQuery libraries) that IIS will continue serving as static files.
Reference link:
use node.js express on iis with iisnode

Related

URL Rewrite not working - React SPA on Azure Web App (node)

I'm running a create-react-app build on an Azure Web App (node LTS). The app uses React Routes, and runs great from the index screen and as long as you are navigating the route links through the app. However, if you refresh a page, or navigate directly to a non-root route, you get 404. It seems to me like my web.config rewrite (of which I've tried many variations) is not being honored. I'm sure I'm just missing something obvious. Any suggestions?
index.js file
var express = require('express');
var server = express();
var options = {
index: 'index.html'
};
server.use('/', express.static('/home/site/wwwroot', options));
server.listen(process.env.PORT);
web.config (edit with updated version)
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<webSocket enabled="false" />
<handlers>
<add name="iisnode" path="index.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="React Routes">
<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="/index.html" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>
File Structure (/site/wwwroot/)
index.html
web.config
static/ (css, js)

Host NextJs to IIS

Good Day!
My colleague has a website node.js (next.js), his website works fine when we build and start thru console (npm run build and npm start).
We have hosted it in a Azure VM (Windows Server 2016 IIS, iisnode and urlrewrite installed), we created a pipeline and we are able to get the artifacts (".next" folder when we run the build) and deploy it to IIS however we still need a manual interaction to place the web.config. Below is the web.config
<!-- indicates that the hello.js file is a node.js application
to be handled by the iisnode module -->
<handlers>
<add name="iisnode" path="service-worker.js" verb="*" modules="iisnode" />
</handlers>
<!-- use URL rewriting to redirect the entire branch of the URL namespace
to hello.js node.js application; for example, the following URLs will
all be handled by hello.js:
http://localhost/node/express/myapp/foo
http://localhost/node/express/myapp/bar
-->
<rewrite>
<rules>
<rule name="AMS" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
But when we visit the website, it throws an error of 403 that need to supply the default page. (I'm lost here and not able to run his website thru IIS)
Note: His other website works fine (because it has a service-worker.js).
Anyone experience deploying the Next.JS to IIS? Thanks in Advance.
In the /public folder, create the following web.config to accept requests from /a/b/c and rewrite them to / where our NextJs code lives.
<?xml version="1.0"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="NextJs Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Just doing this should allow you to reload a page on a route like /products, but NextJs will render /, ie, the index page, because that's what our rewrite rule told it to deliver.
So, we need to create a body Component that takes a NextRouter as a prop then compare the window's url to the router's url. If they don't match, we need to change our client side route with router.push().
I'm using TypeScript so my body.tsx is
import * as React from 'react';
import { NextRouter } from 'next/router';
export default class Body extends React.Component<{router : NextRouter}>
{
componentDidMount()
{
if (window.location.pathname == this.props.router.pathname) return;
this.props.router.push(global.window.location.pathname);
}
render = () => this.props.children;
}
Then in _app.tsx, we simply need to wrap the main Component in our Body Component.
import { useRouter } from 'next/router'
import Head from 'next/head';
import Body from '../src/components/elements/body';
function MyApp({ Component, pageProps }) {
const router = useRouter();
return (
<>
<Head>
<title>NextJs on IIS</title>
</Head>
<Body router={router}>
<Component {...pageProps} />
</Body>
</>
)
}
export default MyApp
Run npm run build, and copy the /out folder to your IIS server.

My iisnode setup wont serve the static files

What is wrong with my setup? have in mind I use express.
web.config file :
<handlers>
<add name="iisnode" path="app.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="myapp">
<match url="/*" />
<action type="Rewrite" url="app.js" />
</rule>
</rules>
</rewrite>
and the app.js is as below :
var express = require('express');
var app = express();
var server = http.createServer(app).listen(process.env.port);
app.use(express.static(__dirname + '/pro/Public'));
app.get('/pro',function(req,res) {
res.sendfile(__dirname + '/yoyo.html');
res.end;
});
and the html has the classic layout like this:
<link rel="stylesheet" type="text/css" href="cssKtelVolouOsm.css">
<script type="text/javascript" src="javascriptKtelVolouOsm.js"></script>
and I get the following errors:
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost/cssKtelVolouOsm.css
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost/javascriptKtelVolouOsm.js
If those files actually exist under ./pro/Public then this is most likely caused by the use of relative URLs in your html. Try this instead.
<link rel="stylesheet" type="text/css" href="/cssKtelVolouOsm.css">
<script type="text/javascript" src="/javascriptKtelVolouOsm.js"></script>

IIS returns 500 when Node app returns a 4xx

I have also asked this on the iisnode github project
I am using IISNode via a Windows Azure Website.
If my Node app returns a 2xx status code (200, 201, etc), then all is well and works as expected.
if my Node app returns a 4xx status code, for example:
response.send(404, "not found") (I am using Restify) then I get a 500 sent to the client and the body is simply "The page cannot be displayed because an internal server error has occurred."
If I do azure site log tail <sitename>, then when the 500 gets sent to the client, the log contains HTML of a 404.
...
<body>
<div id="content">
<div class="content-container">
<h3>HTTP Error 404.0 - Not Found</h3>
<h4>The resource you are looking for has been removed, had its name changed,
or is temporarily unavailable.</h4>
</div>
...
I really just want IISNode/IIS to take my 404 and send it on down to the client. This is also true of 401s, 409s, etc. They all cause a 500 to get sent instead.
I have tried <customErrors mode="off" /> and <httpErrors existingResponse="passThrough" /> in my web.config to no avail. Here is my web.config now:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<customErrors mode="off" />
</system.web>
<system.webServer>
<httpErrors existingResponse="PassThrough" />
<staticContent>
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
<mimeMap fileExtension=".ico" mimeType="image/x-icon" />
</staticContent>
<handlers>
<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="server.js"/>
<match url="/*" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
For what it's worth, I can't get IISnode to do anything but return 200s from my node app. Trying to get IIS to serve my static files, enable node-inspector, or really any of the more interesting IISNode features cause a 500 for me. No idea why, and would love to get past it!
Similar StackOverflow Questions
this question is pretty much exactly my problem. Except that <httpErrors existingResponse="PassThrough" /> does not work for me. I feel like I have something more fundamentally wrong with my IISNode setup, so if anyone has any ideas on things to try I'm all ears.
for some reason the following section in your config is causing this issue:
<staticContent>
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
<mimeMap fileExtension=".ico" mimeType="image/x-icon" />
</staticContent>
As a workaround, remove this section if possible and retry. I will investigate why this section is causing this issue.
As Ranjith pointed out, the staticContent section is the cause of the conflict. I just found you can limit those entries inside of location entries, so you can get all of IISNode as desired and still serve up static content. My web.config now looks like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<customErrors mode="off" />
</system.web>
<system.webServer>
<handlers>
<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="Redirect to https" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="off" ignoreCase="true" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Found" appendQueryString="true" />
</rule>
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>
</rule>
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="server.js"/>
</rule>
</rules>
</rewrite>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
<location path="public/fonts">
<system.webServer>
<staticContent>
<mimeMap fileExtension=".woff" mimeType="application/font-woff" />
</staticContent>
</system.webServer>
</location>
</configuration>
The added location entry at the end works around the 500 and static content conflicts.
I'm getting the exact same...500 error page returned from Azure/IIS. I used the web.config that Azure automatically generated as the basis, then added <customErrors mode="off" /> and <httpErrors existingResponse="PassThrough" />. I also removed the default staticContent node. Nothing is working.
Below is the web.config that I'm uploading to Azure. What in here is causing IIS/iisnode to return a 500 error page instead of the simple text error message I have node/express returning to the client?
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<customErrors mode="off" />
</system.web>
<system.webServer>
<httpErrors existingResponse="PassThrough" />
<webSocket enabled="false" />
<handlers>
<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^server.js\/debug[\/]?" />
</rule>
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="server.js"/>
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
This works for me:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="app.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="DynamicContent">
<match url="/*" />
<action type="Rewrite" url="app.js"/>
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
I had the same issue (IIS Node on Win Server 2012R2). So I did not get the target content in the body.
At the beginning I had response:
code: 400
body: Bad Request
Then I added <httpErrors existingResponse="PassThrough" /> and I got response:
code: 500
body: The page cannot be displayed because an internal server error has occurred
Then I have changed IIS Feature Delegation for "Error Pages" to "Read/Write" and this resolved my issue. Finally I had proper response:
code: 400
body: { "error": {...} }
I hope this helps.

IISNode and Express 3 yields http 403.13 error

I am setting up IISNode on IIS 7 locally on my Win7 box. I followed the instructions on the site and the samples are working fine.
I created a new website and AppPool in IIS Manager to run a brand new shell of an Express site. I've added the web.config to tie the iisnode module to my starting .js file.
When I browse to the default route (/) I get an Http 403.14 error (Server is configured to not list the contents of the directory).
I have attempted to remap the IISNode sample directory to where my Express app is and the same error occurs.
If I attempt to go to a non-existing route, I DO get Connect's 404 error message of Cannot VERB ROUTE.
I feel like I"m missing something simple and (hopefully obvious).
Has anyone ran into this and can provide me some insight? Looking online has provided little light in terms of even when I can check.
I figured out what issue I was having. In my web.config, I had the default IISNode section and the handler section to map the iisnode module to my app.js file.
However, when using Express, every route has to go through that file. So by adding the rewrite section as below it resolved my issue.
<rewrite>
<rules>
<rule name="Catch All">
<match url="/*" />
<action type="Rewrite" url="app.js" />
</rule>
</rules>
</rewrite>
For a more advanced URL rewriting configuration check out the web.config template at http://tomasz.janczuk.org/2012/05/yaml-configuration-support-in-iisnode.html. This template allows you to redirect requests for static content to the IIS static file handler, as well as retain access to iisnode logs over HTTP.
<?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="LogFile" patternSyntax="ECMAScript" stopProcessing="true">
<match url="iisnode"/>
</rule>
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^server.js\/debug[\/]?" />
</rule>
<rule name="StaticContent">
<action type="Rewrite" url="public{{REQUEST_URI}}"/>
</rule>
<rule name="DynamicContent">
<conditions>
<add input="{{REQUEST_FILENAME}}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="server.js"/>
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
The web.config above has the following effect:
It specifies server.js as the entry point to your node.js application.
It redirects all requests for URLs that map to physical files in the “public” subdirectory to an IIS static file handler. Using IIS static file handler has a large performance benefit compared to serving static content from within a node.js application. The handler leverages IIS and OS low level caching mechanisms which offer superb performance.
It allows IIS to serve the log files that capture output of a node.js application as static files such that you can access them over HTTP. By default, if your server.js entry point is reached at http://example.com/server.js, the log files would be accessible at http://example.com/iisnode.
It exposes the built-in node-inspector debugger at http://example.com/server.js/debug. Learn more about debugging with iisnode.
It sends all other HTTP requests to be processed by your node.js application in server.js.
Fin more info in my link there I have a working example a the bottom of the question
Below its the project configuration
below the code for server.js
"use strict";
var express = require('express');
// determind what mode we are
var env = process.env.NODE_ENV = process.env.NODE_ENV||'developemnt';
var app = express();
//configure the view engine
app.set('views', __dirname + '/views');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
//app.use('/public', express.static(__dirname + '../public'));
//the asterisk handles all routes includes javascript, css, html request...
app.get('*', function (req , res) {
res.render('index');
});
var PORT = 3030;
app.listen((process.env.PORT!==undefined)?process.env.PORT:PORT);
console.log('Listening to PORT : ' + process.env.PORT );
Below the index.html
<!doctype html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"/>
<base href="/">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
</head>
<body ng-app="idetikApp">
<div ng-view=""></div>
<script src="app/app.js"></script>
</body>
</html>
web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="server/server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="LogFile" patternSyntax="ECMAScript" stopProcessing="true">
<match url="iisnode"/>
</rule>
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^server/server.js\/debug[\/]?" />
</rule>
<rule name="StaticContent">
<action type="Rewrite" url="public{{REQUEST_URI}}"/>
</rule>
<rule name="DynamicContent">
<conditions>
<add input="{{REQUEST_FILENAME}}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="server/server.js"/>
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
app.js
angular.module('idetikApp', [ 'ngResource', 'ngRoute']);
angular.module('idetikApp').config(function ($routerProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routerProvider.when('/', {templateUrl: '/partials/main', controller:'mainctrl'})
});
angular.module('idetikApp').controller('mainctrl', function ($scope) {
$scope.mayvar = "hello world"
})
and the main.html
<h1>this is a partial</h1>
<h2>{{myvar}}</h2>
and my folder structure
but now I cant find my files inside public :(

Resources