老实说,我感到很烦,这种情况一直在变化。我在一个更简单的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上监听
据我所知,它应该工作正常。没有人知道为什么不是吗?
对于面对这些问题的任何人,我都已经解决了,这是我们的解决方案,但事实不多:
考虑到在部署过程中,您将必须根据此新结构生成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