Azure Websites + node.js - Cannot GET / - node.js

When deploying a node.js / express app to Azure Websites I am getting "Cannot GET /" error in the browser. The same application runs flawlessly on the local machine.
web.config is standard (I only removed the Static Rewrite rule), so:
<!-- All other URLs are mapped to the node.js site entry point -->
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="src/server/app.js"/>
</rule>
</rules>
Code is deployed in src/server/ (node functions) and src/client/ (static content) folders.
As per the FREB logs the src/server/apps.js is fetched, but in the end the following error is thrown:
ErrorCode The system cannot find the file specified. (0x80070002)
Inside app.js I have the following configuration for static files:
app.use(express.static('./src/client/'));

Azure Websites runs node from the folder where the file defined in the package json as the start file is located, so e.g. for the following:
"start": "node src/server/app.js"
fact it will run node app.js from the src/server folder (you can find there also the iisnode.yml file). Which results in all relative paths getting messed up. One solution for this is to use absolute paths, so e.g.:
app.use(express.static('D:/home/site/wwwroot/src/client/'));
and switching between paths using e.g. process.env.NODE_ENV variable.

Related

IIS Router Site rewrite rule doesn't find resource file when accessed with resource /current

On my IIS I have configured rewrite rules in the following way.
There is a Router Site which listens on port 80 and rewrites to proper websites by specified resource e.g when user enters host http://testpage.com/current it rewrites it to another website hosted under port 5001. Sample config:
<rule name="RewriteRule" stopProcessing="true">
<match url="^current(.*)?" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Rewrite" url="http://{HTTP_HOST}:5001/{R:1}" />
</rule>
The site hosted under 5001 contains index.html and javascript file index.js which is referenced in index.html like this:
<script type="text/javascript" src="/index.js">
The entire configuration works super fine when I replace
<match url="^current(.*)?" />
with
<match url="^(.*)?" />
However when I use current here then it finds index.html located in page hosted under port 5001 but it can't find index.js. I suspect that the reason is because it tries to find /current/index.js but it doesn't exist.
I always thought that my RewriteRule should basically rewrite url to
http://testpage.com:5001
then get index.html and resolve index.js from current directory so it shouldn't have any knowledge about "current" resource.
Is there an easy way to fix this?
Obviously when I enter to the website like this: http://testpage.com:5001, bypassing rewrite rules it works fine.
I'll answer this for my future reference. It's not an issue with rewrite rules. The issue is with the way html resolves resources.
We need to do a few steps:
Set <base href="virtualpath">, in this example virtualpath should be equal to /current. We use webpack during TeamCity deployment to put correct value here. Example:
<script type="text/javascript">
window.virtualpath = window.location.pathname.toLowerCase().indexOf("${virtual_path_legacy}") >= 0 ? "/${virtual_path_legacy}" : "";
document.write("<base href='" + window.virtualpath + "/'/>");
</script>
new webpack.DefinePlugin({ "process.env": { NODE_ENV: JSON.stringify("production") }, virtual_path_legacy: JSON.stringify(virtual_path_legacy) })
In webpack.config.js make sure your publicPath is "" instead of "/"
If you use SignalR make sure you pass your virtualpath to $.hubConnection(If SignalR is hosted under virtualpath)
in .NET Core:
app.UseAppBuilder(appBuilder => appBuilder.MapSignalR(rootPath, hubConfiguration), camelCaseSerialization);
If you use react-router make sure you pass virtualpath to useRouterHistory. Example:
const browserHistory = useRouterHistory(createHistory)({
basename: infoService.virtualpath,
});
If you use libraries like fetch to connect to the API make sure to prefix all your api urls with virtualpath (if your API is also using virtualpath)

How to setup iisnode with express?

I recently started working on nodejs. I created a simple nodejs api (with express) which connect to SQL server database and return result. After my development I had challenge how to host this node js api. I decided to host my api on IIS. I got different errors and in the end I was able to make it work. Thanks to different articles on internet.
Below are the steps I followed. May be this can help anyone who is new and trying to host nodejs in windows IIS.
I recently started working on nodejs. I created a simple nodejs api (with express) which connect to SQL server database and return result. After my development I had challenge how to host this node js api. I decided to host my api on IIS. I got different errors and in the end I was able to make it work. Thanks to different articles on internet.
Below are the steps I followed. May be this can help anyone who is new and trying to host nodejs in windows IIS.
Step 1: Install IISnode. Make sure to select correct bit version as per your machine. I was using windows 10 64 bit. I installed iisnode-full-v0.2.21-x64.msi
https://github.com/azure/iisnode/wiki/iisnode-releases
Step 2: Install URL rewrite module
https://www.iis.net/downloads/microsoft/url-rewrite
Step 3: For my use I created a new website in IIS with name "Node Web Site". This site is running on port 90. Point this web site to physical path where your Nodejs api is available.
Step 4: Provide node js api folder access to "IIS_IUSRS" group. You will get access error if don't provide access.
Step 5: Add a web.config file in your node js api folder. Add below code in your config file. This will tell IIS that server.js will be handled by IISnode.
Note: I have only one file in my project (server.js). If you have multiple files then you can add all those files here.
<configuration><system.webServer><handlers><add name="iisnode" path="server.js" verb="*" modules="iisnode" /></handlers>
</system.webServer></configuration>
Step 6: Add URL rewrite rule in your config file. This is required to make url user friendly. otherwise you need to provide .JS file path in the url. below is the final config file which I have in my application.
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="server.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="api">
<match url="api/*" />
<action type="Rewrite" url="server.js" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="node_modules" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</configuration>
Before Rewrite section I was calling my application with url http://localhost/nodesample1/server.js
After rewrite url can be like
http://localhost/nodesample1/api
Step 7: Now you need to make changes in get call of express. you need to provide full path in get call.
for example before hosting application in IISNode my default get call code was like below snippet
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser());
app.get('/', function (request, response) {
response.write('running');
response.end();
});
But after IISNode hosting I had to change my get call like below
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser());
app.get('nodesample1/api', function (request, response) {
response.write('running');
response.end();
});
As I want me url to be like "http://localhost/nodesample1/api" I had to provide complete path in get call.
That's it.
This approach worked for me.

Child Process won't spawn with iisnode

I have a MEAN (Angular 2) app working on a Windows Server.
I run my app with iisnode on the server.
The app works fine, but at some point I try to spawn a child process with node (powershell.exe). Everything works fine locally (the powershell script executes successfully) but when I test it from the server URL, the child-process never spawns.
I don't have any error message, the app just kinda pauses itself.
When I run node.js from the command prompt with "node server.js" and I go to : http://myserver.com:9000 then the app works fine and the child-process spawns sucessfully.
I've tried to put the absolute path of powershell.exe but it doesn't work neither.
localhost:9000 : app works, child-process spawns ans works fine
myserver.com:9000 : app works, child-process spawns ans works fine
myserver.com/ : app works, child-process won't spawn, no error message
Here's my web.config file :
<configuration>
<system.webServer>
<!-- indicates that the hello.js file is a node.js application
to be handled by the iisnode module -->
<handlers>
<add name="iisnode" path="server.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="server">
<match url="/*" />
<action type="Rewrite" url="server.js" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
And here's the route .js from where I spawn the child-process :
child = spawn("powershell.exe",[`some commands`]);
child.stdout.on("data",function(data){
console.log("Powershell Data: " + data);
});
child.stderr.on("data",function(data){
console.log("Powershell Errors: " + data);
});
child.on("exit",function(){
console.log("Powershell Script finished");
//some other commands
});
child.stdin.end();
Finally, it appeared that the powershell.exe was spawning correctly.
However, the powershell script was supposed to open Powerpoint and do some actions within it. That part wouldn't work, and powershell would raise this error :
Insufficient memory to continue the execution of the program
This was solved by modifying DCOM config, as suggested here : How to deploy COM object Microsoft.Office.Interop to IIS so that my C# WCF service reference will work?

How and what to deploy Angular2 to IIS

Take for instance angular2-quickstart. What files need to be deployed and what settings need to be set to launch this web app from IIS?
This is a Typescript tutorial that I have opted to compile to JavaScript.
Setting up Angular 2 for me was quite an issue because the HTML5 routes (without a hashbang) weren't working.
To get an Angular2 project working on an IIS environment without serving it using the Angular-CLI (you still need it to build it!):
After you're finished with development on your project, you'll need to build (or compile) it to a different environment. If you need to set that up, read this.
The shortest build command you need is:
ng b
In your build folder (if you didn't add an external/different folder, this will be the 'dist' folder in your project), copy the contents to your IIS server.
Ensure the folder of your IIS server has the needed permissions for the IIS_IUSRS group and IUSR user to access it. (Right click on the folder -> Properties -> Security -> Edit -> Add, and type those in. You can click the 'Check Name' button to ensure it's the correct ones you're typing in)
The next issue you need to tackle is getting a web.config file to put in your server folder to fix routing issues.
If we were working with Apache, we would need an .htaccess, but for IIS, we're using a web.config. The one found here worked for me if your application is routing from the root directory of your server.
(Note: As reminded by #Demortes, this will require an additional module to be added to your IIS environment called URLRewrite)
This (web.config) file goes in the root directory of your server.
Hope this helps anyone who had similar issues to me :)
Cheers.
Download and install IIS rewrite plugin https://www.iis.net/downloads/microsoft/url-rewrite
Create application under default website.
Create a folder in c:\inetpub\wwwroot to host the application
After step 8 copy dist folder contents to c:\inetpu\wwwroot\
Before build in index.html change base href="/" to base href="//"
To skip steps 1,2 and 3 for every new build check out step 9 as alternative to step 8
Web config structure.
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Main Rule" >
<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="/<appname subfolder>/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Note: Create a Web.config in src folder and add a reference to it in angular-cli.json in assets section so that it gets copied over during each build. -
Otherwise you will have manually create this file every time ng build is used.
not applicable
In angular-cli.json put web.config in assets block
"assets": [
"assets",
"favicon.ico",
"Web.config"
],
In angular-cli.josn put custom css path example so that it will be packaged in styles..bundle.cs
"styles": [
"../node_modules/font-awesome/css/font-awesome.min.css",
"../node_modules/bootstrap/dist/css/bootstrap.min.css",
"assets/site.css",
"assets/page.css",
"assets/menu.css",
"styles.css",
"../node_modules/primeng/resources/primeng.min.css",
"../node_modules/primeng/resources/themes/omega/theme.css"
],
If you have custom scripts put those path under scripts section example
"scripts": [
"../node_modules/jquery/dist/jquery.js",
"index.js"
],
ng build --prod
Note: ng build --prod starts AOT (ahead of time compilation) by default in latest version of angular-cli
Note: ng build command deletes dist folder and recreates that folder every time you ng build command
Alternative build command:
ng build --prod --output-path 'C:\inetpub\wwwroot\<appname subfolder>' --base-href /<appname subfolder>'/
a- if you don't want to manually update base-href in index.html
b- if you don't want to copy dist folder and wwwroot folder of app.
Note1: Following command will only work if you open visual code (or any terminal app) with administrative privileges. Otherwise mkdir command to create output folder in wwwroot will fail.
Note2: You still need to update Web.config with . See step 4
Note3: Checkout slash / at both starting and end of --base-href /'/
Check direct quote from one of the posting. Not sure if changing security privileges of IIS_IUSRS group and IUSR user for ...wwwroot\
as described in one of the web links is required. May be it is not required but I am highlighting it over here for you to keep in mind.
Direct quote from another use : " Ensure the folder of your IIS server has the needed permissions for the IIS_IUSRS group and IUSR user to access it. (Right click on the folder -> Properties -> Security -> Edit -> Add, and type those in. You can click the 'Check Name' button to ensure it's the correct ones you're typing in)"
References :
- How and what to deploy Angular2 to IIS
- https://www.youtube.com/watch?v=K0XORWxG11k
The webapp itself needs no server intelligence, as it is just static files - web assets ( *.js, *.html files etc). The static files are what angular2-quickstart generates as output of its build process, which you run in your dev environment (probably locally on your personal computer). The dev environment will need node (+ npm). And infact, you can test this tutorial on your local dev environment without the need for any external server.
edit:
If u look in the package.json u can see it has lite-server:
"scripts": {
"start": "npm run lite",
"lite": "lite-server"
},
Lite server is a small server that simulates a simple (web) file server.
Lightweight development only node server that serves a web app, opens
it in the browser, refreshes when html or javascript change, injects
CSS changes using sockets, and has a fallback page when a route is not
found.
To give you an answer, to serve your app with IIS, you only need http://docs.asp.net/en/latest/fundamentals/static-files.html
I have created a small github project that has Angular2 current as of today (10/13/2016) that runs in IIS 7.5 with routing and under ng serve.
It does use the hash based URL strategy.
It uses angular-cli, the read me has details on how to setup under IIS or ng serve.
https://github.com/howserss/Angular2-IIS-sample-app
Angular 2 routing (with hash) will work without any issue on IIS. Just create default URL rewrite rule which will redirect all the requests to index.html file of your angular app. Rule will redirect all requests to index.html except for required js files and actual angular app urls (i.e. index.html or index.html#/{route-value}.
EX:
<rules>
<rule name="Default">
<match url="(.* ).js|index.html(.*)" negate="true" />
<action type="Rewrite" url="/index.html" />
</rule>
</rules>
Angular 2 routing (without hash) will not work with IIS.
In case of pure HTML application IIS will be routing the incoming request and it will redirect the request to error page if such page does not exist at that location.
In case of .Net MVC application you can create a default route to handle all the incoming request url's and redirect it to your angular index view.
Ex Route for MVC application:
routes.MapRoute(
name: "Angular",
url: "{*url}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { url = new AppFeatureUrlConstraint() }
public class AppFeatureUrlConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (values[parameterName] != null)
{
var url = values[parameterName].ToString();
if (url.StartsWith("angular/", StringComparison.InvariantCultureIgnoreCase))
return true;
else
return false;
}
return false;
}
}
Here is a nice and detailed explanation for deploying angular app in IIS. The summarized steps for publishing as a separate website are as follows:-
Create a web site in IIS, lets say the physical path you provided for this web application is C:\Publish.
publish you angular app using the below command:-
ng build --prod
After the application is built successfully copy and paste the contents of dist folder to C:\Publish folder.
Add web.config file to folder C:\Publish with following contents:-
<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="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
All done, now just go to IIS and browse your web-site, you will see it working.
after you build it, copy all files in dist fold put in a fold in your IIS server, it only contains html, css and js files, so just host it like a static website.
It works for any web server, no matter Apache, IIS or nginx.

deploying nodejs app to Azure via FTP does not work

I am trying out Azure with Node and using Wercker CI to build then deploy to Azure via FTP.
But it seems like I am having some trouble getting this to work. I am copying a server.js file along with a package.json and some other assets. But it looks like nothing runs the npm install command. Plus, I get a You do not have permission to view this directory or page. when reaching the site.
There is a web.config file with the following config:
<!--
This configuration file is required if iisnode is used to run node processes behind IIS or IIS Express. For more information, visit:
https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config
-->
<configuration>
<system.webServer>
<handlers>
<!-- indicates that the app.js file is a node.js application to be handled by the iisnode module -->
<add name="iisnode" path="./server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<!-- Don't interfere with requests for logs -->
<rule name="LogFile" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^[a-zA-Z0-9_\-]+\.js\.logs\/\d+\.txt$"/>
</rule>
<!-- Don't interfere with requests for node-inspector debugging -->
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^\.\/server.js\/debug[\/]?" />
</rule>
<!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
<!--<rule name="StaticContent">
<action type="Rewrite" url="./app/assets/{REQUEST_URI}"/>
</rule>-->
<!-- All other URLs are mapped to the Node.js application entry point -->
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="./server.js"/>
</rule>
</rules>
</rewrite>
<iisnode watchedFiles="*.js;node_modules\*;routes\*.js;views\*.jade;middleware\*.js"/>
</system.webServer>
</configuration>
And the server.js is as simple as:
var http_1 = require('http');
var express = require('express');
var log4js_1 = require('log4js');
var morgan = require('morgan');
var split = require('split2');
// NOTE: Some of the features used are only available in ES6
// do not forget to start the server using `node --harmony` option so that everything will work properly,
// in case you use anything that is behind the staging flag.
// Check https://nodejs.org/en/docs/es6/#which-features-are-behind-the-es_staging-flag
// to see which features require the flag.
var app = express();
var APP_PORT = process.env.PORT || 5000;
var APP_PATH = __dirname + "/public";
// We will need to split the output from morgan into lines to avoid trailing line feeds,
// https://github.com/expressjs/morgan/issues/70.
var log = log4js_1.getLogger();
var log4jsStream = split().on('data', function (line) {
log.debug(line);
});
app.use(morgan('tiny', { stream: log4jsStream }));
// `__dirname` in this case will be `./dist/` since we start the server from there.
app.use(express.static(APP_PATH));
app.all('/*', function (req, res) {
res.sendFile(APP_PATH + "/index.html");
});
var server = http_1.createServer(app);
server.listen(APP_PORT, function () {
// console.log(`Express server listening on port ${APP_PORT}`);
});
//# sourceMappingURL=server.js.map
Finally, the package.json contains the necessary deps.
So I am just wondering, is there any way to deploy an app to Azure and trigger the npm install and start the web server?
I can see that if you do the deployment via Git, Kudu will do all of that for me. But I do not think that is an option for me while using Wercker to do the deployment. Also, I am compiling some TypeScript files during the CI builds and I do not want those in version control, so having a git repo where I need to store all the compiled files is also not an option.
I would really appreciate some input on how to handle this.
When deploying via FTP npm install will not be automatically run. In this case you can use console to run npm install
It’s a little confused according your description:
So I am just wondering, is there any way to deploy an app to Azure and trigger the npm install and start the web server?
I can see that if you do the deployment via Git, Kudu will do all of that for me. But I do not think that is an option for me
Generally, via Git to deploy your node app to Azure, it will run npm install automatically. What are specific requirements you prefer to if you wouldn’t like to use Git.
Currently I can’t reproduce your issue about Wercker. Does it occur errors when you deploy via GIT?
And according your comments with # theadriangreen, it seems you failed in npm install because of time out. If so, you can try to enable Always On setting of your Web App to prevent your site unload if they haven’t gotten any requests for a period time.
Additionally, if you still need some custom tasks after deployment, you can refer to Post Deployment Action Hooks to configure your custom deployment scripts.
When deploying via FTP npm install will not be automatically run. In this case you're going to have to include all of the dependencies in the folder that you deploy. So you can do that and deploy over FTP, or you can use git.
If you use git, you can add the files that you don't want deployed to your .gitignore, and then deploy them separately (i.e. put them in a blob).

Resources