外部ユニバーサルの配置をロードするユニバーサル
42323 ワード
私のポストでLoading external configurations via http using APP_INITIALIZER , 私はクライアント側でHTTPを介して外部の設定をロードしようとしました.このポストでは、私はSSRのためのオプションを調査しています.
拡張StackBlitz Token Test Project , 設定のURLをリモートHTTPに設定し、ローカルにビルドしてサーバーをテストする場合、同じ結果が得られました.The
見分けたい
ファイルの移動
によるとAngular Docs :
まあ、
我々がドキュメンテーションに従うならばServer-side rendering (SSR) with Angular Universal これは、srcフォルダ内のseverを構築し、ビルドプロセスでは、サーバーを切断するまでを歩く.私は、私のサーバーが私の開発ソースコードにある間、古い学校からあまりに卑屈なことがよく眠ることができないとわかります.サーバー上の何かが間違っている場合、私は構築し、テストする必要がありますか?毎回?涼しくない.
私はすぐにポストするかもしれない一つの良いシナリオは、同じビルドを使用して多言語アングルアプリを提供しています.
まずサイズを小さくしましょう
PS : StackBlitzでテストできません
ローカルリクエストは
このポストの範囲を超えて、無関心なことなく、プロキシサーバと負荷分散を通常のサーバーでリバース
ここに、私が設定したAzureウェブサイトの例があります.これは、クラウドホスティングの設定によって、ヘッダーの値がどのようにプレーンなものと異なるかに注目しています.
https://aumet.azurewebsites.net/webinfo
そして、これについてとても良いことは、サーバーを変更することができますか
私たちは別のサーバを持っており、Cofigurationファイルはリモートではありません.
私の非常に長いポストのこれまで読んでくれてありがとう.私は間違いをしたに違いない.
Angular Express Engine Angular Docs X-Forwarded-Proto StackBlitz
Find the final result here StackBlitz
外部リモート構成
拡張StackBlitz Token Test Project , 設定のURLをリモートHTTPに設定し、ローカルにビルドしてサーバーをテストする場合、同じ結果が得られました.The
project resolve
予想通り働いた.唯一の問題は:リモートURLの失敗は、アプリケーションのブロックを意味した.これはリモート設定の落とし穴です.これを解決する一つの方法は次のとおりです.設定にわずかな修正
見分けたい
served
しかし、失敗した場合はUIをブロックしたくない.The project resolve
たとえば、エラーをどうするかを決める必要があります. return this.configService.config$.pipe(
first((n) => n.isServed),
map((n) => {
// if served with error, reroute or notify user, but do not block user
console.log(n.withError); // let's introduce this property
return true;
})
);
インConfigService
私は成功と失敗の区別をするのを止めますserved
. 次に導入することでwithError
プロパティは、失敗するとtrueに設定されます.// after defining withError property in IConfig...
private _createConfig(config: any, withError: boolean): void {
// cast all keys as are
const _config = { ...Config, ...(<IConfig>config) };
// is severd, always
_config.isServed = true;
// with error
_config.withError = withError;
// set static member
ConfigService._config = _config;
// next, always next the subject
this.config.next(config);
}
loadAppConfig(): Observable<boolean> {
return this.http.get(environment.configUrl).pipe(
map((response) => {
// create with no errors
this._createConfig(response, false);
return true;
}),
catchError((error) => {
// if in error, return set fall back from environment
// and create with errors
this._createConfig(Config, true);
return of(false);
})
);
}
This works as expected, however, if the HTTP request fails on server, Angular will attempt to reconnect after rehydration, on client.
外部ローカル設定
ファイルの移動
localdata
フォルダ使用angular.json
資産"assets": [
{
"glob": "*.json",
"input": "configs",
"output": "/localdata"
}
]
設定URLは次のようになりますlocaldata/config.json
. 相対です.によるとAngular Docs :
If you are using one of the
@nguniversal/*-engine
packages (such as@nguniversal/express-engine
), this is taken care for you automatically. You don't need to do anything to make relative URLs work on the server.
まあ、
GET localdata/config.prod.json NetworkError
私は、彼らが何を意味するかと思います.つまり、server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
そして、私はあなたに理由を話します、そして、方法.それから、私は相対的なURLの解決を経験します.サーバーの分離
我々がドキュメンテーションに従うならばServer-side rendering (SSR) with Angular Universal これは、srcフォルダ内のseverを構築し、ビルドプロセスでは、サーバーを切断するまでを歩く.私は、私のサーバーが私の開発ソースコードにある間、古い学校からあまりに卑屈なことがよく眠ることができないとわかります.サーバー上の何かが間違っている場合、私は構築し、テストする必要がありますか?毎回?涼しくない.
私はすぐにポストするかもしれない一つの良いシナリオは、同じビルドを使用して多言語アングルアプリを提供しています.
まずサイズを小さくしましょう
server.ts
角度の医者によって提案されたngExpressEngine
, それをエクスポートし、別のエクスプレスアプリケーションを作成します.// server.ts
// export the ngExpressEngine
export const AppEngine = ngExpressEngine({
bootstrap: AppServerModule
});
SSRをビルドするには以下を使用しますangular.json
設定// ... angular.json
"architect": {
// ...
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
// choose the output path where the main.js will end up
"outputPath": "./host/server",
"main": "server.ts",
"tsConfig": "tsconfig.server.json"
},
"configurations": {
"production": {
// don't delete because there will be other files
"deleteOutputPath": false
// ...
}
}
}
}
The main.js
生成されますoutputPath
, そこにサーバーを作りましょうAppEngine
.// host/server.js
const express = require('express');
// express app
var app = express();
// setup express
require('./server/express')(app);
// setup routes
require('./server/routes')(app);
// other stuff is up to you
// listen
var port = process.env.PORT || 1212;
app.listen(port, function (err) {
console.log('started to listen to port: ' + port);
if (err) {
console.log(err);
return;
}
});
Expressのモジュールは基本です、あなたはそれを見てみることができますStackBlitz . The routes.js
料理が起こるところ__dirname
正確な道をたどるconst express = require('express');
// ngExpressEngine from compiled main.js
const ssr = require('./main');
// setup the routes
module.exports = function (app) {
// set engine, we called it AppEngine in server.ts
app.engine('html', ssr.AppEngine);
// set view engine
app.set('view engine', 'html');
// set views directory
app.set('views', '../client');
// expose the configs path as localdata (or whatever you choose to name it)
app.use('/localdata', express.static('../localdata', { fallthrough: false }));
// expose client folder
app.use(express.static('../client'));
// now THIS
app.get('/*', (req, res) => {
// point to your index.html
res.render(`../client/index.html`, {
req, // pass request
res, // pass response
// here, we can provide things for ssr
});
});
};
インres.render
, 戻ってきたresponse
and request
ちょうど私は角でそれらを使用したい場合.(珍しいことですが).それで、それは理由です、そして、方法.ローカルリクエストの絶対URLを指定する
ローカルリクエストは
localdata/config.prod.json
. それを修正するには、サーバURLによってprependedしなければなりません.の最終結果ConfigService
次のようになります. loadAppConfig(): Observable<boolean> {
// fix url first if its on server
let url = environment.configUrl;
if (serverUrlExsits) {
url = serverUrl + url;
}
return this.http.get(url).pipe(
// ... etc
);
}
サーバー上のURLはREQUEST
インジェクショントークンdocumented on NPM packages .// change ConfigService
// for this line to work, install @types/express
import { Request } from 'express';
import { REQUEST } from '@nguniversal/express-engine/tokens';
@Injectable()
export class RequestService {
// make it Optional to work on browser platform as well
constructor(@Optional() @Inject(REQUEST) private request: Request) {}
}
loadAppConfig(): Observable<boolean> {
// fix url first if its on server
let url = environment.configUrl;
if (this.request) {
// on ssr get a full url of current server
url = `${this.request.protocol}://${this.request.get('host')}/${url}`;
}
// ... etc
}
}
すでに提供されてreq
にres.render
これは十分です.しかし、それは醜く見えます.HTTPインターセプターを作成できますlocaldata
他のローカルデータを使用するには.でもまず逆プロキシの奇妙な場合
このポストの範囲を超えて、無関心なことなく、プロキシサーバと負荷分散を通常のサーバーでリバース
https
into http
, and real.host.com
into localhost
. 使用して固定した後者req.get('host')
はheader
. プロトコルを修正するには、別のヘッダ値にアクセスします.x-forwarded-proto
. ここに、私が設定したAzureウェブサイトの例があります.これは、クラウドホスティングの設定によって、ヘッダーの値がどのようにプレーンなものと異なるかに注目しています.
https://aumet.azurewebsites.net/webinfo
{
"request": {
"headers": {
"host": "aumet.azurewebsites.net",
"disguised-host": "aumet.azurewebsites.net",
"x-original-url": "/webinfo",
"x-forwarded-for": "client-ip-address-here",
"x-forwarded-proto": "https"
},
// on other servers this could be localhost
"hostname": "aumet.azurewebsites.net",
"path": "/webinfo",
// don't read this value
"protocol": "http",
}
}
しかし、私は私の角度のアプリに戻る前に、懸念の分離について強迫観念されて、これは角度の問題ではないので、それはアプリに属してはならない.私はむしろ正しいURLを設定し、それを提供したい.このように:// in host/server/routes.js
// change the final get
app.get('/*', (req, res) => {
// fix and provide actual url
let proto = req.protocol;
if (req.headers && req.headers['x-forwarded-proto']) {
// use this instead
proto = req.headers['x-forwarded-proto'].toString();
}
// also, always use req.get('host')
const url = `${proto}://${req.get('host')}`;
res.render(`../client/index.html`, {
req,
res,
// here, provide it
providers: [
{
provide: 'serverUrl',
useValue: url,
},
],
});
});
私たちの角度のアプリに戻るには、適切なHTTPインターセプターを作成するlocaldata
呼び出し// Angular inteceptor
@Injectable()
export class LocalInterceptor implements HttpInterceptor {
constructor(
// inject our serverURL
@Optional() @Inject('serverUrl') private serverUrl: string
) {}
intercept(req: HttpRequest<any>,next: HttpHandler): Observable<HttpEvent<any>> {
// if request does not have 'localdata' ignore
if (req.url.indexOf('localdata') < 0) {
return next.handle(req);
}
let url = req.url;
if (this.serverUrl) {
// use the serverUrl if it exists
url = `${this.serverUrl}/${req.url}`;
}
const adjustedReq = req.clone({ url: url });
return next.handle(adjustedReq);
}
}
HttpIntereptorを提供するAppModule
// app.module.ts
providers: [
{
provide: APP_INITIALIZER,
useFactory: configFactory,
multi: true,
deps: [ConfigService],
},
// provide http interceptor here
{
provide: HTTP_INTERCEPTORS,
useClass: LocalInterceptor,
multi: true,
},
],
クリーンアップConfigService
任意の参照から我々のサーバーに.建築試験所そして、これについてとても良いことは、サーバーを変更することができますか
config.prod.json
サーバーを再起動せずに、他の環境、およびサーバーを汚染する心配しないでください.現在、私はよりよく眠ることができます.サーバへの設定の提供
私たちは別のサーバを持っており、Cofigurationファイルはリモートではありません.
ConfigService
?// host/server/routes.js
// require the json file sitting in localdata
const localConfig = require('../localdata/config.prod.json');
// setup the routes
module.exports = function (app) {
// ...
res.render(`../client/index.html`, {
req,
res,
// also provide the localConfig
providers: [
{
provide: 'localConfig',
useValue: localConfig
}
// though don't lose the serverUrl, it's quite handy
]
});
});
};
インConfigService
constructor(
private http: HttpClient,
// optional injector for localConfig
@Optional() @Inject('localConfig') private localConfig: IConfig
) {}
loadAppConfig(): Observable<boolean> {
// if on server, grab config without HTTP call
if (this.localConfig) {
this._createConfig(this.localConfig, true);
return of(true);
}
return this.http.get(environment.configUrl).pipe(
// ...
);
}
これは、サーバーの設定を取得するための最速のエラー最小の方法です.しかし、それは一部のためのoverkillであるかもしれません.力はあなたと共にありますように.私の非常に長いポストのこれまで読んでくれてありがとう.私は間違いをしたに違いない.
資源
Reference
この問題について(外部ユニバーサルの配置をロードするユニバーサル), 我々は、より多くの情報をここで見つけました https://dev.to/ayyash/loading-external-configurations-in-angular-universal-4di1テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol