webpack-dev-middleware with HMR behind reverse proxy in IIS - iis

I'm trying to setup webpack with Hot Module Replacement and reaching it through a reverse proxy in IIS using the Rewrite module
My simple rewrite rule:
<rewrite>
<rules>
<clear />
<rule name="ReverseProxyTEST" stopProcessing="true">
<match url="^test/(.*)" />
<action type="Rewrite" url="http://127.0.0.1:8080/{R:1}" logRewrittenUrl="true" />
</rule>
</rules>
</rewrite>
i.e. rewriting localhost/test/__webpack_hmr -> localhost:8080/__webpack_hmr
And my webpack config:
var webpack = require('webpack');
var express = require('express');
var config = {
devtool: 'cheap-module-eval-source-map',
entry: [
'webpack-hot-middleware/client?path=http://localhost/test/__webpack_hmr',
'./src/index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
...
}
var app = express();
var compiler = webpack(config);
app.use(require('webpack-dev-middleware')(compiler, {
publicPath: config.output.publicPath
}));
I'm running the webpack server on port 8080 and if I access the site at http://localhost:8080 or manually enter http://localhost:8080/__webpack_hmr in the browser everything works fine.
But if I'm trying to access the site through http://localhost/test or manually enter http://localhost/test/__webpack_hmr in the browser the /test request works as expected but the http://localhost/test/__webpack_hmr request is getting through and remains open but no data is streamed back.
I've verified that the reverse proxy is working. Is there some options in webpack I need to use in order to get this to work?

Related

http request redirect issue with express-js and cloudflare

I am build an express js server app for example as following
const express = require('express')
const app = express()
const port = 7000
const myUrl = new URL('https://www.google.com/pathname')
app.all('*',(req, res) => {
let { query:{ redirect } } = req;
if( redirect === true ){
res.redirect(myUrl)
} else {
res.json({hi:5})
}
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
I made a redirection function to take some users to another domain,
when I test, the redirecting activity into localhost is working fine but when I work with production mode the redirecting activity is not working properly, for example:
On HTTP request is requested from localhost, the user is redirected from http://localhost:3003/test to https://www.google.com/pathname, and this is what I want but,
On HTTP request is requested from production mode, the user is redirected from https://my-domain/test to https://my-domain.com/pathname, and this is not what I want
FYI
my domain is connected to Cloudflare
My web.config
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="http://web.mydomain.com/*" />
<action type="Rewrite" url="http://localhost:7000/{R:1}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>

Angular 9 universal deploy to azure with web.config file

have created an Asp.NET Core Web Application with Angular 9. I have added Angular Universal to my application. Now I need to publish my application into the Azure app service.
I have used the following command to build an application,
npm run build:ssr
After building angular universal application it will give two folder structures under the dist folder.
dist/{app-name}/browser
dist/{app-name}/server
It's working fine locally. Now I need to publish the application in the Azure app service. So far I have searched a lot of things and integrated them. But unfortunately, I did not find any solution to work for the last couple of days.
Here is my web.config file
<?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>
<rule name="DigitalTalksAngulartest" 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="/digitaltalkstest/" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough"/>
</system.webServer>
</configuration>
server.ts file
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '#nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '#angular/common';
import { existsSync } from 'fs';
// The Express app is exported so that it can be used by serverless Functions.
export function app() {
const server = express();
let distFolder = join(process.cwd(),'dist/#abb/customer-platform/browser');
const indexHtml = 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);
const MockBrowser = require('mock-browser').mocks.MockBrowser;
const mock = new MockBrowser();
const win = mock.getWindow();
global['window'] = win;
global['document'] = mock.getDocument();
global['location'] = mock.getLocation();
global['navigator'] = mock.getNavigator();
global['history'] = mock.getHistory();
global['localStorage'] = mock.getLocalStorage();
global['sessionStorage'] = mock.getSessionStorage();
global['object'] = win.object;
global['HTMLElement'] = win.HTMLElement;
global['Event'] = win.Event;
global['Event']['prototype'] = win.Event.prototype;
global.Buffer = global.Buffer || require('buffer').Buffer;
global.btoa = function (str) {
return Buffer.from(str, 'binary').toString('base64');
};
global.atob = function (b64Encoded) {
return Buffer.from(b64Encoded, 'base64').toString('binary');
};
// Example Express Rest API endpoints
// app.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;
}
function run() {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
currently, the normal angular application resides in site/wwwroot/projec-name
Inside this path, all my build contents reside. Build content consists of web.config, index.html, and all other files.
Any help is appreciated.

Route error in Angular 6/Express

I'm developping a nodeJS/Angular 6/Express app.
There's only 1 route in express for the "back-end" and many routes for angular.
I have no problems to run that locally but when I try to deploy it on Azure, Angular routes works fine but not back-end routes (which redirect me to the root url).
I think Angular is taking priority on back-end routes.
Here are some files :
server.js :
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const fetch = require('node-fetch');
const publicweb = './dist/forms';
const app = express();
app.use(express.static(publicweb));
app.use('/api/test', (req, res) => {
res.send("test");
});
app.get('*', (req, res) => {
res.sendFile('index.html', { root: publicweb });
});
const port = '1337';
app.listen(port, () => console.log(`API running on localhost:${port}`));
web.config :
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="server.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="Express.js URIs">
<match url="api/*" />
<action type="Rewrite" url="server.js" />
</rule>
<rule name="Angular" 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>
package.json :
"start" : "node src/server.js"
app-routing.module.ts :
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { RetailComponent } from './retail/form/retail.component';
const routes: Routes = [
{
path: '',
redirectTo: '/',
pathMatch: 'full'
},
{
path: 'retail',
component: RetailComponent
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
When I deploy that app on Azure, and I try to access /api/test
My build definition in Azure
Many thanks for your answer!
I think all your get request are mapping to this piece of code
app.get('*', (req, res) => {
enter code here`res.sendFile('index.html', { root: publicweb });
});
that is why you are getting index.html in your queries to your server.
And app.use() is to be used for applying middleware so after middleware work is over the app.get() method gets executed and so you get index.html

reactjs, webpack 3+, babel, with IIS 8+ - Where do I point IIS to serve up the dist folder for deployment?

I have constructed a website using a react/webpack dev-server. I want to deploy the site to the web server now. I have cloned the project from github to the server. I have created a new website and app pool in iis and set the basic path to the website folder where my index.html file is. I run the npm start command which concurrently launches the dev server (port 8081) and the node.js server (port 5000) .... I can see the dev server with no issue when I go to localhost:8081 but when I go to localhost:8080 the page times out and displays a
Bad Request - Invalid Hostname
HTTP Error 400. The request hostname is invalid.
error message. Can someone tell me whats wrong with my web.config file and why IIS won't proxy the node server and dist folder? (I am using url rewrite)
Here is my webpack.config (located in root folder):
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:8081',
'webpack/hot/only-dev-server',
'./src/index.js'
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff2?|ttf|svg|eot)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file-loader',
}
],
},
devServer: {
historyApiFallback: true,
hot: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
]
};
Here is the server.js (located in /server/core/ folder):
const webpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');
const config = require('../../webpack.config.js');
const options = {
contentBase: './dist',
hot: true
};
var app = require('express')();
var cors = require('cors');
var bodyParser = require('body-parser')
webpackDevServer.addDevServerEntrypoints(config, options);
const compiler = webpack(config);
const server = new webpackDevServer(compiler, options);
app.listen(process.env.PORT || 5000, () => {
console.log('dev server listening on port 5000');
});
and finally here is the web.config file (located in the root folder):
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<webSocket enabled="false" />
<handlers>
<add name="iisnode" path="/\server/\core/\server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<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/\core/\server.js"/>
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>
Finally Here is my folder structure:

web.config with angular universal

I want to run angular 4 univeral in Azure Web. I deployed the code, but I have some troubles with web.config (i think so).
The server.js located in dist folder, so I set path in web.config "dist/server.js", but when server.js runs it gives an error:
ENOENT: no such file or directory, open 'D:\home\site\wwwroot\dist\dist\browser\index.html'
If I remove the "dist" from path it will 404. And if I remove "dist" from
const DIST_FOLDER = join(process.cwd(), 'dist'); in server.js
it will give me an error:
ENOENT: no such file or directory, open 'D:\home\site\wwwroot\browser\index.html'
Or double dist, or no dist at all.
The web.config looks like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<webSocket enabled="false" />
<handlers>
<add name="iisnode" path="dist/server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^dist/server.js\/debug[\/]?" />
</rule>
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="dist/server.js"/>
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>
The server.js code:
const PORT = process.env.PORT || 8080;
const DIST_FOLDER = join(process.cwd(), 'dist');
const app = express();
const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString();
const { AppServerModuleNgFactory } = require('main.server');
app.engine('html', (_, options, callback) => {
const opts = { document: template, url: options.req.url };
renderModuleFactory(AppServerModuleNgFactory, opts)
.then(html => callback(null, html));
});
app.set('view engine', 'html');
app.set('views', 'src');
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
app.get('*', (req, res) => {
res.render('index', { req });
});
app.listen(PORT, () => {
console.log(`listening on http://localhost:${PORT}!`);
});
I figured out, instead of process.cwd(), I should use __dirname.
Your server.js is located in "dist" folder. So, please change the following line
const DIST_FOLDER = join(process.cwd(), 'dist');
to
const DIST_FOLDER = process.cwd();

Resources