可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an Ionic app that has a user
provider with a signup()
method:
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();
});
}
For some reason, this code never calls the success callback, only the error handler. When it does, it results in the error you see in the comment above.
My user.signup()
method looks as follows:
signup(accountInfo: any) {
return this.api.post('register', accountInfo).share();
}
My Api
class looks as follows:
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);
}
}
I tried removing share()
from user.signup()
, and returning Observable<any>
, but that doesn't help.
回答1:
I faced this issue when creating a new project using generator-jhipster-ionic (yo jhipster-ionic giving v3.1.2), following Matt Raible's (OP) Use Ionic for JHipster to Create Mobile Apps with OIDC Authentication blog article, but choosing JWT authentication instead of OIDC.
The origin of the issue was a mix of problems.
I had the issue when running a Ionic app with the livereload server, CORS issues happening and Angular HTTP returning the classic HTTP failure response for (unknown url): 0 Unknown Error
, where 0 is the HTTP error code.
However, in the current case the issue was hidden by bad error handling at the Observable level.
When you follow Angular's HttpClient #Error Details section advices, and add an Observable pipe with a catchError operator on the HTTP POST call, you can get the proper HTTP error details. In this case, instead of going down to the rxjs/util/subscribeToResult.js:71
(TS source #L80) TypeError: You provided 'undefined' where a stream was expected
default error case, it goes to rx/util/subscribeToResult.js:23
(TS source #L34), and the error is handled properly in the piped method.
After following the Observable calls, I found that the current default authentication interceptor, as seen in this template src/providers/auth/auth-interceptor.ts catches HTTP error 401 and does nothing for the others, basically muting them and preventing their propagation.
TL;DR In the JWT case, the solution is to simply remove the src/providers/auth/auth-interceptor.ts
.catch(...)
block, allowing error propagation to login.service.ts
, and in its this.authServerProvider.login(credentials).subscribe((data) => { ... }, (err) => { ... })
error callback.
I believe the issue and solution could be the same for your OIDC case, its signup method, and error callback.
[Edit] Even more since the same .catch
code can be found in the starter example mentioned in the first post comments: ionic-jhipster-starter - auth-interceptor.ts#L31
回答2:
In my case I was returing with empty return:
if (...)
return; // problem here
To fix, I returned the observed object:
if (...)
return req.next();
回答3:
I did face this issue and in my case responseType
suppose to be text
(because of designed API end-point) rather than default json
.
import { HttpClient } from '@angular/common/http';
Before:
getToken(tokenCommand) {
return this.http.post(API_BASE_URL + 'user/token', tokenCommand);
}
After fixed:
getToken(tokenCommand) {
return this.http.post(API_BASE_URL + 'user/token', tokenCommand
, { responseType: 'text' });
}
I think this error message is too general and it will be nice if it's developers could provide more detail/helpful error message. Thanks.
回答4:
Resolved here the right way :)
I have been facing this issue when trying to authenticate a user using JSON Web Token. in my case it's related to authentication interceptor.
Sending a request to authenticate a user doesn't have to provide a token since it doesn't exist yet.
Check that your interceptor include this:
if (req.headers.get('No-Auth') == "True")
return next.handle(req.clone());
And that you provide {'No-Auth':'True'}
to your header's request like this:
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:
In my case, the error was caused by a different problem.
I was providing the service in two different points. I had written:
@Injectable({provideIn: 'root'})
and I was also providing the module in the app.module.ts
. So if you encountered this error you can check this before going ahead
回答6:
It happened to me doing unit-test on watch mode.
Stop the build, trigger it again = the error is gone.