angular - Angular Universal Transfer State 未按预期工作
问题描述
我有一个问题,我的 API 中的数据没有显示在我的项目的视图源中。我做了一些研究,发现了TransferState,所以我创建了一个类:
import { Injectable } from '@angular/core';
import { TransferState } from '@angular/platform-browser';
import { Observable, from } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class TransferHttpService {
constructor(
private transferHttp: TransferState,
private httpClient: HttpClient
) {}
get(url, options?): Observable<any> {
return this.getData(url, options, () => {
return this.httpClient.get(url, options);
});
}
post(url, body, options?): Observable<any> {
return this.getData(url, options, () => {
return this.httpClient.post(url, body, options);
});
}
delete(url, options?): Observable<any> {
return this.getData(url, options, () => {
return this.httpClient.delete(url, options);
});
}
put(url, body, options?): Observable<any> {
return this.getData(url, options, () => {
return this.httpClient.put(url, body, options);
});
}
getData(url, options, callback: () => Observable<any>): Observable<any> {
const optionsString = options ? JSON.stringify(options) : '';
let key = `${url + optionsString}`;
try {
return this.resolveData(key);
} catch (e) {
console.log('In catch', key);
return callback().pipe(
tap((data) => {
console.log('cache set', key);
this.setCache(key, data);
})
);
}
}
resolveData(key) {
let resultData: any;
if (this.hasKey(key)) {
resultData = this.getFromCache(key);
console.log('got cache', key);
} else {
throw new Error();
}
return from(Promise.resolve(resultData));
}
setCache(key, value) {
this.transferHttp.set(key, value);
}
getFromCache(key) {
return this.transferHttp.get(key, null); // null set as default value
}
hasKey(key) {
return this.transferHttp.hasKey(key);
}
}
在我使用HttpClient的任何服务中,我现在都替换为TransferHttpService。如果您查看该服务,您可以看到一些控制台日志。这很有用,因为当从缓存中获取数据时,我可以在查看页面源中看到它,但如果必须将其插入缓存中,那么它不在页面源中。
当我第一次测试它时,一切似乎都很好。我懂了:
在我的项目控制台中,我可以看到:
这是伟大的。但它似乎并不总是有效。如果我刷新页面,有时它会起作用,但大多数时候它不会:
在这种情况下,导航和页脚都被缓存,但页面没有被缓存,这意味着它不在View Page Source中。
有时情况更糟,所有项目都没有被缓存:
在这种情况下,View Page Source中没有任何内容。
我能做些什么来确保请求总是被缓存?我需要创建一个解析器还是更简单的东西?
解决方案
这是由两个问题引起的。第一个是因为我使用的是contentfuls SDK 并且开箱即用HttpClient
,实际上需要覆盖才能使用TransferHttpService
(请参阅此处)
第二个问题是它TransferHttpService
本身。我把它改成这样:
import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import {
TransferState,
StateKey,
makeStateKey,
} from '@angular/platform-browser';
import { Observable, from } from 'rxjs';
import { tap } from 'rxjs/operators';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
@Injectable({ providedIn: 'root' })
export class TransferHttpService {
constructor(
protected transferState: TransferState,
private httpClient: HttpClient,
@Inject(PLATFORM_ID) private platformId: Object
) {}
request<T>(
method: string,
uri: string | Request,
options?: {
body?: any;
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
reportProgress?: boolean;
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
method,
uri,
options,
(method: string, url: string, options: any) => {
return this.httpClient.request<T>(method, url, options);
}
);
}
/**
* Performs a request with `get` http method.
*/
get<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'get',
url,
options,
(_method: string, url: string, options: any) => {
return this.httpClient.get<T>(url, options);
}
);
}
/**
* Performs a request with `post` http method.
*/
post<T>(
url: string,
body: any,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getPostData<T>(
'post',
url,
body,
options,
// tslint:disable-next-line:no-shadowed-variable
(_method: string, url: string, body: any, options: any) => {
return this.httpClient.post<T>(url, body, options);
}
);
}
/**
* Performs a request with `put` http method.
*/
put<T>(
url: string,
_body: any,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'body';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getPostData<T>(
'put',
url,
_body,
options,
(_method: string, url: string, _body: any, options: any) => {
return this.httpClient.put<T>(url, _body, options);
}
);
}
/**
* Performs a request with `delete` http method.
*/
delete<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'delete',
url,
options,
(_method: string, url: string, options: any) => {
return this.httpClient.delete<T>(url, options);
}
);
}
/**
* Performs a request with `patch` http method.
*/
patch<T>(
url: string,
body: any,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getPostData<T>(
'patch',
url,
body,
options,
// tslint:disable-next-line:no-shadowed-variable
(
_method: string,
url: string,
body: any,
options: any
): Observable<any> => {
return this.httpClient.patch<T>(url, body, options);
}
);
}
/**
* Performs a request with `head` http method.
*/
head<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'head',
url,
options,
(_method: string, url: string, options: any) => {
return this.httpClient.head<T>(url, options);
}
);
}
/**
* Performs a request with `options` http method.
*/
options<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'options',
url,
options,
// tslint:disable-next-line:no-shadowed-variable
(_method: string, url: string, options: any) => {
return this.httpClient.options<T>(url, options);
}
);
}
// tslint:disable-next-line:max-line-length
getData<T>(
method: string,
uri: string | Request,
options: any,
callback: (
method: string,
uri: string | Request,
options: any
) => Observable<any>
): Observable<T> {
let url = uri;
if (typeof uri !== 'string') {
url = uri.url;
}
const tempKey = url + (options ? JSON.stringify(options) : '');
const key = makeStateKey<T>(tempKey);
try {
return this.resolveData<T>(key);
} catch (e) {
//console.log('in catch', key);
return callback(method, uri, options).pipe(
tap((data: T) => {
if (isPlatformBrowser(this.platformId)) {
// Client only code.
// nothing;
}
if (isPlatformServer(this.platformId)) {
//console.log('set cache', key);
this.setCache<T>(key, data);
}
})
);
}
}
private getPostData<T>(
_method: string,
uri: string | Request,
body: any,
options: any,
callback: (
method: string,
uri: string | Request,
body: any,
options: any
) => Observable<any>
): Observable<T> {
let url = uri;
if (typeof uri !== 'string') {
url = uri.url;
}
const tempKey =
url +
(body ? JSON.stringify(body) : '') +
(options ? JSON.stringify(options) : '');
const key = makeStateKey<T>(tempKey);
try {
return this.resolveData<T>(key);
} catch (e) {
return callback(_method, uri, body, options).pipe(
tap((data: T) => {
if (isPlatformBrowser(this.platformId)) {
// Client only code.
// nothing;
}
if (isPlatformServer(this.platformId)) {
this.setCache<T>(key, data);
}
})
);
}
}
private resolveData<T>(key: StateKey<T>): Observable<T> {
const data = this.getFromCache<T>(key);
if (!data) {
throw new Error();
}
if (isPlatformBrowser(this.platformId)) {
//console.log('get cache', key);
// Client only code.
this.transferState.remove(key);
}
if (isPlatformServer(this.platformId)) {
//console.log('we are the server');
// Server only code.
}
return from(Promise.resolve<T>(data));
}
private setCache<T>(key: StateKey<T>, data: T): void {
return this.transferState.set<T>(key, data);
}
private getFromCache<T>(key: StateKey<T>): T {
return this.transferState.get<T>(key, null);
}
}
这是一个更好的版本,现在一切正常
推荐阅读
- python - 如何将字符串转换为“gbk”编码?
- python - 如何将多个文件作为单独的数据框读取并在列上执行计算?
- c# - Xamarin android 在特定时间重新启动完全关闭的应用程序 - Android 版本 10.0
- python - 计算 n 行总和的百分比
- php - 如何直接从视图中使用外观和库
- python - 如何在不知道列名但在数据框中位置的情况下使用 Python 中的 pct_change 计算数据框中 2 列的百分比变化?
- python - 在追加到数据库之前循环遍历数据框并检查行
- go - 语法错误:意外的逗号,期待 go
- jwt - 多用户的 JWT 架构?
- python - “ValueError:matmul:输入操作数 1 的核心维度 0 不匹配……(大小 2 与 1 不同)”