Angular HttpClient异步登录POST请求总返回null如何修复优化?
问题根因
- 你对
await的使用错误:subscribe()方法返回的是RxJS的Subscription订阅对象,不是Promise,await不会等待接口请求完成,直接就执行了后面的return returnValue语句,此时请求还没返回,值依然是null - 调用侧逻辑错误:修改后的
login()返回的是Promise<Account | null>,你直接同步判断!= null,Promise对象本身永远不为null,所以不管接口返回什么,都会直接进入if分支,此时loggedInAccount还没被异步回调赋值,自然为null报错
修复方案
方案1:保留async/await写法
首先用RxJS提供的firstValueFrom把Observable转成Promise,修改AccountService的login方法:
import { firstValueFrom } from 'rxjs'; // 首先引入工具方法 public async login(account: Account):Promise<Account | null> { try { const data = await firstValueFrom(this.restLogin(account)); if (data != null) { this.loggedInAccount = data; } return data; } catch (error: any) { alert('Error: Status ' + error.status + ' - ' + error.error); return null; } }
然后修改登录页的调用方法,加上async/await等待异步结果返回:
public async onLoginClick(): Promise<void> { let email = (document.getElementById("txtEmail") as HTMLInputElement).value; let password = (document.getElementById("txtPassword") as HTMLInputElement).value; const loginResult = await this.accountService.login(new Account(email, password)); if (loginResult != null) { this.accountService.loggedInAccount!.posts = this.postService.getAccountPosts(this.accountService.loggedInAccount!.id); this.route.navigate(['/']); } else { (document.getElementById("txtMessage") as HTMLDivElement).innerHTML = "Incorrect email/password!" } }
方案2:更贴合Angular生态的RxJS原生写法(推荐)
不需要转Promise,直接返回Observable给调用侧订阅即可:
AccountService代码:
import { tap, catchError } from 'rxjs/operators'; import { of } from 'rxjs'; public login(account: Account): Observable<Account | null> { return this.restLogin(account).pipe( tap(data => { if (data) this.loggedInAccount = data; }), catchError(error => { alert('Error: Status ' + error.status + ' - ' + error.error); return of(null); }) ); }
登录页调用代码:
public onLoginClick(): void { let email = (document.getElementById("txtEmail") as HTMLInputElement).value; let password = (document.getElementById("txtPassword") as HTMLInputElement).value; this.accountService.login(new Account(email, password)).subscribe(loginResult => { if (loginResult) { loginResult.posts = this.postService.getAccountPosts(loginResult.id); this.route.navigate(['/']); } else { (document.getElementById("txtMessage") as HTMLDivElement).innerHTML = "Incorrect email/password!" } }) }
可优化点
- 不要直接用
document.getElementById操作DOM,改用Angular的模板双向绑定[(ngModel)]或者响应式表单获取输入值,错误提示也可以用模板变量绑定实现,避免原生DOM操作 - 尽量减少非空断言
!的使用,上面的方案2中可以直接用返回的loginResult访问属性,不需要从service里取loggedInAccount,更安全 Content-Type: application/json不需要手动配置,Angular的HttpClient传递JSON请求体时会自动添加该请求头- 接口地址不要硬写死,可以抽成全局环境变量,方便不同环境(开发/测试/生产)切换
- 错误处理不要直接用
alert,可以封装全局的消息提示组件,体验更友好
内容的提问来源于stack exchange,提问作者A. Vreeswijk
相关产品推荐
相关产品推荐

