Angular+NodeJS求职应用集成Stripe支付遇NullInjectorError求助
Angular集成Stripe支付时NullInjectorError问题排查
问题背景
开发一款职位搜索求职应用,前端基于Angular,后端使用NodeJS,集成Stripe实现支付功能时,反复出现NullInjectorError错误,多次修改代码仍未解决,以下是相关代码及报错信息。
后端NodeJS代码
const express = require("express"); const cors = require("cors"); const bodyparser = require("body-parser"); const app = express(); app.use(express.static("public")); app.use(bodyparser.urlencoded({ extended: false })); app.use(bodyparser.json()); app.use(cors({ origin: true, credentials: true })); const stripe = require("stripe")("confidential"); app.post("/checkout", async (req, res, next) => { try { const session = await stripe.checkout.sessions.create({ payment_method_types: ["card"], shipping_address_collection: { allowed_countries: ["IN"], }, line_items: req.body.items.map((amount) => ({ price_data: { currency: "inr", product_data: { name: "Contribution", images: [], }, unit_amount: amount * 100, }, quantity: 1, })), mode: "payment", success_url: "http://localhost:4242/success.html", cancel_url: "http://localhost:4242/cancel.html", }); res.status(200).json(session); } catch (error) { next(error); } }); app.listen(4242, () => console.log("app is running on 4242"));
前端Angular组件代码
import { ChangeDetectorRef, Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core'; import { Job } from './Job'; import { Router } from '@angular/router'; import { HttpClient } from '@angular/common/http'; import { loadStripe } from '@stripe/stripe-js'; @Component({ selector: 'app-job', templateUrl: './job.component.html', styleUrls: ['./job.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class JobComponent implements OnInit, OnDestroy { jobs!: Job[]; constructor(public changeDetectorRef: ChangeDetectorRef, private http: HttpClient) { this.jobs = JSON.parse(localStorage.getItem('jobs') ?? '[]'); } ngOnDestroy(): void { } ngOnInit(): void { } /** * 从API获取职位并存储到本地存储 * @param text 搜索关键词 */ async fetchJobs(text: string): Promise<void> { const url = `https://jsearch.p.rapidapi.com/search?query=${text}&page=1&num_pages=1`; const options = { headers: { 'X-RapidAPI-Key': '5cf3ab5104msh8b2dc4fbca796c7p15d31ejsn73261ac3aeba', 'X-RapidAPI-Host': 'jsearch.p.rapidapi.com' } }; try { const response = await fetch(url, options); const data = await response.json(); const jobs = data.data.map(({ job_title, job_description, job_google_link }: Job) => ({ job_title, job_description, job_google_link })); localStorage.setItem('jobs', JSON.stringify(jobs)); this.ngOnInit(); } catch (error) { console.error('Error:', error); } } number(string: string): number { return Number.parseInt(string) } onContribute(amount: number): void { this.http.post('http://localhost:4242/checkout', { amount: amount }).subscribe(async (res: any) => { let stripe = await loadStripe('Confidential'); stripe?.redirectToCheckout({ sessionId: res.id }) }) } }
报错信息
ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(a)[B -> B -> B]: NullInjectorError: No provider for B! NullInjectorError: R3InjectorError(a)[B -> B -> B]: NullInjectorError: No provider for B! at Ud.get (core.mjs:7599:27) at zd.get (core.mjs:8020:33) at zd.get (core.mjs:8020:33) at zd.get (core.mjs:8020:33) at Yy.get (core.mjs:12179:36) at Mi (core.mjs:3255:39) at Jo (core.mjs:3300:12) at Object.ys (core.mjs:10135:12) at f.ɵfac [as factory] (job.component.ts:19:26) at So (core.mjs:3485:44) at Ne (zone.js:1214:31) at Ne (zone.js:1168:17) at zone.js:1281:17 at U.invokeTask (zone.js:409:31) at Object.onInvokeTask (core.mjs:24300:33) at U.invokeTask (zone.js:408:60) at D.runTask (zone.js:178:47) at C (zone.js:588:35) at I.invokeTask [as invoke] (zone.js:494:21) at ae (zone.js:1664:18)
问题排查及解决方案
1. HttpClient依赖注入缺失
NullInjectorError核心原因是Angular无法找到对应依赖的提供者。组件中注入了HttpClient,但如果未在模块中导入HttpClientModule,就会触发该错误。
解决方法:
在组件所属的模块(如AppModule)的imports数组中添加HttpClientModule:
import { HttpClientModule } from '@angular/common/http'; @NgModule({ imports: [ // 其他模块 HttpClientModule ], // 其他配置 }) export class AppModule { }
2. 手动调用生命周期钩子的错误
fetchJobs方法中手动调用this.ngOnInit()是错误操作,Angular生命周期钩子由框架自动调用,手动调用会导致不可预期的问题,且无法正确更新视图。
解决方法:
直接更新组件的jobs属性,再触发变更检测(因使用了OnPush策略):
async fetchJobs(text: string): Promise<void> { // ... 原有请求代码 try { const response = await fetch(url, options); const data = await response.json(); const jobs = data.data.map(({ job_title, job_description, job_google_link }: Job) => ({ job_title, job_description, job_google_link })); localStorage.setItem('jobs', JSON.stringify(jobs)); // 替换this.ngOnInit()为以下代码 this.jobs = jobs; this.changeDetectorRef.markForCheck(); } catch (error) { console.error('Error:', error); } }
3. 前后端请求参数不匹配
前端onContribute方法传递的参数是{amount: amount},但后端代码中尝试读取req.body.items并调用map方法,会导致Cannot read property 'map' of undefined错误,虽然这不是当前NullInjectorError的直接原因,但会导致支付接口调用失败。
解决方法:
- 前端修改请求参数,传递
items数组:
onContribute(amount: number): void { this.http.post('http://localhost:4242/checkout', { items: [amount] // 改为数组格式 }).subscribe(async (res: any) => { let stripe = await loadStripe('Confidential'); stripe?.redirectToCheckout({ sessionId: res.id }) }) }
- 或者后端修改代码,读取
req.body.amount并构造line_items:
line_items: [{ price_data: { currency: "inr", product_data: { name: "Contribution", images: [], }, unit_amount: req.body.amount * 100, }, quantity: 1, }]
4. 冗余代码清理
组件中导入了Router但未在构造函数中注入,属于冗余代码,可直接删除该import语句,避免混淆。
内容的提问来源于stack exchange,提问作者Ayan Shekhar
相关产品推荐
相关产品推荐

