I want to host nextjs application under the IIS website as a sub-application, following the below video I am able to run nextjs app as a website in IIS
https://www.youtube.com/watch?v=HLsx0iraA-Y . i need help to host it as a sub-application.
the reason i want to host it as a sub app is i already have 5-6 asp.net websites running as sub applications under the parent site and now i want to host nextjs app under the same site.
here are the serverjs and web.config files
server.js file
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const port = process.env.PORT || 3000
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer((req, res) => {
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url, true)
const { pathname, query } = parsedUrl
if (pathname === '/a') {
app.render(req, res, '/a', query)
} else if (pathname === '/b') {
app.render(req, res, '/b', query)
} else {
handle(req, res, parsedUrl)
}
}).listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:{port}`)
})
})
web.config file
<configuration>
<system.webServer>
<!-- Visit http://blogs.msdn.com/b/windowsazure/archive/2013/11/14/introduction-to-websockets-on-windows-azure-web-sites.aspx for more information on WebSocket support -->
<webSocket enabled="false" />
<handlers>
<!-- Indicates that the server.js file is a node.js site to be handled by the iisnode module -->
<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<!-- Do not 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="public{REQUEST_URI}"/>
</rule>
<!-- 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="server.js"/>
</rule>
</rules>
</rewrite>
<!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="node_modules"/>
</hiddenSegments>
</requestFiltering>
</security>
<!-- Make sure error responses are left untouched -->
<httpErrors existingResponse="PassThrough" />
<iisnode node_env="production" />
<!--
You can control how Node is hosted within IIS using the following options:
* watchedFiles: semi-colon separated list of files that will be watched for changes to restart the server
* node_env: will be propagated to node as NODE_ENV environment variable
* debuggingEnabled - controls whether the built-in debugger is enabled
See https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config for a full list of options
-->
<!--<iisnode watchedFiles="web.config;*.js"/>-->
</system.webServer>
</configuration>
any help is highly appreciated.
I had to add a base path in the next.config.js file to make it work.
Now it works the same way as root application. the requests will have a sub-application after the domain
www.domain.com/subapplication
www.domain.com/subapplication/aboutus
www.domain.com/subapplication/contactus
www.domain.com/subapplication/api/hello
module.exports = {
basePath: '/subapplication',
images: {
path: `subapplication/_next/image`,
},
async rewrites() {
return [
{
source: '/subapplication/',
destination: '/subapplication/index'
},
]
},
} ```
Related
I have a node/express API project. I am trying to run this project through Internet Information Service (IIS). My current structure within IIS - Sites -> environment (website) -> project1 | project2 (node application here). I converted project2 to an application with the environment app pool name. I then create the app.
I try to hit my url but I get back a 403 forbidden error. I have given it all of the credentials. Unsure why this is happening.
Some resources I've been using - video blog
web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<!--
By default IIS will block requests going to the bin directory for security reasons.
We need to disable this since that's where Express has put the application entry point.
-->
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin" />
</hiddenSegments>
</requestFiltering>
</security>
<handlers>
<!-- Indicates that the www file is a node.js entry point -->
<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> -->
<rewrite>
<rules>
<rule name="sendToNode">
<match url="/*" />
<action type="Rewrite" url="app.js" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
app.js
const express = require('express')
const bodyParser = require('body-parser')
const path = require('path')
const cors = require('cors')
const compression = require('compression')
const helmet = require('helmet')
const expressSanitizer = require('express-sanitizer')
const jwt = require('jwt-simple');
const index = require('./routes/index')
const something1 = require('./routes/something1')
const something2 = require('./routes/something2')
const responseTime = require('response-time')
const app = express()
// const app = express.createServer()
const port = 3000
var corsOptions = {
origin: 'http://localhost:8100',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
//added security
app.use(helmet())
// //set logger
// app.use(logger)
//cors options
app.use(cors(corsOptions))
//body parser middleware
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: false}))
// Mount express-sanitizer here
app.use(expressSanitizer()) // this line follows bodyParser() instantiations
//set static path
app.use(express.static(path.join(__dirname, 'client')))
// app.use(express.static(path.join(__dirname, '../../www')))
//Use response time
app.use(responseTime())
// set our default template engine to "ejs"
// which prevents the need for using file extensions
app.set('view engine', 'ejs')
//gzip compression
app.use(compression())
//add authorization request header
app.use((req, res, next) => {
if(!req.headers.authorization){
return res.status(403).json({ error: 'No credentials sent!'});
}
// try {
// let token = req.headers.authorization.split(' ')[1]
// var decoded = jwt.decode(token, 'your secret here');
// console.log(decoded, 'decoded')
// } catch (err) {
// console.log('err ', err.stack);
// return res.status(403).json({
// error: 'invalid token'
// });
// }
next();
})
//set views for error and 404 pages
app.set('views', path.join(__dirname, 'views'))
app.use('/', index)
app.use('/something1/v1', something1)
app.use('/something2/v1', something2)
// app.listen(port, () => {
// console.log('server started on port 3000')
// })
//run export NODE_ENV=production
//NODE_ENV=production node app.js
app.listen(process.env.port)
I have everything installed - (rewrite/node/iisnode) Just don't know how to set it up. Thanks
Hmm I think the way you've setup the website in IIS is not quite right.
Sound like your IIS structure is like:
Sites
└─── environment
└─── project1
└─── project2
| app.js
| web.config
| ...
Where environment is a website, and each folder under it is probably a folder which has been converted to an application.
This seems to be a common mistake people are making because the hello world tutorial runs as an application under the Default Web Site (although I'm not sure why this is the case, but my guess is because it doesn't replace the default website which is easily accessible over port 80 on localhost). This isn't the best way to setup a new IISNode website.
What you should do is add a new site (right click Sites and choose Add Website from the context menu). Fill out the necessary details and point the Physical path: to where your app.js and web.config reside.
IISNode should handle the rest if you've setup the web.config right.
Good luck and let me know if I've made any wrong assumptions.
I could fix a similiar problem adding a iisnode tag.
The Web.config looks like this:
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="app.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="sendToNode">
<match url="/*" />
<action type="Rewrite" url="app.js" />
</rule>
</rules>
</rewrite>
<iisnode
watchedFiles="*.js;node_modules\*;views\*.ejs"
nodeProcessCommandLine="\program files\nodejs\node.exe" />
</system.webServer>
</configuration>
I followed these tutorials to host my node.js apps:
Harvey Williams Tutorial and Scott Hanselman Tutorial
Can anyone help me with this issue ? I am stuck on this for 2 days,....I really do not how to fix it....I built a web page(contains a table) using angular cli/angular5 and expressjs. I use mongoose mongodb as the database. Everything working fine on local. But When I uploaded the dist folder and deployed it to the azure web app, its table is empty.
The error log looks like this:
HttpErrorResponse {headers: HttpHeaders, status: 200, statusText: "OK", url: "https://stringfr.azurewebsites.net/book", ok: false, …}
error:
error: SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse
HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message: "Http failure during parsing for https://stringfr.azurewebsites.net/book"
name:"HttpErrorResponse"
ok:false
status:200
statusText:"OK"
Here is my temp website:"https://stringfr.azurewebsites.net". you can check the full error log by inspect.
I think this is something to do with this line in the book.component.ts.
Here I get data from the database and use it on a simple html table.
Note book_value is just a interface for me to a get a more structural data.
this.http.get<book_value>('/book').subscribe (
data => this.books = data
);
Here is where I init the '/book'.
in app.js
var express = require('express');
var path = require('path');
var logger = require('morgan');
var bodyParser = require('body-parser');
var book = require('./routes/book');
var app = express();
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
//mongoose.connect('put ur own mongodb')
// .then(() => console.log('connection succesful'))
//.catch((err) => console.error(err));
mongoose.connect('mongodb://localhost/mean-angular5')
.then(() => console.log('connection succesful'))
.catch((err) => console.error(err));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({'extended':'false'}));
app.use(express.static(path.join(__dirname, 'dist')));
app.use('/books', express.static(path.join(__dirname, 'dist')));
app.use('/book', book);
In the ./routes/book.js
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var Book = require('../models/Book.js');
/* GET ALL BOOKS */
router.get('/', function(req, res, next) {
Book.find(function (err, products) {
if (err) return next(err);
res.json(products);
console.log(products);
console.log("products info above");
console.log(res);
});
});
Here is my schema in the ../models/Book.js:
var mongoose = require('mongoose');
var BookSchema = new mongoose.Schema({
timestamp: String,
question : String,
answer : String
});
module.exports = mongoose.model('Book',BookSchema );
Since I am using dist folder. I use this web.config.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="AngularJS" stopProcessing="true">
<match url="^(?!.*(.bundle.js|.bundle.map|.bundle.js.gz|.bundle.css|.bundle.css.gz|.png|.jpg|.ico|.svg|.eot|.woff|\.woff2)).*$" />
<conditions logicalGrouping="MatchAll"></conditions>
<action type="Rewrite" url="/" appendQueryString="true" />
</rule>
</rules>
</rewrite>
<staticContent>
<remove fileExtension=".svg" />
<remove fileExtension=".eot" />
<remove fileExtension=".woff" />
<remove fileExtension=".woff2" />
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
<mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
<mimeMap fileExtension=".woff" mimeType="application/font-woff" />
<mimeMap fileExtension=".woff2" mimeType="application/font-woff" />
</staticContent>
</system.webServer>
</configuration>
Note: There is not error showing on the azure log stream.
Here is my app.module:
const appRoutes: Routes = [
{
path: 'books',
component: BookComponent,
data: { title: 'Book List' }
},
{ path: '',
redirectTo: '/books',
pathMatch: 'full'
}
];
#NgModule({
declarations: [
AppComponent,
BookComponent,
],
imports: [
BrowserModule,
HttpClientModule,
CalendarModule,
BrowserAnimationsModule,
FormsModule,
AccordionModule,
PanelModule,
ButtonModule,
RadioButtonModule,
DataTableModule,
MultiSelectModule,
DropdownModule,
RouterModule.forRoot(
appRoutes,
{ enableTracing: true } // <-- debugging purposes only
)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Disclaimer: I've never used Azure.
It looks to me like Azure is rewriting all requests to / and serving Angular's index.html on every path.
How these kinds of rewrite rules usually work:
Request comes in
Azure parses the request, and sees if any rules apply. If so, it changes the request URL based on the redirect.
The new edited request URL gets sent to Express.
Express responds with the edited request URL's result.
It looks to me like when in step 2 Azure rewrites /book to /, then the /book URL is never hit.
I suggest you first try removing the rewrite rule and seeing if the /book URL sends the correct JSON response.
If it does not, check if /book/ with the trailing slash sends the right response. If it does, and /book does not, add a 5-line middleware to make Express think /book and /book/ mean the same thing.
Well, in Azure, your app runs on Microsoft IIS, and /book is an express route which is a backend Node.js app and it should be executed by the IIS module iisnode. So, you'd need to edit your web.config file to set the CGI handler to something like this:
<handlers>
<!-- Indicates that the app.js file is a node.js site to be handled by the iisnode module -->
<add name="iisnode" path="app.js" verb="*" modules="iisnode"/>
</handlers>
And also add the following rule to tell what the entry point is:
<!-- 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="app.js"/>
</rule>
And add this to tell that where the static content are:
<!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>
</rule>
See also:
How to define a web.config file for Azure deployment for a MEAN application?
Using a custom web.config for Node apps
have you tried pasting https://stringfr.azurewebsites.net/book into your broswer and seeing what returns?
If it's not JSON, that's your problem.
iisnode encountered an error when processing the request.
HRESULT: 0x2
HTTP status: 500
HTTP subStatus: 1001
HTTP reason: Internal Server Error
This is a fairly simple node/express application which we cannot get to run under iisnode.
We are seeing errors in the ETW trace such as:
iisnode request processing failed for reasons unrecognized by iisnode
iisnode was unable to establish named pipe connection to the node.exe process
The last thing seen in the node output is:
server Express http server listening on port \.\pipe\15524b7e-249a-4464-b41a-eddab028342e
Where can we look to find out what is happening?
1) the node application works correctly from the command line in the folder, by typing node server.js.
2) There is seemingly nothing "thrown" in the javascript, we have this code:
process.on('uncaughtException', function (err) {
console.log(err);
fs.writeFileSync("exception.txt", err, "utf8");
})
3) the web.config is as follows:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<!-- Indicates that the server.js file is a node.js site to be handled by the iisnode module -->
<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<!-- Do not interfere with requests for node-inspector debugging -->
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^server.js\/debug[\/]?" />
</rule>
<rule name="NodeLog">
<action type="Rewrite" url="iisnode{REQUEST_URI}"/>
</rule>
<!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>
</rule>
<!-- 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="server.js"/>
</rule>
</rules>
</rewrite>
<!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
<add segment="node_modules"/>
</hiddenSegments>
</requestFiltering>
</security>
<!-- Make sure error responses are left untouched -->
<httpErrors existingResponse="PassThrough" />
<!--
You can control how Node is hosted within IIS using the following options:
* watchedFiles: semi-colon separated list of files that will be watched for changes to restart the server
* node_env: will be propagated to node as NODE_ENV environment variable
* debuggingEnabled - controls whether the built-in debugger is enabled
See https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config for a full list of options
-->
<!--<iisnode watchedFiles="web.config;*.js"/>-->
<defaultDocument enabled="true">
<files>
<add value="server.js" />
</files>
</defaultDocument>
</system.webServer>
</configuration>
The server.js is pretty simple:
var fs = require('fs');
var express = require('express');
var path = require('path');
var morgan = require('morgan');
var bodyParser = require('body-parser');
var methodOverride = require('method-override'); // Lets you use HTTP verbs such as PUT or DELETE in places where the client doesn't support it.
var routes = require('./routes/index');
var http = require('http');
var https = require('https');
var options = {
pfx: fs.readFileSync(path.join(__dirname, 'sslcert') + '/web.local.pfx'),
passphrase: ''
}
var app = express();
//app.engine("ejs", ejsEngine); // templating engine
// must set up environment before controllers establish routes
// all environments
// set default port if not set
//app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
// opt into services
app.use(express.static(path.join(__dirname, 'public')));
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(methodOverride());
/* api access */
app.get('/api', function (req, res) {
res.send('api is up and running');
});
//app.get('*', function (req, res) {
// res.render('index.html');
//});
var port = process.env.PORT || 3000;
// start the web server
http.createServer(app).listen(app.get(port), function () {
//http.createServer(app).listen(2025, function () {
console.log('server', 'Express http server listening on port' + port);
});
https.createServer(options, app).listen(8443, function () {
console.log('server', 'Express https server listening on port 8443');
});
module.exports = app;
I had the same issue with IISNode and spent far too much time trying to fix it. I tried adding different permissions for different users (IIS_USRS, IUSRS, Everyone, IIS AppPool/domainname) through Windows explorer with no luck.
I finally found the solution in the setupexamples.bat script, provided with the iisnode examples. Simple run the following script on the folder containing your app code:
icacls (folder_name) /grant IIS_IUSRS:(OI)(CI)F /t
My 404 redirection to the NotFound (Controller Action) is not working while deployed in Azure Websites. I am getting normal browser 404 instead of my custom 404 page. But they work locally when debugging in localhost in Visual Studio. This is my Web.Config.
<system.web>
<!-- ........ -->
<customErrors mode="RemoteOnly" defaultRedirect="~/Error/Index">
<error redirect="~/Error/NotFound" statusCode="404" />
</customErrors>
</system.web>
My ErrorController:
public class ErrorController : Controller
{
public ViewResult Index()
{
ViewBag.Title = "Error Page";
var model = new ErrorViewModel() {ErrorMessage = "Sorry for the error!", TitleBar = "Error Page"};
return View("Error", model);
}
public ViewResult NotFound()
{
Response.StatusCode = 404; //you may want to set this to 200
return View("PageNotFound");
}
}
Will appreciate your help.
It seems like the issue is fixed by adding the following under in your web.config file:
<system.webServer>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
This is because PassThrough leaves the existing response, as long as there is one. So you can define the error page.
Please read my question,even a small piece of advice will be received with gratitude.
I am getting the following error in Google Chrome:
GET http://localhost/socket.io/?EIO=3&transport=polling&t=1419089743449-2 404 (Not Found)
My folder setup is as follows:
localhost
pro
public
socket.io/socket.io.js
cssfile.css
jsfile.js
app.js
node_ modules
It seems to me that the request made by the client for the handshake is wrong because the error should be localhost/pro/public/socket.io/blah blah .
I am using the following setup:
web.config :
<handlers>
<add name="iisnode" path="app.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="DynamicContent">
<match url="/pro/" negate='True'/>
<!--<conditions>
<add input="pro" matchType="IsFile" negate="True"/>
</conditions>-->
<action type="Rewrite" url="app.js"/>
</rule>
<rule name="LogFile" patternSyntax="ECMAScript">
<match url="socket.io"/>
<action type="Rewrite" url="app.js"/>
</rule>
</rules>
</rewrite>
client side js:
var socket = io.connect('http: //localhost', {resource: 'pro/public/socket.io' });
server side js(node):
var http = require('http');
var express = require('express');
var app = express();
var port = process.env.PORT || 3002;
var server = http.createServer(app).listen(port);
io = require('socket.io').listen(server, { resource : '/pro/public/socket.io' });
I get html as expect and the static files are served as well; I just can't get socket.io to work.
SOLUTION:
CHANGE :
io = require('socket.io').listen(server, { resource : '/pro/public/socket.io' });
TO
io = require('socket.io').listen(server, { path : '/pro/public/socket.io' });
THAT is what worked for me I hope it works for you too! :)
Drizo, I think your 404 on the socket.io is a mixture of things, but I'm not a huge expert here! :)
Firstly, the port is missing from your connection string, you'll need this.
Secondly, I'm not 100% sure your socket.io initialisation is correct, though I use namespaces and hadn't heard of resources before, mine looks something like:
var app = require('express')();
var http = require('http').Server(app);
var config = require('./config')();
var server = http.listen(config.server.port, function () {
console.log('listening on *:' + config.server.port);
});
var socketIo = require('socket.io');
var io = socketIo(http);
var namespaceIo = io.of('/namespace');
namespaceIo.on('connection', function (socket) {
//configuration for events follows
socket.on('someEvent', function (input) {
...
}
}
With these lines you can namespace out a socket.io implementaton, and the client can connect with something like
function initSocket(__bool){
if(__bool == true){
io(serverAddress + '/namespace');
if ( !socket ) {
socket = io.connect(serverAddress + '/namespace');
} else {
socket.socket.connect(serverAddress + '/namespace'); // Yep, socket.socket ( 2 times )
}
// now that we have the socket we can bind events to it
bindSocketEvents();
}else{
socket.disconnect();
}
}