Deploying Next Js project in IIS - iis

I had a requirement of server side rendering in my project.So, I have developed it using next js and now i got struct in deployment.
I need to deploy my project in iis and dont know how i can achieve that. I tried a lot no luck. It works fine in development mode but not in production.
I tried next export but this is for static pages deployment and
my project uses dynamic pages.
Any help will be appreciated. Thanks in advance.

Try this youtube link it helped me deploy the nextjs app on IIS. Do not forget that you need NodeJS, URLRewrite and IISNode on your server.
You build your nextjs app and deploy it to the IIS folder on the server. You also need the node_modules folder with all the dependencies including express and node.

I have the same problem as you, but so far I have successfully deployed and solved the routing problem, but the only thing that has not been solved is that I cannot redirect to the "NotFound page" (404.js), Maybe we can find a solution together.
This is the current solution :
folder tree :
node_modules
public
pages
index.js
first.js
two.js
_error.js
next.config.js
package.json
package.lock.json
README.md
package.json :
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"prod": "next build && next export"
},
"dependencies": {
"next": "9.3.5",
"react": "16.13.1",
"react-dom": "16.13.1",
}
}
next.config.js :
const data = [
{
path: '/first'
},
{
path: '/two'
}
];
module.exports = {
exportTrailingSlash: true,
exportPathMap: async function () {
//you can get route by fetch
const paths = {
'/': { page: '/' }
};
data.forEach((project) => {
paths[`${project.path}`] = {
page: project.path,
};
});
return paths;
}
}
index.js :
import Link from 'next/link'
import React from 'react';
import Head from 'next/head'
export default function Home(props) {
return (
<div className="container">
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<h1 className="title">
<Link href="/first">
<a>page first</a>
</Link>
<Link href="/two">
<a>page two</a>
</Link>
</h1>
</div>
)
}
export async function getStaticProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
first.js :
import Link from 'next/link'
import Head from 'next/head'
export default function FirstPost() {
return (
<>
<Head>
<title>First</title>
</Head>
{"First page"}
<h2>
<Link href="/">
<a>Back to home</a>
</Link>
</h2>
</>
)
}
export async function getStaticProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
two.js :
import Link from 'next/link'
import Head from 'next/head'
export default function FirstPost() {
return (
<>
<Head>
<title>two </title>
</Head>
{"two page"}
<h2>
<Link href="/">
<a>Back to home</a>
</Link>
</h2>
</>
)
}
export async function getStaticProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
and then run the scripts "prod", you will have /out folder,
just use it as the root directory of IIS,
now test the route :
Enter URL http://{your IP or domain}/ => you will see index.js
Enter URL http://{your IP or domain}/first => you will see first.js
Enter URL http://{your IP or domain}/whatever => you will get 404 error from IIS, and I need the help here!
update the reply,
Finally, I know how to solve it, add web.config and redirect to the 404 errpr page.
note: <action type="Rewrite" url="404/index.html" /> // You can specify the error page here!
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Static Assets" stopProcessing="true">
<match url="([\S]+[.](html|htm|svg|js|css|png|gif|jpg|jpeg))" />
<action type="Rewrite" url="/{R:1}"/>
</rule>
<rule name="ReactRouter 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="404/index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>

Related

.glb file doesn't show in express server

I want to run a simple webserver that shows a .glb file in VR using a-frame.
When I use the "Open in Default Browser" extension in vs code the html below shows without problems, both box and file.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello, WebVR! • A-Frame</title>
<meta name="description" content="Hello, WebVR! • A-Frame">
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
</head>
<body>
<a-scene background="color: #ECECEC">
<a-gltf-model position="4 -10 -100" rotation="210 0 0" src="/EPE_4.glb" shadow="receive: true"></a-gltf-model>
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" shadow></a-box>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow></a-plane>
</a-scene>
</body>
</html>
However, when I run a server providing the html using Express, with the node command, only the box and plane is visible. Note that the .glb is somewhat large at 200 MB.
Below is my app.js and package.js file.
Thank you in advance for any help.
app.js
const app = express();
const path = require('path');
const router = express.Router();
router.get('/',function(req,res){
res.sendFile(path.join(__dirname+'/index.html'));
});
app.use('/', router);
app.listen(process.env.port || 3000);
console.log('Running at Port 3000');
package.js
{
"name": "vr_test2",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node ./bin/www",
"devstart": "SET DEBUG=vr_test:* & nodemon ./bin/www"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1"
}
}
If someone has a similar problem.
Check the browser's console log, mine showed that the webpage couldn't find the .glb file.
I solved this by adding a public folder and serving it to the client by using:
app.use(express.static('public'))

Azure app service diagnostic blob not logging nlog based logs

I want to log nlog generated application logs in app service diagnostic blob [i.e, Application Logging (Blob)
] but only default logs are printed not the nlog based custom logs
but I can print Application Logging (Filesystem) when file target is added to nlog.config. The problem is only with blob.
nlog.config file:
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
throwConfigExceptions="true"
internalLogLevel="info"
internalLogFile="d:\home\LogFiles\temp\internal-nlog-AspNetCore3.txt">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- the targets to write to -->
<targets>
<target xsi:type="Trace" name="String" layout="${level}\: ${logger}[0]${newline} |trace| ${message}${exception:format=tostring}" />
<target xsi:type="Console" name="lifetimeConsole" layout="${level}\: ${logger}[0]${newline} |console| ${message}${exception:format=tostring}" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="lifetimeConsole,String" final="true"/>
</rules>
</nlog>
program.cs file
namespace testapp
{
public class Program
{
public static void Main(string[] args)
{
var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
logger.Debug("init main");
CreateHostBuilder(args).Build().Run();
}
catch (Exception exception)
{
logger.Error(exception, "Stopped program because of exception");
throw;
}
finally
{
NLog.LogManager.Shutdown();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
logging.AddConsole();
logging.AddDebug();
logging.AddAzureWebAppDiagnostics();
})
.UseNLog() // NLog: Setup NLog for Dependency injection
.ConfigureServices(serviceCollection => serviceCollection
.Configure<AzureBlobLoggerOptions>(options =>
{
options.BlobName = "testlog.txt";
}))
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
The Nlog based loggings are not logged in app service diagnostic blob, instead only default logging is printed.
Kindly help to resolve this issue.
Seems that System.Diagnostics.Trace-Target works best for ASP.NET-application and not for ASP.NetCore applications in Azure.
When using AddAzureWebAppDiagnostics it will redirect all output written to Microsoft ILogger to FileSystem or Blob. But any output written to pure NLog Logger-objects will not be redirected.
Maybe the solution is to setup a NLog FileTarget writing to the HOME-directory:
<nlog>
<targets async="true">
<!-- Environment Variable %HOME% matches D:/Home -->
<target type="file" name="appfile" filename="${environment:HOME:cached=true}/logfiles/application/app-${shortdate}-${processid}.txt" />
</targets>
<rules>
<logger name="*" minLevel="Debug" writeTo="appFile" />
</rules>
</nlog>
See also: https://learn.microsoft.com/en-us/azure/app-service/troubleshoot-diagnostic-logs#stream-logs
For including Blob-output then take a look at NLog.Extensions.AzureBlobStorage
Alternative to Blob-output could be ApplicationInsights but it might have different pricing.

Trying to run .net core api which uses swagger from local IIS

I created a .Net core web api application and using swagger .I am trying to create a profile for the application to run it on IIS while run the application from visual studio 2019.
Application folder has full permissions and the app pool /website, both are running under service account which has full admin rights.
I am trying to configure the application to use https to run on a specific port 443.Not sure if i have to make any code changes so that application runs on specific port
If i try to run the application using IIS express with the url https://localhost:portnumber/TestApi/swagger.It works beautifully
If i try to use the profile that is configured for IIS to run the application with the url "https://localhost/TestApi/swagger, then its throwing the error.
Below is my web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="bin\IISSupport\VSIISExeLauncher.exe" arguments="-argFile IISExeLauncherArgs.txt" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false">
<environmentVariables>
<environmentVariable name="ASPNETCORE_HTTPS_PORT" value="44342" />
<environmentVariable name="COMPLUS_ForceENC" value="1" />
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</configuration>
I have my start up.cs file as follows
public class Startup
{
public Startup(IHostingEnvironment env)
{
//Configuration = configuration;
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
ConfigSettingLayoutRenderer.DefaultConfiguration = Configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
services.Configure<IISOptions>(options =>
{
options.AutomaticAuthentication = false;
});
services.AddSwaggerGen(c => c.SwaggerDoc("v1",
new Swashbuckle.AspNetCore.Swagger.Info()
{
Title = "Test APIs",
Description = "REST APIs",
Version = "v1"
})
);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
string swaggerJsonBasePath = string.IsNullOrWhiteSpace(c.RoutePrefix) ? "." : "..";
c.SwaggerEndpoint($"{swaggerJsonBasePath}/swagger/v1/swagger.json", "My API");
});
app.Run(async (context) =>
{
context.Response.Redirect("TestApi/swagger");
await Task.FromResult(0);
});
}
}
and my launchsettings.json as below
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iis": {
"applicationUrl": "https://localhost/TestApi",
"sslPort": 0
},
"iisExpress": {
"applicationUrl": "http://localhost:57086/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"PmDashBoard.Api": {
"commandName": "IIS",
"launchBrowser": true,
"launchUrl": "https://localhost/TestApi",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost/TestApi"
}
}
}
I have .Net core hosting bundle is installed on my machine and latest version of VS2019 installed. I am trying Ctrl + F5 to run the application from VS2019 and getting the below error
HTTP Error 500.19 - Internal Server Error
The requested page cannot be accessed because the related configuration data for the page is invalid.
Detailed Error Information:
Module
IIS Web Core
Notification
BeginRequest
Handler
Not yet determined
Error Code
0x80070003
Config Error
Cannot read configuration file

Azure LetsEncrypt cannot access .well-known/acme-challenge for my function

I am trying to enable SSL for my azure function using the letsencrypt site-extension for azure as described here. I was following the instructions in that wiki and on this website.
However, I get an error when it tries to verify the website.
The error indicates that the acme-challenge page cannot be accessed (404).
This is my web.config under .well-known/:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<clear />
<add name="ACMEStaticFile" path="*" verb="*" modules="StaticFileModule" resourceType="Either" requireAccess="Read" />
</handlers>
<staticContent>
<remove fileExtension="." />
<mimeMap fileExtension="." mimeType="text/plain" />
</staticContent>
</system.webServer>
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</configuration>
Does anyone have any ideas on what could be going wrong?
Here's how you can do that.
In your function app, create a new proxy for the challenge directory (this is required as the challenge will be a http get to /.well-known/acme-challenge/, and per default a function in a function app will only answer on /api/.
You can setup the proxy in the following way.
proxies.json
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"LetsEncryptProxy": {
"matchCondition": {
"route": "/.well-known/acme-challenge/{code}"
},
"backendUri": "http://%WEBSITE_HOSTNAME%/api/letsencrypt/.well-known/acme-challenge/{code}"
}
}
}
The important setting here is the Route Template: /.well-known/acme-challenge/{*rest} this will match all request that goes to the challenge directory.
Please note that proxies are a preview feature and you have to enable it, for it to work.
Reference:
https://github.com/sjkp/letsencrypt-siteextension/wiki/Azure-Functions-Support
https://blog.bitscry.com/2018/07/06/using-lets-encrypt-ssl-certificates-with-azure-functions/
I figured it out:
Similar to KetanChawda-MSFT's answer, except the proxy needs to hit the api endpoint function that you create.
So here's the function:
// https://YOURWEBSITE.azurewebsites.net/api/letsencrypt/{code}
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, string code, TraceWriter log)
{
log.Info($"C# HTTP trigger function processed a request. {code}");
var content = File.ReadAllText(#"D:\home\site\wwwroot\.well-known\acme-challenge\"+code);
var resp = new HttpResponseMessage(HttpStatusCode.OK);
resp.Content = new StringContent(content, System.Text.Encoding.UTF8, "text/plain");
return resp;
}
And here's the proxy that hits that function when letsEncrypt tries to verify the acme challenge:
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"LetsEncryptProxy": {
"matchCondition": {
"route": "/.well-known/acme-challenge/{code}"
},
"backendUri": "http://%WEBSITE_HOSTNAME%/api/letsencrypt/{code}"
}
}
}

react-router deploying non-static

Is it OK to deploy a react-router based webapp to production running under a node.js server? Deploying static files prevents links like server.com/about from working unless index.html is loaded first. One can get around this by using HashRouter but it seems old school to do down that route.
index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import App from './js/App';
import About from './js/About';
import registerServiceWorker from './registerServiceWorker';
import './index.css';
import { BrowserRouter as Router, Route } from 'react-router-dom';
ReactDOM.render(
<Router>
<App>
<Route path='/about' component={About} />
</App>
</Router>
, document.getElementById('root'));
registerServiceWorker();
you can use nginx fall back any route (eg. server.com/about) to the index.html
for example
location ^~ / {
alias /usr/local/theapp/dist;
try_files $uri $uri/ /index.html;
access_log /var/log/nginx/theapp/acces.web.log;
error_log /var/log/nginx/theapp/error.web.log debug;
}
# assets
location ~ ^/.+\..+$ {
try_files $uri =404;
alias /usr/local/theapp/dist;
error_log /var/log/nginx/theapp/error.web.log debug;
}
Not the prettiest solution but resolving to something like this alongside the nginx try_files $uri $uri/ /index.html; does the trick.
class OnLoadHack extends Component {
componentDidMount() {
const { location, match, history } = this.props;
if(process.env.NODE_ENV !== 'development' && !match.isExact){
history.push(location.pathname + location.search)
}
}
render() {
return null
}
}
ReactDOM.render(
<Router>
<App>
<Route path='/' component={OnLoadHack} />
<Route path='/about' component={About} />
</App>
</Router>
, document.getElementById('root'));
registerServiceWorker();

Resources