kittencup / angular2-ama-cn

angular2 随便问
691 stars 101 forks source link

关于http扩展的问题 #134

Open Naixor opened 8 years ago

Naixor commented 8 years ago

如图,再写一个图片组件时需要用ajax请求图片,希望ajax请求的图片返回arraybuffer,这时Response并不支持直接返回,只能使用response._body,但是这个_body又是私有变量,于是,我如何扩写Response,来实现它目前尚未实现的blob()arrayBuffer()呢?

@Injectable()
class MiResponse extends Response {
    text(): string {
        return super.text();
    }
    json(): any {
        return super.json();
    }
    blob(): any {

    }
    arrayBuffer(): any {

    }
}
Naixor commented 8 years ago

问题描述补充,虽然直接写response._body或者super._body,但是通常这样ts会报错。。。有木有看起来更优雅一点的解决办法呢?

Naixor commented 8 years ago

这个已解决,也贴出来供其他人借鉴: 自己写一个XHRConnection,然后依赖注入进去。

import {Injectable} from 'angular2/core';
import {isPresent, isString, Json} from 'angular2/src/facade/lang';
import {Connection, XHRConnection, Request, ReadyState, BrowserXhr, ResponseOptions, RequestMethod, Headers, ResponseType, ConnectionBackend } from 'angular2/http';
import {isSuccess, isJsObject, getResponseURL} from 'angular2/src/http/http_utils';
import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';

class MiResponseOptions extends ResponseOptions {
    statusText: string;
    type: any;
    merge(options?): MiResponseOptions {
        return new MiResponseOptions({
            body: isPresent(options) && isPresent(options.body) ? options.body : this.body,
            status: isPresent(options) && isPresent(options.status) ? options.status : this.status,
            headers: isPresent(options) && isPresent(options.headers) ? options.headers : this.headers,
            statusText: isPresent(options) && isPresent(options.statusText) ? options.statusText :
                                                                                this.statusText,
            type: isPresent(options) && isPresent(options.type) ? options.type : this.type,
            url: isPresent(options) && isPresent(options.url) ? options.url : this.url,
        });
    }
}

class MiResponse {
    type: ResponseType;
    ok: boolean;
    url: string;
    status: number;
    statusText: string;
    bytesLoaded: number;
    totalBytes: number;
    headers: Headers;
    // TODO: Support ArrayBuffer, JSON, FormData, Blob
    private _body: string | Object;
    constructor(responseOptions: MiResponseOptions) {
        this._body = responseOptions.body;
        this.status = responseOptions.status;
        this.statusText = responseOptions.statusText;
        this.headers = responseOptions.headers;
        this.type = responseOptions.type;
        this.url = responseOptions.url;
    }

    blob(): any { 
        return this._body;
    }
    json(): any {
        var jsonResponse: string | Object;
        if (isJsObject(this._body)) {
        jsonResponse = this._body;
        } else if (isString(this._body)) {
        jsonResponse = Json.parse(<string>this._body);
        }
        return jsonResponse;
    }
    text(): string { return this._body.toString(); }
    arrayBuffer(): any {
        return this._body;
    }
}

class MiXHRConnection implements Connection {
    request: Request;
    response: Observable<MiResponse>;
    readyState: ReadyState;
    private _responseType: any;
    constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: MiResponseOptions) {
        // console.log(baseResponseOptions);
        this._responseType = req.headers.get('responseType');
        if (this._responseType) {
            req.headers.delete('responseType');
        }
        this.request = req;
        this.response = new Observable((responseObserver: Observer<MiResponse>) => {
            let _xhr: XMLHttpRequest = browserXHR.build();
            // 添加blob/arraybuffer responseType到xhr
            switch (this._responseType) {
                case 'blob':
                case 'arraybuffer':
                    _xhr.responseType = this._responseType;
                    break;
                default:
                    break;
            }
            _xhr.open(RequestMethod[req.method].toUpperCase(), req.url);

            let onLoad = () => {
                let body = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText;
                let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders());
                let url = getResponseURL(_xhr);
                let status: number = _xhr.status === 1223 ? 204 : _xhr.status;
                if (status === 0) {
                    status = body ? 200 : 0;
                }
                var responseOptions = new MiResponseOptions({body, status, headers, url});
                if (isPresent(baseResponseOptions)) {
                    responseOptions = baseResponseOptions.merge(responseOptions);
                }
                let response = new MiResponse(responseOptions);
                if (isSuccess(status)) {
                    responseObserver.next(response);
                    responseObserver.complete();
                    return;
                }
                responseObserver.error(response);
            };

            let onError = (err: any) => {
                var responseOptions = new MiResponseOptions({body: err, type: ResponseType.Error});
                if (isPresent(baseResponseOptions)) {
                    responseOptions = baseResponseOptions.merge(responseOptions);
                }
                responseObserver.error(new MiResponse(responseOptions));
            };

            let onProgress = (event: any) => {
                console.log(event);
            }

            if (isPresent(req.headers)) {
                req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));
            }

            _xhr.addEventListener('load', onLoad);
            _xhr.addEventListener('error', onError);
            _xhr.addEventListener('progress', onProgress);

            _xhr.send(this.request.text());

            return () => {
                _xhr.removeEventListener('load', onLoad);
                _xhr.removeEventListener('error', onError);
                _xhr.removeEventListener('progress', onProgress);
                _xhr.abort();
            };
        });
    }
}

@Injectable()
export class MiXHRBackend implements ConnectionBackend {
    constructor(private _browserXHR: BrowserXhr, private _baseResponseOptions: ResponseOptions) {}
    createConnection(request: Request): MiXHRConnection {
        return new MiXHRConnection(request, this._browserXHR, new MiResponseOptions(this._baseResponseOptions));
    }
}

然后调用:

@Component({
            ...
            providers: [
                    HTTP_PROVIDERS,
                    provide(XHRBackend , {
                        useClass: MiXHRBackend
                    }}]
})
class Test {
         constructor(@Inject(Http) http) {
                   let header = new Headers();
                   header.append('Accept', 'image/webp,image/*,*/*;q=0.8');
                   header.append('responseType', 'arraybuffer');
                   http.get( url , { headers: header }).subscribe(response => {
                            console.log(response.blob()); // return _body
                            console.log(response.arrayBuffer()); // return _body
                   });
         }
}