如何使用RxJS和Observables在Angular2中使用HttpModule消耗这样的响应?
编辑:peeskillet在下面给出了一个非常详细的答案.另外,我做了一些进一步挖掘,发现了feature request for the HttpModule
of Angular和StackOverflow question with another approach on how to solve it.
解决方法
XHRConnection
的来源,了解在实施时应该考虑的其他内容.
在尝试实现这一点时,我认为没有任何方法可以直接进入XHR.看起来我们可能需要提供使用Http所涉及的一些组件的自定义实现.我们应该考虑的三个主要组成部分是
>连接
> ConnectionBackend
> Http
Http将ConnectionBackend作为其构造函数的参数.当发出请求时,比如get,Http与ConnectionBackend.createConnection创建连接,并返回Connection的Observable属性(从createConnection返回).在最简单的(简化)视图中,它看起来像这样
class XHRConnection implements Connection { response: Observable<Response>; constructor( request,browserXhr) { this.response = new Observable((observer: Observer<Response>) => { let xhr = browserXhr.create(); let onLoad = (..) => { observer.next(new Response(...)); }; xhr.addEventListener('load',onLoad); }) } } class XHRBackend implements ConnectionBackend { constructor(private browserXhr) {} createConnection(request): XHRConnection { return new XHRConnection(request,this.broswerXhr).response; } } class Http { constructor(private backend: ConnectionBackend) {} get(url,options): Observable<Response> { return this.backend.createConnection(createRequest(url,options)).response; } }
因此,了解这种架构,我们可以尝试实现类似的东西.
对于Connection,这是POC.为简洁而省略了导入,但在大多数情况下,所有内容都可以从@ angular / http导入,Observable / Observer可以从rxjs / {Type}导入.
export class Chunk { data: string; } export class ChunkedXHRConnection implements Connection { request: Request; response: Observable<Response>; readyState: ReadyState; chunks: Observable<Chunk>; constructor(req: Request,browserXHR: BrowserXhr,baseResponSEOptions?: ResponSEOptions) { this.request = req; this.chunks = new Observable<Chunk>((chunkObserver: Observer<Chunk>) => { let _xhr: XMLHttpRequest = browserXHR.build(); let prevIoUsLen = 0; let onProgress = (progress: ProgressEvent) => { let text = _xhr.responseText; text = text.substring(prevIoUsLen); chunkObserver.next({ data: text }); prevIoUsLen += text.length; console.log(`chunk data: ${text}`); }; _xhr.addEventListener('progress',onProgress); _xhr.open(RequestMethod[req.method].toUpperCase(),req.url); _xhr.send(this.request.getBody()); return () => { _xhr.removeEventListener('progress',onProgress); _xhr.abort(); }; }); } }
这是我们刚订阅XHR进展事件.由于XHR.responseText会发出整个连接文本,我们只需子串获取块,然后通过Observer发出每个chuck.
对于XHRBackend,我们有以下(没什么了不起的).同样,一切都可以从@ angular / http导入;
@Injectable() export class ChunkedXHRBackend implements ConnectionBackend { constructor( private _browserXHR: BrowserXhr,private _baseResponSEOptions: ResponSEOptions,private _xsrfStrategy: XSRFStrategy) {} createConnection(request: Request): ChunkedXHRConnection { this._xsrfStrategy.configureRequest(request); return new ChunkedXHRConnection(request,this._browserXHR,this._baseResponSEOptions); } }
对于Http,我们将扩展它,添加一个getChunks方法.如果需要,您可以添加更多方法.
@Injectable() export class ChunkedHttp extends Http { constructor(protected backend: ChunkedXHRBackend,protected defaultOptions: RequestOptions) { super(backend,defaultOptions); } getChunks(url,options?: RequestOptionsArgs): Observable<Chunk> { return this.backend.createConnection( new Request(mergeOptions(this.defaultOptions,options,RequestMethod.Get,url))).chunks; } }
mergeOptions方法可以在Http
source中找到.
现在我们可以为它创建一个模块.用户应该直接使用ChunkedHttp而不是Http.但是因为不试图覆盖Http令牌,如果需要,你仍然可以使用Http.
@NgModule({ imports: [ HttpModule ],providers: [ { provide: ChunkedHttp,useFactory: (backend: ChunkedXHRBackend,options: RequestOptions) => { return new ChunkedHttp(backend,options); },deps: [ ChunkedXHRBackend,RequestOptions ] },ChunkedXHRBackend ] }) export class ChunkedHttpModule { }
我们导入HttpModule,因为它提供了我们需要注入的其他服务,但是如果我们不需要,我们不希望重新实现这些服务.
要测试只是将ChunkedHttpModule导入AppModule.另外要测试我使用了以下组件
@Component({ selector: 'app',encapsulation: ViewEncapsulation.None,template: ` <button (click)="onClick()">Click Me!</button> <h4 *ngFor="let chunk of chunks">{{ chunk }}</h4> `,styleUrls: ['./app.style.css'] }) export class App { chunks: string[] = []; constructor(private http: ChunkedHttp) {} onClick() { this.http.getChunks('http://localhost:8080/api/resource') .subscribe(chunk => this.chunks.push(chunk.data)); } }
我有一个后端端点设置,它每隔半秒就会在10个块中吐出“消息#x”.这就是结果
某处似乎有一个bug.只有九个:-).我认为它与服务器端有关.