2016-04-15 19 views
7

Tôi có một API có cơ chế xác thực dựa trên mã thông báo. Sau khi đăng nhập thành công, tôi lưu trữ hai thẻ trong bộ nhớ cục bộ của trình duyệt - mã thông báo truy cập và làm mới. Mã thông báo truy cập chứa tất cả thông tin cần thiết cần thiết để ủy quyền cho người dùng ở phía máy chủ và có ngày hết hạn. Khi mã thông báo truy cập hết hạn, máy khách có thể yêu cầu mã thông báo truy cập mới sử dụng mã thông báo làm mới và trong phản hồi, mã thông báo sẽ nhận được một cặp mã thông báo mới.Angular2 http retry logic

Trong góc 1.x việc triển khai khá đơn giản và dễ hiểu. Ví dụ chúng ta có thể sử dụng tên lửa đánh chặn:

httpInterceptor.$inject = ['$httpProvider']; 
function httpInterceptor($httpProvider) { 
    $httpProvider.interceptors.push(handleStaleAccessToken); 

    handleStaleAccessToken.$inject = ['$q', '$injector', 'session']; 
    function handleStaleAccessToken($q, $injector, session) { 

    function logoutAndRedirect() { 
     var authenticationRedirect = $injector.get('authenticationRedirect'); 
     session.destroy(); 
     authenticationRedirect.toLoginPage(); 
    } 

    return { 
     responseError: function(rejection) { 
     // Do nothing for non 403 errors 
     if (rejection.status !== 403) { 
      return $q.reject(rejection); 
     } 

     var errorCode = rejection.data.error && rejection.data.error.code; 
     if (errorCode === 'access_token_expired') { 
      var $http = $injector.get('$http'); 

      // Refresh token 
      var params = { refreshToken: session.getRefreshToken() }; 
      return $http.post('/api/auth/refresh', params).then(function(response) { 
      session.setTokens(response.data); 
      // Re try failed http request 
      return $http(rejection.config); 
      }).catch(function(error) { 
      logoutAndRedirect(); 
      return $q.reject(error); 
      }); 
     } else { 
      logoutAndRedirect(); 
     } 

     return $q.reject(rejection); 
     } 
    }; 
    } 
} 

Nhưng làm thế nào để thực hiện logic tương tự ở góc 2 app/rxjs?

Trả lời

7

Điều này có thể được thực hiện một cách minh bạch trong Angular2 bằng cách mở rộng lớp Http và tận dụng các toán tử quan sát như flatMap.

Dưới đây là một số mẫu mã:

if (hasTokenExpired()) { 
    return this.authService 
      .refreshAuthenticationObservable() 
      .flatMap((authenticationResult:AuthenticationResult) => { 
       if (authenticationResult.IsAuthenticated == true) { 
        this.authService.setAuthorizationHeader(request.headers); 
        return this.http.request(url, request); 
       } 
       return Observable.throw(initialError); 
    }); 
} 

Mã này phải được tích hợp vào một lớp tùy chỉnh sub của Http một:

Một cách tiếp cận có thể là để mở rộng đối tượng HTTP để đánh chặn các lỗi:

@Injectable() 
export class CustomHttp extends Http { 
    constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) { 
    super(backend, defaultOptions); 
    } 

    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> { 
    console.log('request...'); 
    return super.request(url, options).catch(res => { 
     // do something 
    });   
    } 

    get(url: string, options?: RequestOptionsArgs): Observable<Response> { 
    console.log('get...'); 
    return super.get(url, options).catch(res => { 
     // do something 
    }); 
    } 
} 

và đăng ký nó như mô tả dưới đây:

bootstrap(AppComponent, [HTTP_PROVIDERS, 
    new Provider(Http, { 
     useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => new CustomHttp(backend, defaultOptions), 
     deps: [XHRBackend, RequestOptions] 
    }) 
]); 

Để biết thêm chi tiết có một cái nhìn tại những câu hỏi này:

+1

aproach của bạn cũng giống như tôi ... Vấn đề duy nhất đối với tôi là yêu cầu song song. Nếu tôi đăng ký 3 yêu cầu http khác nhau ... thì mọi yêu cầu sẽ có cùng mã thông báo. chỉ số đầu tiên sẽ làm mất hiệu lực mã thông báo và 2 yêu cầu http khác sẽ không thành công. Bất kỳ đề xuất? – Michalis

1

tôi phải làm một cái gì đó tương tự trong dự án gần đây của tôi shafihuzaib/cdp-ng-boilerplate và hạ cánh trên câu hỏi này cho câu trả lời của tôi . Tôi không thể đi cho các giải pháp được đề xuất ở trên, vì nó cảm thấy phức tạp và một cái gì đó không mong muốn. Vì vậy, tôi đã trở lại để lại giải pháp của tôi sau khi tôi thực hiện một. Tuy nhiên, sự khác biệt là trong trường hợp của tôi, tôi đã có hai thẻ như vậy.

Vì vậy, mọi yêu cầu cần kiểm tra tính hợp lệ của mã thông báo đều được gọi bên trong chức năng này.

tokenValidatedRequest(func): Observable<any>{ 
    let returnObservable = new Observable(); 

    /** 
    * 1. check for auth token expiry - refresh it, if necessary 
    */ 
    if(parseInt(localStorage.getItem('AUTH_TOKEN_EXPIRY')) < (new Date()).valueOf()){ 
     //auth expired 
     this.refresh().subscribe(res => { 
      //refreshed 
      //this.postAuthSuccess(res); 

      returnObservable = func(); 

     }) 
    } 
    else{ 
     //auth not expired 

     returnObservable = func(); 

    } 

    return returnObservable; 
} 

Điều quan trọng nhất ở đây là func() nên trả lại một Observable, để nó có thể được tiêu thụ cho phù hợp.

makeSomeHttpCall(){ 
    this.tokenValidatedRequest(()=>{ 
     return this.http.post(...); 
    }). subscribe(); 
} 

Điều này có vẻ hơi phức tạp đối với người mới, nhưng tôi chắc chắn nó có hiệu quả hơn một chút.

Trong các liên kết sau, vui lòng bỏ qua các chi tiết không liên quan đến câu hỏi này và tập trung vào việc sử dụng giải pháp được đề xuất.

Actual implementation of tokenValidatedRequest() in my project.

tokenValidatedRequest(func , tqlCheck = false): Observable<any>{ 
    /** 
    * Delegate the actual task. However return an Observable, so as to execute 
    * the callback function only when subscribed to.. 
    */ 
    //return Observable.create(obs => obs = (this.__tokenValidatedRequest(func, tqlCheck))); 

    return this.__tokenValidatedRequest(func, tqlCheck); 
} 
private __tokenValidatedRequest(func, tqlCheck = false): Observable<any>{ 
    let returnObservable = new Observable(); 

    /** 
    * 1. check for auth token expiry - refresh it, if necessary 
    * 2. after step 1 - check for TQL token expiry (if tqlCheck is true) - refresh it, if necessary 
    * 3. 
    */ 
    if(parseInt(localStorage.getItem('AUTH_TOKEN_EXPIRY')) < (new Date()).valueOf()){ 
     //auth expired 
     this.refresh().subscribe(res => { 
      //refreshed 
      this.postAuthSuccess(res); 

      if(tqlCheck && localStorage.getItem("TQL_TOKEN_EXPIRY") && 
        parseInt(localStorage.getItem("TQL_TOKEN_EXPIRY")) < (new Date()).valueOf() 
       ){ 

       this.activateUser().subscribe(res => { 
        //TQL token subscribed 
        returnObservable = func(); 
       }) 
      } 
      else{ 
       // Probably not a TQL request 
       returnObservable = func(); 
      } 
     }) 
    } 
    else{ 
     //auth not expired 

     //check if tql token has expired 
     if(tqlCheck && localStorage.getItem("TQL_TOKEN_EXPIRY") && 
        parseInt(localStorage.getItem("TQL_TOKEN_EXPIRY")) < (new Date()).valueOf() 
       ){ 

       this.activateUser().subscribe(res => { 
        //TQL token subscribed 
        returnObservable = func(); 
       }) 
      } 
      else{ 
       // Probably not a TQL request or none of the tokens expired 
       returnObservable = func(); 
      } 
    } 

    return returnObservable; 
} 

How it is used in other services!

getAllParkingSpaces() : Observable<any> { 
    let query = { 
     Query: { 
      .... 
     } 
    }; 

    return this.authService.tokenValidatedRequest(()=>{ 
     return this.api.post(CONFIG.api.engineUrl + 'devices/parking', query); 
    }, true); 
} 

How I finally subscribe to it!

this.realTimeParkingService.getAllParkingSpaces().subscribe(r => { 
    this.parkingSpaces = r; 
}); 
Các vấn đề liên quan