如何在静态方法或自定义中注入 HttpClient class?
How to inject HttpClient in static method or custom class?
我想在静态方法中使用angular HttpClient
或class(在class中它不能定义为构造函数参数)。
我试过类似的方法:
export class SomeNotInjectableService {
static doSomething() {
const injector = Injector.create({
providers: [{provide: HttpClient, deps:[]}]
});
const httpClient: HttpClient = injector.get(HttpClient);
httpClient.request(...); // error Cannot read property 'handle' of undefined
}
}
这是尝试在静态服务方法中手动注入客户端。不起作用。我很好奇如何做到这一点或如何以正常方法但在不是组件的 class 中注入客户端。
我不太确定为什么它不能像您尝试的那样工作(可能是您在创建注入器时遗漏了一些东西),但是如果您使用 'injected' 注入器
它就可以工作
如果您查看抛出错误的源代码,您会发现它提到了请求的处理程序,这在您的示例中似乎为 null。当以 'traditional' 方式提供 HttpClient 时,angular 可能会注册一些内部处理程序,但不是您这样做的方式
// Start with an Observable.of() the initial request, and run the handler (which
// includes all interceptors) inside a concatMap(). This way, the handler runs
// inside an Observable chain, which causes interceptors to be re-run on every
// subscription (this also makes retries re-run the handler, including interceptors).
var /** @type {?} */ events$ = rxjs_operator_concatMap.concatMap.call(rxjs_observable_of.of(req), function (req) { return _this.handler.handle(req); });
解决方法:
app.module.ts
import {Injector} from '@angular/core';
export let InjectorInstance: Injector;
export class AppModule
{
constructor(private injector: Injector)
{
InjectorInstance = this.injector;
}
}
你的静态class/method
import {InjectorInstance} from './app.module';
export class SomeNotInjectableService {
static doSomething()
{
/* const injector = Injector.create({
providers: [{provide: HttpClient, deps:[]}]
});
const httpClient: HttpClient = injector.get(HttpClient);
*/
const httpClient = InjectorInstance.get<HttpClient>(HttpClient);
httpClient.request(...)...
}
}
Stackblitz 上的示例:
https://stackblitz.com/edit/angular-li8b37?file=app%2Fapp.component.ts
如果您没有注射器,也可以跳过注射器。这意味着你自己做 'injecting'。我不建议这样做。如果你真的想使用静态方法(支持适当的服务),请传递所有需要的东西。
我不确定这是否已经很明显,但是此 httpClient 管道中将缺少任何 HTTP 拦截器,因为无法解析它们。
import { HttpClient, HttpXhrBackend } from '@angular/common/http';
const httpClient = new HttpClient(new HttpXhrBackend({ build: () => new XMLHttpRequest() }));
httpClient.get('test').subscribe(r => console.log(r));
或使用您自己创建的注入器(如果您不喜欢传递构造函数参数):
const injector = Injector.create({
providers: [
{ provide: HttpClient, deps: [HttpHandler] },
{ provide: HttpHandler, useValue: new HttpXhrBackend({ build: () => new XMLHttpRequest }) },
],
});
const httpClient: HttpClient = injector.get(HttpClient);
httpClient.get('test').subscribe(r => console.log(r));
基于安德鲁的回答。
如果您想在此 httpClient 管道中使用拦截器,请添加两个重新定义的 类 from angular repo http/src/interceptor.ts 和 http/src/module.ts:
class HttpInterceptorHandler implements HttpHandler {
constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {}
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
return this.interceptor.intercept(req, this.next);
}
}
class HttpInterceptingHandler implements HttpHandler {
private chain: HttpHandler|null = null;
private httpBackend:HttpHandler;
constructor(private injector: Injector) {
this.httpBackend = new HttpXhrBackend({ build: () => new XMLHttpRequest });
}
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
if (this.chain === null) {
const interceptors = this.injector.get(HTTP_INTERCEPTORS, []);
this.chain = interceptors.reduceRight((next, interceptor) => new HttpInterceptorHandler(next,interceptor),this.httpBackend);
}
return this.chain.handle(req);
}
}
拦截器不需要@Injectable装饰器:
class HttpIntersept implements HttpInterceptor{
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log(req.urlWithParams);
return next.handle(req)
}
}
就像安德鲁所说的那样
const injector = Injector.create({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: HttpIntersept, multi: true, deps: []},
{ provide: HTTP_INTERCEPTORS, useClass: HttpIntersept2, multi: true, deps: []},
{ provide: HttpHandler, useClass:HttpInterceptingHandler,deps [Injector,HTTP_INTERCEPTORS]},
{ provide: HttpClient, deps: [HttpHandler] }
],
});
将需求 service/object 作为参数传递有很大帮助。此外,它有助于测试和编码"readability"。以下解决方案适用于您尝试注入的任何类型的对象。而且,至少,你注入它 where/when 需要。调用对象负责注入需要的对象。
export class SomeNotInjectableService {
static doSomething(injected: any) {
httpClient = injected as HttpClient;
if(httpClient) {
httpClient.get(...);
}
}
}
然后在您的调用组件或服务中,像这样使用它
...
export class MyService/*or MyComponent*/{
constructor(private http: HttpClient){}
doTheThing(){
SomeNotInjectableService.doSomething(this.http)/*...subscribe()*/;
}
}
我想在静态方法中使用angular HttpClient
或class(在class中它不能定义为构造函数参数)。
我试过类似的方法:
export class SomeNotInjectableService {
static doSomething() {
const injector = Injector.create({
providers: [{provide: HttpClient, deps:[]}]
});
const httpClient: HttpClient = injector.get(HttpClient);
httpClient.request(...); // error Cannot read property 'handle' of undefined
}
}
这是尝试在静态服务方法中手动注入客户端。不起作用。我很好奇如何做到这一点或如何以正常方法但在不是组件的 class 中注入客户端。
我不太确定为什么它不能像您尝试的那样工作(可能是您在创建注入器时遗漏了一些东西),但是如果您使用 'injected' 注入器
它就可以工作如果您查看抛出错误的源代码,您会发现它提到了请求的处理程序,这在您的示例中似乎为 null。当以 'traditional' 方式提供 HttpClient 时,angular 可能会注册一些内部处理程序,但不是您这样做的方式
// Start with an Observable.of() the initial request, and run the handler (which
// includes all interceptors) inside a concatMap(). This way, the handler runs
// inside an Observable chain, which causes interceptors to be re-run on every
// subscription (this also makes retries re-run the handler, including interceptors).
var /** @type {?} */ events$ = rxjs_operator_concatMap.concatMap.call(rxjs_observable_of.of(req), function (req) { return _this.handler.handle(req); });
解决方法:
app.module.ts
import {Injector} from '@angular/core';
export let InjectorInstance: Injector;
export class AppModule
{
constructor(private injector: Injector)
{
InjectorInstance = this.injector;
}
}
你的静态class/method
import {InjectorInstance} from './app.module';
export class SomeNotInjectableService {
static doSomething()
{
/* const injector = Injector.create({
providers: [{provide: HttpClient, deps:[]}]
});
const httpClient: HttpClient = injector.get(HttpClient);
*/
const httpClient = InjectorInstance.get<HttpClient>(HttpClient);
httpClient.request(...)...
}
}
Stackblitz 上的示例: https://stackblitz.com/edit/angular-li8b37?file=app%2Fapp.component.ts
如果您没有注射器,也可以跳过注射器。这意味着你自己做 'injecting'。我不建议这样做。如果你真的想使用静态方法(支持适当的服务),请传递所有需要的东西。
我不确定这是否已经很明显,但是此 httpClient 管道中将缺少任何 HTTP 拦截器,因为无法解析它们。
import { HttpClient, HttpXhrBackend } from '@angular/common/http';
const httpClient = new HttpClient(new HttpXhrBackend({ build: () => new XMLHttpRequest() }));
httpClient.get('test').subscribe(r => console.log(r));
或使用您自己创建的注入器(如果您不喜欢传递构造函数参数):
const injector = Injector.create({
providers: [
{ provide: HttpClient, deps: [HttpHandler] },
{ provide: HttpHandler, useValue: new HttpXhrBackend({ build: () => new XMLHttpRequest }) },
],
});
const httpClient: HttpClient = injector.get(HttpClient);
httpClient.get('test').subscribe(r => console.log(r));
基于安德鲁的回答。 如果您想在此 httpClient 管道中使用拦截器,请添加两个重新定义的 类 from angular repo http/src/interceptor.ts 和 http/src/module.ts:
class HttpInterceptorHandler implements HttpHandler {
constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {}
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
return this.interceptor.intercept(req, this.next);
}
}
class HttpInterceptingHandler implements HttpHandler {
private chain: HttpHandler|null = null;
private httpBackend:HttpHandler;
constructor(private injector: Injector) {
this.httpBackend = new HttpXhrBackend({ build: () => new XMLHttpRequest });
}
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
if (this.chain === null) {
const interceptors = this.injector.get(HTTP_INTERCEPTORS, []);
this.chain = interceptors.reduceRight((next, interceptor) => new HttpInterceptorHandler(next,interceptor),this.httpBackend);
}
return this.chain.handle(req);
}
}
拦截器不需要@Injectable装饰器:
class HttpIntersept implements HttpInterceptor{
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log(req.urlWithParams);
return next.handle(req)
}
}
就像安德鲁所说的那样
const injector = Injector.create({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: HttpIntersept, multi: true, deps: []},
{ provide: HTTP_INTERCEPTORS, useClass: HttpIntersept2, multi: true, deps: []},
{ provide: HttpHandler, useClass:HttpInterceptingHandler,deps [Injector,HTTP_INTERCEPTORS]},
{ provide: HttpClient, deps: [HttpHandler] }
],
});
将需求 service/object 作为参数传递有很大帮助。此外,它有助于测试和编码"readability"。以下解决方案适用于您尝试注入的任何类型的对象。而且,至少,你注入它 where/when 需要。调用对象负责注入需要的对象。
export class SomeNotInjectableService {
static doSomething(injected: any) {
httpClient = injected as HttpClient;
if(httpClient) {
httpClient.get(...);
}
}
}
然后在您的调用组件或服务中,像这样使用它
...
export class MyService/*or MyComponent*/{
constructor(private http: HttpClient){}
doTheThing(){
SomeNotInjectableService.doSomething(this.http)/*...subscribe()*/;
}
}