You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 06:25:04