IIS & HapiJS error : 404 on trying to serve static files - node.js

So, I'm hosting a hapijs application on Windows server.
Everything worked okay until I had the need to serve some static files (my front-end) from my hapijs application.
I added a simple route:
// Index
server.route({
method: 'GET',
path: '/dist',
options: {
cors: {
origin: ['*'],
},
},
handler(request, h) {
server.log('info', `GET - ${request.url.path}`);
return h.file(__dirname + '/dist/index.html')
},
});
and this works okay.
But for the assets (located in the same folder), nothing to do, I can't make it work. I tried the following things:
server.route({
method: 'GET',
path: '/assets/{filename}',
options: {
cors: {
origin: ['*'],
},
},
handler(request, h) {
server.log('info', `GET - ${request.url.path}`);
return h.file(`${__dirname}/dist/assets/${request.params.filename}`)
},
});
and
server.route({
method: 'GET',
path: '/assets/{filename}.js',
options: {
cors: {
origin: ['*'],
},
},
handler(request, h) {
server.log('info', `GET - ${request.url.path}`);
return h.file(`${__dirname}/dist/assets/${request.params.filename}.js`)
},
});
And some other flavours of it. Until I realized that it is actually IIS and not the hapijs that returns a 404.
So here is my web.config file :
<configuration>
<system.webServer>
<handlers>
<remove name="OPTIONSVerbHandler" />
<add name="iisnode" path="server.js" verb="GET,POST,OPTIONS" modules="iisnode" />
<add name="OPTIONSVerbHandler" path="*" verb="OPTIONS" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="DynamicContent">
<match url=".*" />
<action type="Rewrite" url="server.js" />
</rule>
</rules>
<outboundRules>
<rule name="Set-Access-Control-Allow-Origin for known origins">
<match serverVariable="RESPONSE_Access-Control-Allow-Origin" pattern=".+" negate="true" />
<action type="Rewrite" value="*" />
</rule>
</outboundRules>
</rewrite>
<httpErrors existingResponse="PassThrough" />
<security>
<requestFiltering>
<hiddenSegments>
<add segment="node_modules" />
</hiddenSegments>
</requestFiltering>
</security>
<iisnode nodeProcessCommandLine=""C:\Program Files (x86)\nodejs\node.exe"" />
</system.webServer>
<system.web>
<httpRuntime enableVersionHeader="false" />
</system.web>
</configuration>
I tried adding a
<rule name="StaticContent">
<action type="Rewrite" url="server.js"/>
</rule>
block but that does not change anything.
In case you didn't get it yet, I am no windows server user so I'm trying to solve that problem with Google searches & al but no luck so far.
I guess my question is :
WHY does IIS try to search for a file, even though it is supposed to rewrite all path to go to my NodeJS server ???
How does it know which of my routes are good and which are not?
How to make it not search for that file?

In the end, I managed to solve the problem with the following configuration:
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="server.js" verb="GET,POST,OPTIONS" 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="DynamicContent" stopProcessing="true">
<match url="^v1.*" />
<action type="Rewrite" url="server.js"/>
</rule>
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>
</rule>
</rules>
<outboundRules>
<rule name="Set-Access-Control-Allow-Origin for known origins">
<match serverVariable="RESPONSE_Access-Control-Allow-Origin" pattern=".+" negate="true" />
<action type="Rewrite" value="*" />
</rule>
</outboundRules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="node_modules" />
</hiddenSegments>
</requestFiltering>
</security>
<iisnode nodeProcessCommandLine=""C:\Program Files (x86)\nodejs\node.exe"" />
</system.webServer>
<system.web>
<httpRuntime enableVersionHeader="false" />
</system.web>
</configuration>

Related

Serve Angular client and Express Sever on IIS without breaking urls

I used angular on the client-side, and nodejs on server-side, when running them using webstorm IDE they behave as expected but now I'm trying to put them on is and I'm encountering some troubles.
My main goal is to make just the client and server work together so if you have another suggestion which I have not tried, please comment.
In the IIS I had to put both server and client under the same URL (obviously) so I have deployed my angular app and copied the content of the dist folder into folder name "wwwroot" to my server (express).
Here is my code:
app.js files:
app.use(express.static(__dirname + '/wwwroot'));
app.post('/home', (req, res) => {
// some content here
});
app.get('/home', (req, res) => {
// some content here
});
const server = http.createServer(app);
server.listen(port, () => console.log('Listening to port: ' + port));
my web.config file:
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="app.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="nodejs">
<match url="(.*)" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Rewrite" url="/app.js" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="node_modules" />
<add segment="iisnode" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</configuration>
Now, it would work just fine but I have 1 main problem which I can't solve when I'm trying to reload a page via a URL,
e.g. "http://www.automationcompare.com:4040/home;urlId=23119.06799149191"
then I get: Cannot GET /home;urlId=23119.06799149191.
I think the server should be set up to return the base HTML page no matter the url. You need to use url rewrite rule like below sample:
<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>
For further information about your problem, you can refer to this post. I think it is similar to your problem.

Disable directory browsing in Nodejs, Azure

I'm trying to disable directory browsing in node, hosted on Azure.
This is my root folder and I want to disable browsing.
The following is my index.js file
const express = require('express');
const publicweb = './';
const app = express();
app.use(express.static(publicweb));
app.disable('x-powered-by');
console.log(`serving ${publicweb}`);
app.get('*', (req, res) => {
res.sendFile(`index.html`, { root: publicweb });
});
const port = process.env.PORT || '3500';
app.listen(port, () => console.log(`API running on localhost:${port}`));
I do not want the files to be accessible from the front-end application.
You can try by creating a web.config, under the hood Azure uses IIS to serve your app
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<webSocket enabled="false" />
<handlers>
<add name="iisnode" path="app.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^app.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="app.js"/>
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough" />
<iisnode watchedFiles="web.config;*.js" debuggingEnabled="false" />
</system.webServer>
</configuration>

Azure node.js relative paths with graphql cli

I have a small node.js application acting as a graph-api created with graphql-cli.
Everything works fine on localhost, but when I try to run it in azure as a web app, I seem to have a problem with paths. The below snippet is working on localhost running npm start
const server = new GraphQLServer({
typeDefs: './src/schema.graphql',
resolvers,
context: req => ({
...req,
db: new Prisma({
typeDefs: 'src/generated/prisma.graphql',
endpoint: 'xxx',
secret: 'xxx',
debug: true,
}),
}),
})
The path to one of the .graphql-file is defined:
typeDefs: './src/schema.graphql',
Which I find a bit weird considering the folder structure where index.js is in the same folder as schema.graphql
Anyhow, this is working on localhost, but when trying to run it as an azure web app I get the following error:
No schema found for path: D:\home\site\wwwroot\src\src\schema.graphql
Since this is just a scaffolded app, I don't want to change the paths in code. I don't think they are wrong since it is working on localhost. I´m thinking that I'm missing some configuration on azure.
This is my web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<webSocket enabled="false" />
<handlers>
<add name="iisnode" path="src/index.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^src/index.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="src/index.js"/>
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>
And my iisnode.yml looks just like this:
nodeProcessCommandLine: "D:\Program Files (x86)\nodejs\8.4.0\node.exe"
I´ve tried a bunch of different node versions, but I'm currently running 8.4.0 on my localhost where its working
Anyone has any ideas?
You need to move the index.js file to your root and change the web.config to:
<?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="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^index.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="index.js"/>
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>

How to host a service and static content in iisnode

I have my website working within express. But now I need to host it on IIS. How can I get my routing to work with IISNode?
I was able to move my static content to a /public folder (html, front end js, etc). I didn't want to do this, but got that part working.
I moved my server side logic to /server.
I had previously had .Net Web API style routing working, where my services were hosted at /api. I moved these to /server/api.
Whenever I try to request my API, I get a 404. How can I get this working with IISNode?
app.use("/api", require("./api"));
// routes/api/index.js
var router = require('express').Router();
router.use('/questionsets', require('./questionsets'));
module.exports = router;
var router = require('express').Router();
router.use('/questionsets', require('./questionsets.js'));
module.exports = router;
// routes/api/questions.js
var router = require('express').Router();
router.get('/', function (req, res) {
...
});
router.get('/:id', function (req, res) {
...
});
module.exports = router;
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin" />
</hiddenSegments>
</requestFiltering>
</security>
<handlers>
<add name="iisnode" path="bin/www" verb="*" modules="iisnode" />
</handlers>
<iisnode loggingEnabled="false" />
<httpErrors errorMode="Detailed" existingResponse="Replace">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" prefixLanguageFilePath="" path="/" responseMode="ExecuteURL" />
</httpErrors>
<rewrite>
<rules>
<rule name="LogFile" patternSyntax="ECMAScript" stopProcessing="true">
<match url="iisnode" />
</rule>
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^bin\/www\/debug[\/]?" />
</rule>
<rule name="StaticContent" patternSyntax="ECMAScript" stopProcessing="true">
<match url=".*" />
<action type="Rewrite" url="{C:1}" logRewrittenUrl="true" />
<conditions>
<add input="{REQUEST_URI}" pattern=".*?\/(.*)" />
</conditions>
</rule>
<rule name="DynamicContent" patternSyntax="ECMAScript">
<match url=".*" />
<conditions>
<add input="{{REQUEST_FILENAME}}" matchType="IsFile" negate="True" />
</conditions>
<action type="Rewrite" url="server/app.js" logRewrittenUrl="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Source for url rewriting configuration
You need to install Application Request Routing here , and the documentation here.
Simply putting your server side files under /server would not work.
Start your express server, let's say listenning to http://localhost:3200/ ,
route request '/' to 'localhost:3200' will just route each request to your express server.

Cannot Get Azure Node.js Logging Enabled

I am unable to get logging to work for a node application I am deploying to Windows Azure. I am overriding the web.config with the iisnode.yml file option.
Here's the yml file contents:
node_env: development
nodeProcessCountPerApplication: 1
maxConcurrentRequestsPerProcess: 1024
maxNamedPipeConnectionRetry: 24
namedPipeConnectionRetryDelay: 250
maxNamedPipeConnectionPoolSize: 512
maxNamedPipePooledConnectionAge: 30000
asyncCompletionThreadCount: 0
initialRequestBufferSize: 4096
maxRequestBufferSize: 65536
watchedFiles: *.js;iisnode.yml
uncFileChangesPollingInterval: 5000
gracefulShutdownTimeout: 60000
loggingEnabled: true
logDirectory: iisnode
debuggingEnabled: true
debuggerPortRange: 5058-6058
debuggerPathSegment: debug
maxLogFileSizeInKB: 128
maxTotalLogFileSizeInKB: 1024
maxLogFiles: 20
devErrorsEnabled: true
flushResponse: false
enableXFF: false
Here's the contents of the web.config file that's being overriden:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="false" />
<iisnode configOverrides="iisnode.yml" debuggingEnabled="true" loggingEnabled="true" logDirectory="iisnode" devErrorsEnabled="true" nodeProcessCommandLine=""%programfiles(x86)%\nodejs\node.exe"" />
<handlers>
<add name="iisnode" path="server.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<clear />
<rule name="app" enabled="true" patternSyntax="ECMAScript" stopProcessing="true">
<match url="server\.js.+" negate="true" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Rewrite" url="server.js" />
</rule>
<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>
I'm pretty sure I have enabled every logging option I can in both, but neither seems to be picked up by Azure during deployment and when I access the typical logging location, http://mysite.com/iisnode/index.html or http://mysite.com/iisnode/ I receive a 404 error.
Any help or ideas would be appreciated.
If you are using express add this under your static folder location
app.use(express.static(__dirname + '/../iisnode'));
Or define your own log routes like below.. here you can get fancy and only show errors etc.
//logging routes
var fs = require('fs');
app.get('/iisnode', function(req, res){
var _index = fs.readFileSync('./iisnode/index.html', 'utf8');
var index = _index.replace("logCell.innerHTML = 'log';", "logCell.innerHTML = 'log';");
res.send(index);
});
app.get('/iisnode/:log', function(req, res){
var _log = fs.readFileSync('./iisnode/' + req.route.params['log'], 'utf8');
res.contentType('text/plain');
res.send(_log);
});
You can replace the app.get with whatever routing solution you are using.
Remember to remove before launching the app in production.

Resources