将Angular Universal部署到IIS

8

我在将Node.js部署到IIS时遇到了问题,在我的web.config文件中:

> <handlers>
>         <add name="iisnode-ng-universal" path="dist/server.js" verb="*" modules="iisnode" />
>     </handlers>

 <rule name="ng-universal" stopProcessing="true">
        <match url=".*" />
        <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{HTTP_HOST}" pattern="test" />
        </conditions>
        <action type="Rewrite" url="dist/server.js" />
    </rule>

我遇到了这个错误:

image

这是我的网页文件树的样子:

web files

我试图给IIS_IUSRSIUSR完全权限,但似乎不起作用。就像这篇帖子建议的那样: iisnode encountered an error when processing the request

4个回答

6

如果您想使用服务器端渲染运行您的Angular应用程序:

1-创建一个新项目:

ng new anguniversal

2-启用服务器端渲染 (SSR)

ng add @nguniversal/express-engine

步骤 1

npm run build:ssr

现在您可以在项目文件夹中看到dist文件夹。

步骤2

步骤3

步骤4

创建并打开Web.config文件(在angular-universal文件夹中),并向其中添加代码。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="iisnode" path="main.js" verb="*" modules="iisnode" />
    </handlers>
    <rewrite>
      <rules>
        <rule name="DynamicContent">
          <match url="/*" />
          <action type="Rewrite" url="main.js"/>
        </rule>
        <rule name="StaticContent" stopProcessing="true">
          <match url="([\S]+[.](jpg|jpeg|gif|css|png|js|ts|cscc|less|ico|html|map|svg))" />
          <action type="None" />
        </rule>
      </rules>
    </rewrite>
    <staticContent>
      <clientCache cacheControlMode="UseMaxAge" />
      <remove fileExtension=".svg" />
      <remove fileExtension=".eot" />
      <remove fileExtension=".ttf" />
      <remove fileExtension=".woff" />
      <remove fileExtension=".woff2" />
      <remove fileExtension=".otf" />
      <mimeMap fileExtension=".ttf" mimeType="application/octet-stream" />
      <mimeMap fileExtension=".svg" mimeType="image/svg+xml"  />
      <mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
      <mimeMap fileExtension=".woff" mimeType="application/x-woff" />
      <mimeMap fileExtension=".woff2" mimeType="application/x-woff" />
      <mimeMap fileExtension=".otf" mimeType="application/otf" />
    </staticContent>
  </system.webServer>
</configuration>

第五步

在服务器上安装 IIS NodeURL Rewrite

第六步

在IIS中添加网站。 enter image description here

第七步

在IIS中,转到为您创建的网站(angular-universal)的应用程序池。然后将.NET CLR版本更改为无托管代码。

enter image description here

完成了,您可以检查结果。


1
这对我有用,只需额外的步骤是为您的网站根目录授予IIS_IUSERS完全控制权限。 - Kyle Barnes

3

将我的Angular 8项目升级到9之后,我的所有东西都被升级到了最新版本,出现了几个问题。

1.- webpack.config.js 文件已更名为 .bak ,因此似乎不再使用 webpack 了。实际上,如果我尝试重新激活 webpack 构建,什么也不会发生,它会创建构建,但脚本不会运行。使用 node server.js 运行时没有任何提示。

2.- 现在 dist 文件夹中没有根目录下的 .js 文件,浏览器和服务器构建的文件夹与以前一样,但即使编辑 web.config 文件以指示 iisnode 运行 server/main.js 而不是 server.js ,无论我做什么都会抛出500错误。

3.- 唯一起作用的是在本地运行。

npm run dev:ssr

这意味着浏览器构建中对视图的引用已经存在,但它会像从根目录请求dist/browser一样运行。

我在angular.json中修改了构建路径和服务器构建路径,以便在./dist文件夹的根目录中创建main.js文件。

angular.json(outputPath已被修改)

architect: {
  "build": {
    "builder": "@angular-devkit/build-angular:browser",
    "options": {
      "outputPath": "dist/browser",
      "index": "src/index.html",
      "main": "src/main.ts",
      "tsConfig": "tsconfig.app.json",
      ...
    },
    ...
  },
  "server": {
    "builder": "@angular-devkit/build-angular:server",
    "options": {
      "outputPath": "dist",
      "main": "server.ts",
      "tsConfig": "tsconfig.server.json"
    },
    ...
  },
  ...
}

好的,问题来了。浏览器生成构建文件夹 ./dist/browser 没有问题,服务器生成构建文件夹 ./dist 并将 main.js 文件直接放入其中,但不知何故它决定删除 ./dist/browser 文件夹,当运行 node main.js (web.config 指向 main.js)时,它无法找到 ./browser/index.html,当然这是因为服务器构建过程成功删除了 browser 文件夹。
一个临时的解决方案是再运行一次第三个构建(即浏览器构建)。
我希望这个问题能够得到解决,但说实话,这并不会对我造成困扰,因为是计算机运行构建,而不是我,但是你知道,如果服务器构建过程不删除浏览器构建文件夹,那么这样做可能会更好、更快。
现在,每次我部署所选的构建时,它都会直接从 AppVeyor 部署到 Azure 应用服务,没有任何问题。 package.json(请注意,在服务器构建之后还有额外的生产构建)。
{
  ...
  "scripts": {
    ...
    "dev:ssr": "ng run Angular-Universal:serve-ssr",
    "serve:ssr": "node dist/main.js",
    "build:ssr": "npm run build:universal && ng run Angular-Universal:build:production",
    "start:ssr": "npm run build:ssr && npm run serve:ssr",
    "build:universal": "ng run Angular-Universal:build:production && ng run Angular-Universal:server:production",
    "prerender": "ng run Angular-Universal:prerender"
  },
  ...
}

请注意,build:ssr脚本执行了两次构建,然后再进行浏览器构建。 还要注意,serve:ssr脚本已被修改为运行dist/main.js 最后,必须修改server.ts文件,以请求从./browser/index.html或./dist/browser/index.html获取视图,具体取决于目标系统的文件夹结构。当部署到Azure应用服务时,main.js请求browser/index.html,但如果在macOS上运行npm run dev:ssrnode main.js的本地环境中提供项目,则请求dist/browser/index.html。无论如何,一个简单的if语句都可以解决这个问题。
server.ts(对app函数的更改)
// The Express app is exported so that it can be used by serverless Functions.
export function app() {
  const server = express();

  let distFolder: string;
  const path1 = join(process.cwd(), 'browser');         // Running from the deployed version (production)
  const path2 = join(process.cwd(), 'dist/browser');    // Running from the source (macOS local development)

  if (fs.existsSync(path1)) {
    distFolder = path1;
  } else if (fs.existsSync(path2)) {
    distFolder = path2;
  } else {
    return null;
  }
  console.log('distFolder:', distFolder);
  const indexHtml = fs.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);

  // Example Express Rest API endpoints
  // server.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;
}

如果您想知道将 web.config 文件复制到 dist 文件夹的根目录会发生什么,那么由于我正在使用 AppVeyor 进行 CI/CD,我已经修改了构建命令以包括 web.config(直接从源代码文件中放入 .7z 文件)。

为什么不包含我的 web.config 呢?

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <httpRuntime enableVersionHeader="false" />
  </system.web>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Strict-Transport-Security" value="max-age=31536000"/>
        <add name="X-Content-Type-Options" value="nosniff" />
        <add name="X-Frame-Options" value="DENY" />
        <add name="X-XSS-Protection" value="1; mode=block" />
        <remove name="X-Powered-By" />
      </customHeaders>
    </httpProtocol>
    <webSocket enabled="false" />
    <handlers>
      <!-- Indicates that the main.js file is a node.js site to be handled by the iisnode module -->
      <add name="iisnode" path="main.js" verb="*" modules="iisnode"/>
    </handlers>
    <rewrite>
      <rules>
        <rule name="HTTP to HTTPS redirect" stopProcessing="true">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="off" ignoreCase="true" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
        </rule>
        <!-- Do not interfere with requests for node-inspector debugging -->
        <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
          <match url="^main.js\/debug[\/]?" />
        </rule>
        <!-- All other URLs are mapped to the node.js site entry point -->
        <rule name="DynamicContent">
          <match url="^(?!.*login).*$"></match>
          <conditions>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
          </conditions>
          <action type="Rewrite" url="main.js"/>
        </rule>
      </rules>
      <outboundRules>
        <rule name="Add Strict-Transport-Security when HTTPS" enabled="true">
          <match serverVariable="RESPONSE_Strict_Transport_Security" pattern=".*" />
          <conditions>
            <add input="{HTTPS}" pattern="on" ignoreCase="true" />
          </conditions>
          <action type="Rewrite" value="max-age=31536000" />
        </rule>
      </outboundRules>
    </rewrite>
    <!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->
    <security>
      <requestFiltering>
        <hiddenSegments>
          <remove segment="bin"/>
        </hiddenSegments>
      </requestFiltering>
    </security>
    <!-- Make sure error responses are left untouched -->
    <httpErrors existingResponse="PassThrough" />

    <!-- Restart the server if any of these files change -->
    <iisnode watchedFiles="web.config;*.js;browser/*.*" />
  </system.webServer>
</configuration>

我希望这篇文章能够帮助到其他人,或者至少对于讨论有所用处。没有人喜欢在升级到9版本后尝试进行Azure应用服务的成功部署时遇到问题。如果有什么可以改进的地方,请告诉我,也许可以通过构建参数来避免浏览器构建文件夹被删除等问题。干杯。


兄弟,我无法感谢你。在过去的两周里,我尝试了很多解决方案,但直到我看到你的帖子才解决了问题。 - Osama

2

你能试试下面的代码吗?它对我有效。

<?xml version="1.0" encoding="utf-8"?>


<configuration>
    <system.webServer>        
      <handlers>
        <add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
      </handlers>
      <rewrite>
        <rules>
            <rule name="DynamicContent">
                 <match url="/*" />
                 <action type="Rewrite" url="server.js"/>
            </rule>
       </rules>
      </rewrite>
    </system.webServer>
  </configuration>

你的 server.js 文件可以放在 dist 文件夹之外。


2
iisnode在处理请求时遇到错误。HRESULT:0x2 HTTP状态:500 HTTP子状态:1002 HTTP原因:内部服务器错误 您收到此HTTP 200响应,因为system.webServer/iisnode/@devErrorsEnabled配置设置为“true”。除了node.exe进程的stdout和stderr日志之外,考虑使用调试和ETW跟踪来进一步诊断问题。错误,请帮我修复它。 - kamalav
2
你好,我遇到了这个错误:HTTP 错误 500.21 - 内部服务器错误 处理程序 "iisnode" 在其模块列表中有一个错误的模块 "iisnode"有任何解决方法吗? - Kunal Kakkad

0

以下流程在我经过两天的试错后成功运行(Angular 8)

  • 假设您已经按照 https://angular.io/guide/universal 进行了操作
    • 假设您已经将 src\tsconfig.server.json 更改为 -- 如果 tsconfig.server.json 已经在 src 文件夹中生成,请使用此注释

{
  "extends": "./tsconfig.app.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app-server",
    "module": "commonjs"
  },
  "files": [
    "src/main.server.ts" -- old
    "main.server.ts" -- use this
  ],
  "angularCompilerOptions": {
    "entryModule": "./app/app.server.module#AppServerModule"
  }
}

website_root:.
│   server.js
│   web.config
│
├───dist
│   └───browser
│           3rdpartylicenses.txt
│           favicon.ico
│           index.html
│           main-es2015.da6c4688eda738c0ae37.js
│           main-es5.da6c4688eda738c0ae37.js
│           polyfills-es2015.0ef207fb7b4761464817.js
│           polyfills-es5.2b9ce34c123ac007a8ba.js
│           runtime-es2015.e8a2810b3b08d6a1b6aa.js
│           runtime-es5.e8a2810b3b08d6a1b6aa.js
│           styles.09e2c710755c8867a460.css
│
└───server
        main.js

部署到 Azure(灵感来自 **r3plica**的答案链接

唯一的区别是增加了一个“复制文件”任务,将服务器文件夹移动到根目录

注意

如果出现问题,请始终使用 node 指向 server.js 运行此应用

node <path to server.js>

这告诉你最基本的东西,你做错了什么。


嘿,你是怎么在构建期间或之后从项目根文件夹复制 server.js 和 web.config 文件到 dist 根文件夹的?我也想这样做,但一直没成功。我试过 angular.json 但它不允许为服务器构建配置资源。此外,在升级到 Angular 9 后,不建议使用 webpack 了,而我以前是通过安装 copy-webpack-plugin 来复制文件的。有什么建议吗? - Sparker73
我是在构建之后完成的。使用Azure Build Pipes。如果您需要构建流程的截图,我可以上传到某个地方。 - Supun De Silva
哦,太感谢了!我们不使用Azure Build Pipes,我们使用AppVeyor进行CI/CD。但是我在整个周末都在努力攻克这个难题,最终从中获得了一些收获。这是一个Angular CLI团队可以避免的hack。我无论如何都会发布我的“解决方案”。 - Sparker73

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接