TypeError:您提供了“未定义”,其中包含一个流

时间:2017-12-16 19:33:47

标签: angular ionic-framework rxjs

我有一个Ionic应用,其user提供商使用signup()方法:

doSignup() {
  // set login to same as email
  this.account.login = this.account.email;
  // Attempt to login in through our User service
  this.user.signup(this.account).subscribe((resp) => {
    this.navCtrl.push(MainPage);
  }, (err) => {
    //console.log('error in signup', err);
    // ^^ results in 'You provided 'undefined' where a stream was expected'
    //this.navCtrl.push(MainPage);

    // Unable to sign up
    let toast = this.toastCtrl.create({
      message: this.signupErrorString,
      duration: 3000,
      position: 'top'
    });
    toast.present();
  });
}

由于某种原因,此代码从不调用成功回调,只调用错误处理程序。如果是这样,会导致您在上面的评论中看到的错误。

我的user.signup()方法如下所示:

signup(accountInfo: any) {
  return this.api.post('register', accountInfo).share();
}

我的Api课程如下:

import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';

/**
 * Api is a generic REST Api handler. Set your API url first.
 */
@Injectable()
export class Api {
  public static API_URL: string = 'http://localhost:8080/api';

  constructor(public http: HttpClient) {
  }

  get(endpoint: string, params?: any, reqOpts?: any) {
    if (!reqOpts) {
      reqOpts = {
        params: new HttpParams()
      };
    }

    // Support easy query params for GET requests
    if (params) {
      reqOpts.params = new HttpParams();
      for (let k in params) {
        reqOpts.params.set(k, params[k]);
      }
    }

    return this.http.get(Api.API_URL + '/' + endpoint, reqOpts);
  }

  post(endpoint: string, body: any, reqOpts?: any) {
    return this.http.post(Api.API_URL + '/' + endpoint, body, reqOpts);
  }

  put(endpoint: string, body: any, reqOpts?: any) {
    return this.http.put(Api.API_URL + '/' + endpoint, body, reqOpts);
  }

  delete(endpoint: string, reqOpts?: any) {
    return this.http.delete(Api.API_URL + '/' + endpoint, reqOpts);
  }

  patch(endpoint: string, body: any, reqOpts?: any) {
    return this.http.put(Api.API_URL + '/' + endpoint, body, reqOpts);
  }
}

我尝试从share()删除user.signup(),然后返回Observable<any>,但这没有帮助。

11 个答案:

答案 0 :(得分:13)

我在使用generator-jhipster-ionic(yo jhipster-ionic give v3.1.2)创建一个新项目时遇到了这个问题,在Matt Raible的(OP)Use Ionic for JHipster to Create Mobile Apps with OIDC Authentication博客文章之后,但是选择了JWT身份验证而不是OIDC。

问题的根源是各种各样的问题。

在使用livereload服务器运行Ionic应用程序时出现问题,CORS问题正在发生,Angular HTTP返回经典HTTP failure response for (unknown url): 0 Unknown Error,其中0是HTTP错误代码。 但是,在当前情况下,Observable级别的错误处理会隐藏该问题。

当您按照Angular's HttpClient #Error Details部分建议,并在HTTP POST调用上添加带有catchError运算符的Observable管道时,您可以获得正确的HTTP错误详细信息。在这种情况下,而不是转到rxjs/util/subscribeToResult.js:71TS source #L80TypeError: You provided 'undefined' where a stream was expected默认错误情况,而是转到rx/util/subscribeToResult.js:23TS source #L34),错误在管道方法中正确处理。

在跟踪Observable调用之后,我发现当前的默认身份验证拦截器,如此模板src/providers/auth/auth-interceptor.ts中所示,捕获HTTP错误401并且对其他人没有任何作用,基本上将它们静音并阻止它们传播。

TL; DR 在JWT案例中,解决方案是简单地移除src/providers/auth/auth-interceptor.ts .catch(...)块,允许错误传播到login.service.ts,并在其中this.authServerProvider.login(credentials).subscribe((data) => { ... }, (err) => { ... })错误回调。

我认为问题和解决方案可能与您的OIDC案例,注册方法和错误回调相同。

[编辑]更多,因为在第一篇帖子评论中提到的首发示例中可以找到相同的.catch代码:ionic-jhipster-starter - auth-interceptor.ts#L31

答案 1 :(得分:3)

在我的情况下,我的回归是空的:

if (...)
   return; // problem here

要修复,我返回了观察对象:

if (...)
   return req.next();

答案 2 :(得分:1)

我确实遇到了这个问题,在我的情况下responseType假设是text(因为设计的API终点)而非默认json

import { HttpClient } from '@angular/common/http';

在:

getToken(tokenCommand) {
    return this.http.post(API_BASE_URL + 'user/token', tokenCommand);
}

修好后:

getToken(tokenCommand) {
    return this.http.post(API_BASE_URL + 'user/token', tokenCommand
                                                     , { responseType: 'text' });
}

我认为此错误消息过于笼统,如果开发人员可以提供更多详细信息/有用的错误消息,那就太好了。感谢。

答案 3 :(得分:1)

对我来说,我订阅了 takeUntil 这样的订阅:

this.route.queryParams
  .pipe(takeUntil(this.ngUnsubscribe))
  .subscribe(async(queryParams) => {
  });

我忘记用可观察对象初始化 this.ngUnsubscribe。当我在组件 (ngUnsubscribe = new Subject()) 中将该值设置为 observable 时,错误消失了。

答案 4 :(得分:0)

这里解决了正确的方法:)

尝试使用JSON Web Token对用户进行身份验证时,我一直面临这个问题。就我而言,它与身份验证拦截器有关。

发送对用户进行身份验证的请求并不需要提供令牌,因为它尚不存在。

检查你的拦截器是否包括:

if (req.headers.get('No-Auth') == "True")
            return next.handle(req.clone());

并且您提供了{'No-Auth':'True'}标题的请求,如下所示:

  authenticateUser(user): Observable<any> {
    const headers = new HttpHeaders({'No-Auth':'True'});
    headers.append('Content-Type', 'application/json');
    return this.httpClient.post(`${this.apiEndpoint}/auth/authenticate`, user, {headers: headers});
  }

答案 5 :(得分:0)

就我而言,该错误是由另一个问题引起的。

我在两个不同的方面提供服务。我写过:

@Injectable({provideIn: 'root'})

,我也在app.module.ts中提供了该模块。因此,如果您遇到此错误,可以在继续操作之前进行检查

答案 6 :(得分:0)

发生在手表模式下的单元测试。 停止构建,再次触发它=错误消失了。

答案 7 :(得分:0)

我有相同的错误,典型的情况是我根据特定条件返回了不同的动作。但是缺少对一种情况的处理,因此我并没有退还任何东西。那时就是这个问题。最好使用默认的return操作来避免此类情况。

...mergeMap((res: FetchWorkflowStatusResponse) => {
   if(this){
      return action1
   } else if(that){
      return action2
   }
}

现在,如果出现thisthat以外的其他值,则将没有任何return语句,并且会引发错误。

答案 8 :(得分:0)

就我而言,我忽略了以NgRx效果返回一个动作。我有一条条件语句,它只在一个条件下返回一个动作。

答案 9 :(得分:0)

就我而言,该错误是由另一个问题引起的。

我在两个不同的方面提供服务。我写过:

@Injectable({provideIn:'root'}) 而且我还在app.module.ts中提供了该模块。因此,如果您遇到此错误,可以在继续操作之前进行检查

答案 10 :(得分:0)

我在 Angular 中使用了 ngrx 来存储令牌。我在interceptor.ts中遇到了此错误。

错误的代码是这样的:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        this._store
            .pipe(
                select(fromSelectors.selectAuth),
            )
            .subscribe((state: fromReducers.AuthState) => {

                if (state.token != null) {
                    const clonedReq = req.clone({
                        headers: req.headers.set('Authorization', 'Bearer ' + state.token)
                    });
                    return next.handle(clonedReq);
                }
                else {
                    this.router.navigate(['login']);
                }
            });
    }

我通过返回流程固定了代码:

 intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        return this._store
            .pipe(
                select(fromSelectors.selectAuth),
                switchMap((state: fromReducers.AuthState) => {

                    if (state.token != null) {
                        const clonedReq = req.clone({
                            headers: req.headers.set('Authorization', 'Bearer ' + state.token)
                        });
                        return next.handle(clonedReq);
                    }
                    else {
                        this.router.navigate(['login']);
                        // return EMPTY stream
                        return of({}) as Observable<HttpEvent<any>>;
                    }
                })
            );

    }