实现 ionic 的本机 HTTP 拦截器时出现 Typescript 错误
Typescript error while implementing ionic's native HTTP interceptor
当我遇到一些 CORS 问题时,我正在将已经运行的 Angular 10 网络应用程序修改为离子本机应用程序。由于我无法在 BE 上进行任何更改,因此我遇到了 Ionic 拥有的原生 HTTP 插件。
我在 how to make an interceptor and this article that explains how to implement both HttpClient and Ionic's native HTTP 上遵循了 Angular 的说明,但我 运行 遇到了新问题。
使用文章中的代码,TS 抱怨这一行:
headers: nativeHttpResponse.headers
(property) headers?: HttpHeaders
Type '{ [key: string]: string; }' is missing the following properties from type 'HttpHeaders': headers, normalizedNames, lazyInit, lazyUpdate, and 12 more.ts(2740)
http.d.ts(3406, 9): The expected type comes from property 'headers' which is declared here on type '{ body?: any; headers?: HttpHeaders; status?: number; statusText?: string; url?: string; }'
这里是完整的原生-http.interceptor.ts:
import { Injectable } from "@angular/core";
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent,
HttpResponse,
} from "@angular/common/http";
import { Observable, from } from "rxjs";
import { Platform } from "@ionic/angular";
import { HTTP } from "@ionic-native/http/ngx";
type HttpMethod =
| "get"
| "post"
| "put"
| "patch"
| "head"
| "delete"
| "upload"
| "download";
@Injectable()
export class NativeHttpInterceptor implements HttpInterceptor {
constructor(private nativeHttp: HTTP, private platform: Platform) {}
public intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (!this.platform.is("cordova")) {
return next.handle(request);
}
return from(this.handleNativeRequest(request));
}
private async handleNativeRequest(
request: HttpRequest<any>
): Promise<HttpResponse<any>> {
const headerKeys = request.headers.keys();
const headers = {};
headerKeys.forEach((key) => {
headers[key] = request.headers.get(key);
});
try {
await this.platform.ready();
const method = <HttpMethod>request.method.toLowerCase();
// console.log(‘— Request url’);
// console.log(request.url)
// console.log(‘— Request body’);
// console.log(request.body);
const nativeHttpResponse = await this.nativeHttp.sendRequest(
request.url,
{
method: method,
data: request.body,
headers: headers,
serializer: "json",
}
);
let body;
try {
body = JSON.parse(nativeHttpResponse.data);
} catch (error) {
body = { response: nativeHttpResponse.data };
}
const response = new HttpResponse({
body: body,
status: nativeHttpResponse.status,
headers: nativeHttpResponse.headers, <--------
url: nativeHttpResponse.url,
});
// console.log(‘— Response success’)
// console.log(response);
return Promise.resolve(response);
} catch (error) {
if (!error.status) {
return Promise.reject(error);
}
const response = new HttpResponse({
body: JSON.parse(error.error),
status: error.status,
headers: error.headers,
url: error.url,
});
return Promise.reject(response);
}
}
}
这是我的 app.module.ts
的样子:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { IonicModule } from '@ionic/angular';
import { HTTP } from '@ionic-native/http/ngx';
import { CoreModule } from './core/core.module';
import { SharedModule } from './shared/shared.module';
import { AppComponent } from './app.component';
import { PageNotFoundComponent } from './shared/page-not-found/page-not-found.component';
import { appRoutes } from './app.routes';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
SharedModule,
CoreModule,
RouterModule.forRoot(
appRoutes
),
IonicModule.forRoot()
],
providers: [HTTP],
declarations: [
AppComponent,
PageNotFoundComponent
],
bootstrap: [AppComponent]
})
export class AppModule { }
Andd 这是我的 core.module.ts
(我想使用拦截器的地方)的样子:
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http";
import { NativeHttpInterceptor } from "./service/native-http.interceptor";
import { AuthService } from "./service/auth.service";
import { ApiService } from "./service/api.service";
import { AuthGuardService } from "./service/auth-guard.service";
import { AuthInterceptor } from "./service/auth-interceptor";
import { WindowRef } from "./service/window-ref-service";
@NgModule({
imports: [CommonModule, HttpClientModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: NativeHttpInterceptor,
multi: true,
},
AuthService,
ApiService,
AuthGuardService,
WindowRef,
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true,
},
],
})
export class CoreModule {}
Angular 的 HttpRequest
has an awkwardly designed API. Specifically, its constructor requires an instance of Angular's HttpHeaders
,而不是接受 headers 的 object。
因此,您的情况下正确的代码是
const response = new HttpResponse({
body,
status: nativeHttpResponse.status,
headers: new HttpHeaders(nativeHttpResponse.headers),
url: nativeHttpResponse.url
});
我认为这是糟糕的 API 设计简单明了。它偏离了常用的对应 API,例如 fetch
,增加了耦合度,而且远不是惯用的,同时迫使您编写样板文件。相比之下,Ionic Native 团队采取了正确的方法,将 headers 指定为 object.
当我遇到一些 CORS 问题时,我正在将已经运行的 Angular 10 网络应用程序修改为离子本机应用程序。由于我无法在 BE 上进行任何更改,因此我遇到了 Ionic 拥有的原生 HTTP 插件。
我在 how to make an interceptor and this article that explains how to implement both HttpClient and Ionic's native HTTP 上遵循了 Angular 的说明,但我 运行 遇到了新问题。
使用文章中的代码,TS 抱怨这一行:
headers: nativeHttpResponse.headers
(property) headers?: HttpHeaders
Type '{ [key: string]: string; }' is missing the following properties from type 'HttpHeaders': headers, normalizedNames, lazyInit, lazyUpdate, and 12 more.ts(2740)
http.d.ts(3406, 9): The expected type comes from property 'headers' which is declared here on type '{ body?: any; headers?: HttpHeaders; status?: number; statusText?: string; url?: string; }'
这里是完整的原生-http.interceptor.ts:
import { Injectable } from "@angular/core";
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent,
HttpResponse,
} from "@angular/common/http";
import { Observable, from } from "rxjs";
import { Platform } from "@ionic/angular";
import { HTTP } from "@ionic-native/http/ngx";
type HttpMethod =
| "get"
| "post"
| "put"
| "patch"
| "head"
| "delete"
| "upload"
| "download";
@Injectable()
export class NativeHttpInterceptor implements HttpInterceptor {
constructor(private nativeHttp: HTTP, private platform: Platform) {}
public intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (!this.platform.is("cordova")) {
return next.handle(request);
}
return from(this.handleNativeRequest(request));
}
private async handleNativeRequest(
request: HttpRequest<any>
): Promise<HttpResponse<any>> {
const headerKeys = request.headers.keys();
const headers = {};
headerKeys.forEach((key) => {
headers[key] = request.headers.get(key);
});
try {
await this.platform.ready();
const method = <HttpMethod>request.method.toLowerCase();
// console.log(‘— Request url’);
// console.log(request.url)
// console.log(‘— Request body’);
// console.log(request.body);
const nativeHttpResponse = await this.nativeHttp.sendRequest(
request.url,
{
method: method,
data: request.body,
headers: headers,
serializer: "json",
}
);
let body;
try {
body = JSON.parse(nativeHttpResponse.data);
} catch (error) {
body = { response: nativeHttpResponse.data };
}
const response = new HttpResponse({
body: body,
status: nativeHttpResponse.status,
headers: nativeHttpResponse.headers, <--------
url: nativeHttpResponse.url,
});
// console.log(‘— Response success’)
// console.log(response);
return Promise.resolve(response);
} catch (error) {
if (!error.status) {
return Promise.reject(error);
}
const response = new HttpResponse({
body: JSON.parse(error.error),
status: error.status,
headers: error.headers,
url: error.url,
});
return Promise.reject(response);
}
}
}
这是我的 app.module.ts
的样子:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { IonicModule } from '@ionic/angular';
import { HTTP } from '@ionic-native/http/ngx';
import { CoreModule } from './core/core.module';
import { SharedModule } from './shared/shared.module';
import { AppComponent } from './app.component';
import { PageNotFoundComponent } from './shared/page-not-found/page-not-found.component';
import { appRoutes } from './app.routes';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
SharedModule,
CoreModule,
RouterModule.forRoot(
appRoutes
),
IonicModule.forRoot()
],
providers: [HTTP],
declarations: [
AppComponent,
PageNotFoundComponent
],
bootstrap: [AppComponent]
})
export class AppModule { }
Andd 这是我的 core.module.ts
(我想使用拦截器的地方)的样子:
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http";
import { NativeHttpInterceptor } from "./service/native-http.interceptor";
import { AuthService } from "./service/auth.service";
import { ApiService } from "./service/api.service";
import { AuthGuardService } from "./service/auth-guard.service";
import { AuthInterceptor } from "./service/auth-interceptor";
import { WindowRef } from "./service/window-ref-service";
@NgModule({
imports: [CommonModule, HttpClientModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: NativeHttpInterceptor,
multi: true,
},
AuthService,
ApiService,
AuthGuardService,
WindowRef,
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true,
},
],
})
export class CoreModule {}
Angular 的 HttpRequest
has an awkwardly designed API. Specifically, its constructor requires an instance of Angular's HttpHeaders
,而不是接受 headers 的 object。
因此,您的情况下正确的代码是
const response = new HttpResponse({
body,
status: nativeHttpResponse.status,
headers: new HttpHeaders(nativeHttpResponse.headers),
url: nativeHttpResponse.url
});
我认为这是糟糕的 API 设计简单明了。它偏离了常用的对应 API,例如 fetch
,增加了耦合度,而且远不是惯用的,同时迫使您编写样板文件。相比之下,Ionic Native 团队采取了正确的方法,将 headers 指定为 object.