javascript - ข้อผิดพลาด: สิ่งนี้เกิดจากข้อบกพร่องใน Node.js หรือการใช้งานภายในของ Node.js ไม่ถูกต้อง
ฉันกำลังสร้างกลไกการตรวจสอบสิทธิ์สำหรับบริการของฉัน และในบางช่วงฉันมีปัญหาเกี่ยวกับคุกกี้ คุณสามารถดูเพิ่มเติมได้ที่นี่ ดังนั้นฉันจึงแก้ไขปัญหานี้แล้ว
ปัญหาคือฉันพยายามส่งคุกกี้ผ่านการร้องขอ 2 ครั้ง หน้า Next.js ของฉันส่งการร้องขอไปยัง API ภายในของมันเอง แล้วหลังจากนั้น API ภายในจึงส่งการร้องขอนี้ไปยังแบ็กเอนด์
วิธีแก้ปัญหานี้ง่ายมาก สิ่งที่ฉันต้องทำคือ ตั้งคุกกี้ในฝั่งเซิร์ฟเวอร์และส่งกลับในส่วนหัว นี่คือลักษณะของการไหลของงาน
นี่คือหน้าตาของมัน, จุดสิ้นสุดใน Next.js ฝั่งหน้า. นอกจากข้อมูลในคำตอบ, มันยังได้รับเฮดเดอร์, ที่มีการตั้งคุกกี้ (คำตอบจากฝั่งหลัง) และส่งมันในเฮดเดอร์ของคำตอบ, ที่จะถูกส่งไปยังฝั่งหน้า, ที่ซึ่งคุกกี้จะถูกตั้งค่า:
import { NextApiRequest, NextApiResponse } from "next";
import { AxiosError } from "axios";
import { api } from "../../../api";
export default async (
req: NextApiRequest,
res: NextApiResponse
) => {
try {
const { data, headers } = await api.post('/user/sign-in', req.body)
if (headers["set-cookie"]) {
res.setHeader("Set-Cookie", headers["set-cookie"]);
}
return res.json(data)
} catch (error) {
return res
.status((error as AxiosError).response?.status as number)
.json((error as AxiosError).response?.data);
}
}
และจุดสิ้นสุดบนแบ็กเอนด์:
import { Response as Res } from 'express';
import * as dayjs from 'dayjs';
...
async signIn(@Body() signInUserDto: SignInUserDto, @Response() res: Res) {
const { _at, _rt } = await this.userService.signIn(signInUserDto);
res.cookie('_rt', _rt, {
httpOnly: true,
expires: dayjs().add(7, 'days').toDate()
});
return res.send(_at);
}
และนี่คือปัญหา เพราะคลาส Response ของ express ฉันจึงได้รับคำเตือนนี้อยู่ตลอด:
Error: This is caused by either a bug in Node.js or incorrect usage of Node.js internals.
Please open an issue with this stack trace at https://github.com/nodejs/node/issues
at new NodeError (node:internal/errors:371:5)
at assert (node:internal/assert:14:11)
at ServerResponse.detachSocket (node:_http_server:249:3)
at resOnFinish (node:_http_server:819:7)
at ServerResponse.emit (node:events:390:28)
at onFinish (node:_http_outgoing:830:10)
at callback (node:internal/streams/writable:552:21)
at afterWrite (node:internal/streams/writable:497:5)
at afterWriteTick (node:internal/streams/writable:484:10)
at processTicksAndRejections (node:internal/process/task_queues:82:21)
มันแน่นอนว่ามาจากลักษณะของฟังก์ชัน signIn นี้ เพราะฉันพยายามที่จะ return แบบนี้ - return this.userService.signIn(signInUserDto) - และมันทำงานได้ แต่ในกรณีนี้ฉันไม่สามารถใช้ cookie ได้
ดังนั้น คำถามของฉันคือ - ข้อผิดพลาดนี้คืออะไร? ฉันสามารถเพิกเฉยมันได้ไหม? ถ้าไม่ใช่ แล้วฉันจะแก้ไขมันได้อย่างไร?
ขอบคุณล่วงหน้า!
คำตอบ #1
สรุปย่อ
สุดท้ายฉันก็สามารถแก้ไขข้อผิดพลาดนี้ได้ ก่อนอื่นเลย ตามที่ฉันได้กล่าวไป ข้อมูลของฉันต้องผ่าน 2 API จากแบ็กเอนด์ไปยังฟรอนต์เอนด์ API และต่อเมื่อฟรอนต์เอนด์ API นี้จึงจะส่งคำขอนี้ไปยังฟรอนต์เอนด์ที่แท้จริง
ดังนั้น สิ่งที่ฉันทำคือเพียงส่งคืนโทเค็น 2 ตัว - refresh และ access - เป็นตัวเนื้อหา
@ApiOperation({ summary: 'Resource for sign in user.' })
@ApiResponse({ status: 200, type: TokensDto })
@Post('/sign-in')
async signIn(@Body() signInUserDto: SignInUserDto) {
return this.userService.signIn(signInUserDto);
}
จากนั้น ในฝั่งหน้าเว็บ ผมได้ติดตั้ง cookie และ @types/cookie และในจุดเชื่อมต่อนี้ของฝั่งหน้าเว็บ ใน headers ผมเพียงแค่ทำการแปลงโทเค็นรีเฟรชนี้จาก payload ของ body เป็นแบบ serialized และลบออกจาก body
import { NextApiRequest, NextApiResponse } from "next";
import { AxiosError } from "axios";
import { api } from "../../../api";
import { serialize } from 'cookie';
export default async (
req: NextApiRequest,
res: NextApiResponse
) => {
try {
const { data } = await api.post('/user/sign-in', req.body)
res.setHeader('Set-Cookie', serialize(
'_rt',
data._rt,
{ path: '/', httpOnly: true })
);
delete data._rt
return res.json(data)
} catch (error) {
return res
.status((error as AxiosError).response?.status as number)
.json((error as AxiosError).response?.data);
}
}
และมันทำงานได้อย่างสมบูรณ์แบบ ฉันไม่มีข้อผิดพลาด Node.js นี้อีกต่อไปเนื่องจากการตอบสนองด้วยคลาส Express response และฉันสามารถตั้งค่า cookie ได้
แก้ไข
ฉันได้ปรับปรุงโค้ดนี้ให้ดียิ่งขึ้นโดยใช้ fastify และในทั้งกระบวนการ pipeline คุกกี้จะถูกตั้งค่าในส่วน header ก่อนอื่น ในฝั่ง back-end ติดตั้ง @fastify/cookie และ @nestjs/platform-fastify จากนั้น เพิ่มสิ่งนี้ในไฟล์ที่คุณเริ่มแอป Nest.js ของคุณ:
import {
FastifyAdapter,
NestFastifyApplication
} from '@nestjs/platform-fastify';
import { fastifyCookie } from '@fastify/cookie';
async function bootstrap() {
const PORT = process.env.PORT || 3002;
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter()
);
await app.register(fastifyCookie, {
secret: 'my-secret'
});
สิ่งนี้จะอนุญาตให้คุณใช้ FastifyReply จาก fastify, สิ่งนี้จะกำจัดข้อผิดพลาด Node.js นี้ ในฐานะคลาสการตอบสนอง:
import { FastifyReply } from 'fastify';
@ApiTags('User')
@Controller('user')
export class UserController {
constructor(private userService: UserService) {}
@Post('/sign-in')
async signIn(
@Body() signInUserDto: SignInUserDto,
@Res({ passthrough: true }) res: FastifyReply
) {
const { _at, _rt } = await this.userService.signIn(signInUserDto);
res.setCookie('_rt', _rt);
return res.send(_at);
}
...
และขั้นตอนสุดท้าย บนจุดสิ้นสุดของหน้าแรก ใช้ cookie ทำการแยกวิเคราะห์คู๊กกี้นี้และส่งไปยังหน้าแรก
const { data, headers } = await api.post('/user/sign-in', req.body)
if (headers["set-cookie"]) {
const refreshToken = headers["set-cookie"][0].split('=')[1];
res.setHeader('Set-Cookie', serialize(
'_rt', refreshToken, { path: '/', httpOnly: true })
);
}
return res.json(data)
และนี่คือวิธีที่ดีที่สุดที่ฉันพบ เพราะมันช่วยให้คุณสามารถส่งคุกกี้ในเฮดเดอร์ตลอดทั้งพายป์ไลน์ ไม่ใช่ในบอดี้ แล้วจึงลบมัน และวิธีแก้ปัญหานี้ก็ขจัดข้อผิดพลาดแปลก ๆ ของ Node.js ได้
