2016-07-10 4 views
1

Я пытаюсь написать Угловую службу, которая будет обертывать обычную службу Http и автоматически совершать вызовы аутентификации, если токен-носитель недоступен или недействителен.ReactiveX наблюдаемый, который использует другой наблюдаемый

Вот пример регулярного GET вызова, сделанный с помощью сервиса:

this.httpWithAutomagicAuth 
    .get("http://localhost:5001/books") 
    .map(res => res.json()) 
    .subscribe(
     data => { 
      self.data = data; 
     } 
    ); 

А вот моя очень неаккуратно реализации такого сервиса. Очевидно, что я не использую правильные идиомы ReactiveX в коде.

Как вы можете легко заметить, я пытаюсь построить наблюдаемое (Observable.create), которое использует другое наблюдаемое, возвращаемое this.login(). Я уверен, что существует лучший способ привязки/вложения, наблюдаемый для этого сценария.

Пожалуйста, предлагает усовершенствование, которые сделают код:

  • лаконична
  • легко читать и понимать

    @Injectable() 
    export class HttpWebApiAuthService { 
    
    constructor(private http: Http) { } 
    
    // ... 
    
    public get(url: string, options?: RequestOptionsArgs): Observable<Response> { 
        // TODO this code is for test purpose only (basically, it required to enforce the jwtToken retrieval branch execution) 
        // this.clearJwtToken(); 
    
        if (!this.getJwtToken()) { 
         return Observable.create(
          (result: Observer<Response>) => { 
           this.login() 
            .map(res => res.json()) 
            .subscribe(
             data => { 
              this.saveJwtToken(data.id_token); 
    
              this.executeGet(url, options) 
               .subscribe(authenticationResult => { 
                result.next(authenticationResult); 
               }); 
             }, 
             error => { console.error("Authentication error", error); }, 
             () => { console.info("Authentication complete"); } 
            ); 
           }, 
           error => { console.error("OBSERVABLE error: ", error); }, 
           () => { console.info("OBSERVABLE complete"); 
          } 
         ); 
        } else { 
         return this.executeGet(url, options); 
        } 
    } 
    
    private login() : Observable<Response> { 
        const authBody = 
         { 
          "client_id": "...", 
          "username": "...", 
          "password": "...", 
          // ... 
         }; 
        const headers = new Headers(); 
        headers.append("Content-Type", "application/json"); 
    
        return this.http.post("https://AUTH_URL", JSON.stringify(authBody), { headers: headers }); 
    } 
    
    // ... 
    
    } 
    

ответ

0

Давайте назовем наблюдаемый, который излучает одно значение а, задача, как правило, асинхронная задача. Давайте разложим поток на меньшие задачи:

Во-первых, у вас уже есть задача login(), которая возвращает Observable<Response>. Давайте расширим его, чтобы сохранить jwtToken:

let loginAndSaveToken = login().map(res => res.json()) 
    .do((data: any) => this.saveJwtToken(data.id_token)/*, onError, onComplete if neccessary*/); 

Теперь перейдем к вашей get(url: string, options?: RequestOptionsArgs) функции. Вам нужно сначала войти в систему и сохранить маркер, если у вас еще нет действительного токена, а затем перейдите к вызову http.get, не так ли? Для того, чтобы do this, then that вам понадобилось concat Наблюдатели, или используйте concatMap. Но если у вас уже есть действующий токен, вам не нужно переписывать и просто возвращать http.get. Это эквивалентно do A, then B, где A - это манекен . Я реализую фиктивную задачу Observable.of(1)

public get(url: string, options?: RequestOptionsArgs): Observable<Response> 
{ 
    return (this.tokenValid() ? Observable.of(1) : loginAndSaveToken) 
     .concatMap(x => this.executeGet(url, options)); 
} 

Теперь у вас есть поток в более чистом способе. У него могут быть некоторые предостережения, например: Если ваш второй вызов get приходит до первого успешного входа в систему, он попытается снова войти в систему. Лучше, чтобы он ожидал результата аутентификации, если он выполняется, особенно если ваш сервер аутентификации настроен на отказ от нескольких запросов от конкретного пользователя в окне времени.