Angular 9通用部署难题

问题描述 投票:1回答:1

老实说,我感到很烦,这种情况一直在变化。我在一个更简单的Angular版本中解决了这个问题:

Deploying Angular Universal to Azure

但是现在这已经过时了。不再生成server.js,而是必须修改web.config以指向main.js,这听起来像是一种改进。我已将yaml更新为此:

pool:
  name: Azure Pipelines
steps:
- task: gittools.gitversion.gitversion-task.GitVersion@5
  displayName: GitVersion

- task: NodeTool@0
  displayName: 'Use Node 12.x'
  inputs:
    versionSpec: 12.x

- task: Npm@1
  displayName: 'npm install angular cli'
  inputs:
    command: custom
    verbose: false
    customCommand: 'install @angular/cli -g'

- task: Npm@1
  displayName: 'npm install'
  inputs:
    verbose: false

- task: Npm@1
  displayName: 'npm build'
  inputs:
    command: custom
    verbose: false
    customCommand: 'run build:ssr'

- task: CopyFiles@2
  displayName: 'Copy dist files to staging'
  inputs:
    SourceFolder: '$(Build.SourcesDirectory)/dist'
    TargetFolder: '$(Build.ArtifactStagingDirectory)/app/dist'

- task: AzureRmWebAppDeployment@4
  displayName: 'Azure App Service Deploy: app-name'
  inputs:
    azureSubscription: 'Pay-As-You-Go (f61dc7cf-0ca2-4982-bbe7-9b6527c2962b)'
    WebAppName: r3plica
    packageForLinux: '$(Build.ArtifactStagingDirectory)/app'
    WebConfigParameters: '-Handler iisnode -NodeStartFile dist/app-name/server/main.js -appType node'

就应该这样,但是,当然不是那么简单。现在,如果运行node dist / app-name / server / main.js,我将收到错误消息。返回此:

ReferenceError:在createBase64WorkerFactory上未定义Blob(D:\ home \ site \ wwwroot \ dist \ app-name \ server \ main.js:1:1418371)

所以我做了一些环顾四周,有人建议我安装我已经安装的npm install --save-dev blob-polyfill,然后编辑server.ts文件:

import { Blob } from 'blob-polyfill';

global['Blob'] = Blob;

但是这似乎没有做任何事情。该错误仍然存​​在。有谁知道我该怎么办?


更新

我决定今天再试一次。我运行了npm build:ssr并将服务器和浏览器文件夹复制到本地Web服务器上并运行了node server/main.js,它抱怨说它在dist / my-project / browser / index.html对我有帮助。所以我将整个dist文件夹复制到wwwroot并运行node dist/my-project/server/main.js,它可以正常工作。

所以我更新了管道以执行相同的操作。我验证了它实际上复制了整个dist文件夹,然后将web.config复制到了根目录。我的web.config文件如下所示:

<configuration>
    <system.web>
        <customErrors mode="Off" />
    </system.web>
    <system.webServer>
        <staticContent>
            <remove fileExtension=".woff2" />
            <mimeMap fileExtension=".woff2" mimeType="font/woff2" />
        </staticContent>
        <handlers>
            <!-- Indicates that the server.js file is a node.js site to be handled by the iisnode module -->
            <add name="iisnode" path="dist/my-project/server/main.js" verb="*" modules="iisnode"/>
        </handlers>
        <httpErrors errorMode="Detailed"></httpErrors>
    </system.webServer>
</configuration>

但是当我尝试加载我的网站时,它只会给我一个错误:

HTTP错误403.14-禁止使用

未为请求的URL配置默认文档,并且在服务器上未启用目录浏览。

这真令人讨厌。我看了一下https://example.scm.azurewebsites.net,然后转到调试控制台并键入了node dist/my-project/server/main.js,它返回了:

Node Express服务器正在http://localhost:4000上监听

据我所知,它应该工作正常。没有人知道为什么不是吗?

angular azure angular-universal
1个回答
0
投票

对于面对这些问题的任何人,我都已经解决了,这是我们的解决方案,但事实不多:

  • [[Web.config]节点上下文,我的意思是流程工作目录,在iisnode中工作不同,PWD是目标文件路径,这意味着如果main.js位于dist / server / main.js之内那么相对于浏览器的路径将不是dist / browser /,而是../browser/
  • 考虑到在部署过程中,您将必须根据此新结构生成Web.config

    -Handler iisnode -NodeStartFile dist / server / main.js -appType节点

  • [[server.ts]-考虑到这一点,还要考虑根据您的运行时环境设置浏览器路径,以便在生产环境中也应使用../browser

  • [[server.ts]-订单在server.ts中很重要。 如果您有FACE BROWSER API问题是因为“从'./ main.server导入{AppServerModule};“ 必须放在之后多米诺骨牌声明。

这是server.ts上的一个工作示例,该服务器也根据具有区域设置字符串的url请求使用i18n重定向(现在我也解决了i18n问题,可以告诉你值得阅读这些文档)。 >

/***************************************************************************************************
 * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
 */
import { APP_BASE_HREF } from '@angular/common';
import '@angular/localize/init';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { existsSync } from 'fs';
import { join } from 'path';
import 'zone.js/dist/zone-node';
import { environment } from './environments/environment';

// THIS FIX MOST OF THE COMMON ISSUES WITH SSR:
// FIRST SET THE BROWSER PATH ACCORDING TO RUNTIME ENVIRONMENT
let browserPath;
if (environment.production) {
  browserPath = '../browser';
} else {
  browserPath = 'dist/browser';
}
const enDistFolder = join(process.cwd(), browserPath + '/en');

// Emulate browser APIs
const domino = require('domino');
const fs = require('fs');
const templateA = fs.readFileSync(join(enDistFolder, 'index.html')).toString();

const win = domino.createWindow(templateA);
console.log('win');
win.Object = Object;
console.log('Object');
win.Math = Math;
console.log('Math');

global['window'] = win;
global['document'] = win.document;
global['Event'] = win.Event;
console.log('declared Global Vars....');

/****************************************************/   
/** NOTE THIS: I need to avoid sorting this line */
// USE CTRL+P -> SAVE WITHOUT FORMATTING
import { AppServerModule } from './main.server';
/****************************************************/

// The Express app is exported so that it can be used by serverless Functions.
export function app() {
  const server = express();
  const indexHtml = existsSync(join(browserPath, 'index.original.html')) ? 'index.original.html' : 'index.html';

  // 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', browserPath);

  // Example Express Rest API endpoints
  // server.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('*.*', express.static(browserPath, {
    maxAge: '1y'
  }));

  server.use('/robots.txt', express.static('/en/robots.txt'));
  server.use('/ads.txt', express.static('/en/ads.txt'));

  // THE ORIGINAL Universal Requests handler
  // // // All regular routes use the Universal engine
  // // server.get('*', (req, res) => {
  // //   res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
  // // });

  // OUR i18n REQUESTS HANDLER
  // All regular routes use the Universal engine
  server.get('*', (req, res) => {
    // this is for i18n
    const supportedLocales = ['en', 'es'];
    const defaultLocale = 'es';
    const matches = req.url.match(/^\/([a-z]{2}(?:-[A-Z]{2})?)\//);

    // check if the requested url has a correct format '/locale' and matches any of the supportedLocales
    const locale = (matches && supportedLocales.indexOf(matches[1]) !== -1) ? matches[1] : defaultLocale;

    res.render(`${locale}/index.html`, { req });
  });

  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 './main.server';

我仍然需要对此代码和我们的应用程序进行一些处理(SSR和oauth问题,另一个有趣的话题),但我想分享它,因为它花费了我们近20次部署才能解决这些问题。

最后一句话:如果您是经过8角迁移后来到这里的,我会很乐意为您提供帮助,并给您很好的提示,但老实说,请按照指南进行操作并仔细阅读文档。另外,如果使用的是Azure DevOps管道,则应考虑使用npm缓存。我们的工作量很大,我们现在在每个构建过程中都节省了12分钟以上(那是大量的时间,不是吗?)请随时与我联系。

Juan

© www.soinside.com 2019 - 2024. All rights reserved.