Json Server发起PUT/PATCH请求返回404,GET请求正常
问题描述
在浏览器端通过组件内输入控件修改从服务端获取的skills: SkillSet[]类型数据,计划通过两个按钮控制是否将变更同步更新到服务端,但saveChanges函数始终无法正常运行。
Skills组件的saveChanges函数代码如下:
saveChanges(): void { this.editMode = false; this.skillsService.saveChanges(this.skills).subscribe(); }
Skills服务的saveChanges函数代码如下:
// Update changes made by the component saveChanges(skills: SkillSet[]): Observable<SkillSet[]> { console.log(skills); // 控制台打印可以证明数组已经携带了编辑后的内容 return this.http.patch<SkillSet[]>( this.skillsUrl, skills, this.httpOptions ); }
点击触发按钮后,控制台返回错误:PATCH http://localhost:5000/skills 404 (Not Found),改用PUT方法请求同样报错。但该URL的GET请求可以正常访问,页面初始加载时能正常获取模板渲染所需数据。
完整相关代码
SkillSet接口定义
export interface SkillSet { id: number; type: string; title: string; description: string; tools: string[]; }
Skills组件完整代码
import { Component, OnInit } from '@angular/core'; import { SkillSet } from 'src/app/interfaces/skill-set'; import { SkillsService } from 'src/app/services/skills.service'; @Component({ selector: 'app-skills', templateUrl: './skills.component.html', styleUrls: ['./skills.component.scss'], }) export class SkillsComponent implements OnInit { login: boolean = true; editMode: boolean = false; skills: SkillSet[] = []; newTool: string = ''; constructor(private skillsService: SkillsService) {} ngOnInit(): void { this.getSkills(); } getSkills() { this.skillsService .getSkills() .subscribe((skills) => (this.skills = skills)); } // 板块编辑功能函数 addTool(skillSet: SkillSet, tool: string) { if (tool) { skillSet.tools.push(tool); } } deleteTool(skillSet: SkillSet, tool: string) { skillSet.tools = skillSet.tools.filter((t) => t !== tool); } // 变更操作菜单 editStart() { this.editMode = true; console.log('Editing skills'); } saveChanges(): void { this.editMode = false; this.skillsService.saveChanges(this.skills).subscribe(); } cancelChanges() { this.editMode = false; this.getSkills(); } }
Skills服务完整代码
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable, of } from 'rxjs'; import { SkillSet } from '../interfaces/skill-set'; @Injectable({ providedIn: 'root', }) export class SkillsService { private skillsUrl = 'http://localhost:5000/skills'; httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', }), }; constructor(private http: HttpClient) {} getSkills(): Observable<SkillSet[]> { return this.http.get<SkillSet[]>(this.skillsUrl); } // 同步组件端的修改到服务端 saveChanges(skills: SkillSet[]): Observable<SkillSet[]> { console.log(skills); // 控制台打印可证明数组已携带编辑后内容 return this.http.patch<SkillSet[]>( this.skillsUrl, skills, this.httpOptions ); } }
问题原因与解决方案
核心原因
GET请求/skills路径正常但PATCH/PUT返回404,和前端代码无关,是后端接口路由没有配置对应路径的PATCH/PUT方法导致的:
- 你当前后端只给
/skills路径配置了GET方法,用于获取全量技能列表,所以页面初始化加载数据正常 - RESTful接口规范中,批量更新全量列表、单条资源更新的路径是不一样的:
- 单条资源更新:PATCH/PUT
/skills/{资源id},需要携带对应单条SkillSet的id和修改内容 - 全量列表批量替换:PUT
/skills,需要后端单独实现该路径的PUT方法逻辑 - 批量部分更新:PATCH
/skills,同样需要后端单独实现对应方法逻辑
- 单条资源更新:PATCH/PUT
修复步骤
- 先确认你的后端服务(看端口5000大概率是json-server或者Node/Flask/Django类服务)的接口配置:
- 如果用的是json-server:默认不支持直接对列表根路径发PATCH/PUT批量更新,要么循环单条调用
/skills/{id}的PATCH接口更新每一项,要么自定义路由添加批量更新逻辑 - 如果是自己写的后端服务:给
/skills路径添加PUT/PATCH方法的处理逻辑,或者调整前端逻辑为遍历修改过的SkillSet项,逐个调用单条更新接口
- 如果用的是json-server:默认不支持直接对列表根路径发PATCH/PUT批量更新,要么循环单条调用
- 前端代码对应调整(以后端支持单条更新为例):
先在service里添加单条更新方法:
再修改组件里的updateSkill(skill: SkillSet): Observable<SkillSet> { const url = `${this.skillsUrl}/${skill.id}`; return this.http.patch<SkillSet>(url, skill, this.httpOptions); }saveChanges方法,用forkJoin并行提交所有更新:// 记得在组件顶部导入forkJoin:import { forkJoin } from 'rxjs'; saveChanges(): void { this.editMode = false; const updateTasks = this.skills.map(skill => this.skillsService.updateSkill(skill)); forkJoin(updateTasks).subscribe({ next: () => console.log('所有修改已同步'), error: err => console.error('同步失败', err) }); } - 如果需要做批量更新,就先在后端实现
/skills路径的PATCH/PUT处理逻辑,再保留你当前前端的写法即可。
额外排查点:如果是本地开发跨域场景,检查后端是否配置了对应请求方法的CORS放行规则,OPTIONS预检请求失败也会返回404类错误,但你GET请求正常的话这个概率很低。
内容的提问来源于stack exchange,提问作者SebaQuiros
相关产品推荐
相关产品推荐

