Routing & API:前后端协作的本质与实现

一、路由(Routing)

1. 什么是路由?

路由(Routing)本质是 "根据请求条件,映射到对应处理逻辑"的机制

  • 前端路由:根据 URL 变化,渲染对应的页面/组件(不刷新页面)。

  • 后端路由:根据 HTTP 方法(GET/POST 等)和 URL 路径,分发到对应的控制器/函数处理。

2. 前端路由(SPA 核心)

常见模式:

  • Hash 模式# 号后面的变化,不会触发页面刷新,兼容性好)

  • History 模式 (利用 history.pushState,URL 更优雅,需服务端配合)

原生 JS 实现简单路由(Hash 模式)

html

复制代码
<!DOCTYPE html>
<html>
<body>
  <nav>
    <a href="#/home">首页</a>
    <a href="#/about">关于</a>
  </nav>
  <div id="app"></div>

  <script>
    function render(path) {
      const app = document.getElementById('app');
      if (path === '/home') app.innerHTML = '<h1>🏠 首页</h1>';
      else if (path === '/about') app.innerHTML = '<h1>📄 关于我们</h1>';
      else app.innerHTML = '<h1>404</h1>';
    }

    // 监听 hash 变化
    window.addEventListener('hashchange', () => {
      const path = window.location.hash.slice(1) || '/home';
      render(path);
    });

    // 首次加载执行
    window.addEventListener('DOMContentLoaded', () => {
      const path = window.location.hash.slice(1) || '/home';
      render(path);
    });
  </script>
</body>
</html>

3. 后端路由(以 Express.js 为例)

后端路由收到请求后,执行数据库查询、业务逻辑,最终返回 JSON/HTML。

javascript

复制代码
const express = require('express');
const app = express();

// 路由:GET /users
app.get('/users', (req, res) => {
  res.json([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]);
});

// 路由:POST /users
app.post('/users', (req, res) => {
  // 假设保存新用户
  res.status(201).json({ message: '用户创建成功' });
});

// 动态路由参数 :id
app.get('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.json({ id: userId, name: `User ${userId}` });
});

// 启动服务
app.listen(3000, () => console.log('服务运行在 http://localhost:3000'));

二、接口(API / Interface)

1. 什么是接口?

接口是 前后端交互的契约。它定义了:

  • 请求方式(GET/POST/PUT/DELETE)

  • 请求路径(如 /api/orders

  • 请求参数(Query、Body、Header)

  • 响应格式(状态码、数据结构)

2. RESTful 风格接口(最主流)

REST 强调资源(Resource),用 HTTP 方法代表操作:

方法 路径 含义
GET /books 获取所有书籍
GET /books/:id 获取某本书
POST /books 新增一本书
PUT /books/:id 完整更新某本书
DELETE /books/:id 删除某本书

Node.js + Express 实现 RESTful 接口

javascript

复制代码
const express = require('express');
const app = express();
app.use(express.json()); // 解析 JSON 请求体

let books = [
  { id: 1, title: '红楼梦', author: '曹雪芹' },
  { id: 2, title: '西游记', author: '吴承恩' }
];

// 获取所有书籍
app.get('/api/books', (req, res) => {
  res.json(books);
});

// 获取单本书籍
app.get('/api/books/:id', (req, res) => {
  const book = books.find(b => b.id === parseInt(req.params.id));
  if (!book) return res.status(404).json({ error: '书籍不存在' });
  res.json(book);
});

// 新增书籍
app.post('/api/books', (req, res) => {
  const newBook = { id: books.length + 1, ...req.body };
  books.push(newBook);
  res.status(201).json(newBook);
});

// 更新书籍(全量更新)
app.put('/api/books/:id', (req, res) => {
  const index = books.findIndex(b => b.id === parseInt(req.params.id));
  if (index === -1) return res.status(404).json({ error: '书籍不存在' });
  books[index] = { id: parseInt(req.params.id), ...req.body };
  res.json(books[index]);
});

// 删除书籍
app.delete('/api/books/:id', (req, res) => {
  const index = books.findIndex(b => b.id === parseInt(req.params.id));
  if (index === -1) return res.status(404).json({ error: '书籍不存在' });
  books.splice(index, 1);
  res.status(204).send(); // 无内容返回
});

app.listen(3000);

3. GraphQL 接口(灵活查询)

GraphQL 允许客户端精确指定所需字段,减少过取或欠取。

简单 GraphQL 示例(Apollo Server)

javascript

复制代码
const { ApolloServer, gql } = require('apollo-server');

// 定义类型和查询
const typeDefs = gql`
  type Book {
    id: ID!
    title: String!
    author: String!
  }

  type Query {
    books: [Book]
    book(id: ID!): Book
  }

  type Mutation {
    addBook(title: String!, author: String!): Book
  }
`;

const books = [
  { id: '1', title: '三体', author: '刘慈欣' },
  { id: '2', title: '流浪地球', author: '刘慈欣' }
];

const resolvers = {
  Query: {
    books: () => books,
    book: (parent, args) => books.find(b => b.id === args.id)
  },
  Mutation: {
    addBook: (parent, args) => {
      const newBook = { id: String(books.length + 1), ...args };
      books.push(newBook);
      return newBook;
    }
  }
};

const server = new ApolloServer({ typeDefs, resolvers });
server.listen(4000).then(() => console.log('GraphQL 服务已启动 http://localhost:4000'));

三、路由与接口的关系与最佳实践

1. 关系

  • 路由是"分发器",决定请求交给谁处理。

  • 接口是"具体处理逻辑 + 数据格式规范"。

  • 通常后端路由直接对应接口路径(如 /api/users)。

2. 实践中如何设计?

  • 路径设计 :使用名词复数表示资源,如 /orders 而不是 /getOrders

  • 状态码语义化

    • 200 OK(成功)

    • 201 Created(创建成功)

    • 400 Bad Request(参数错误)

    • 401 Unauthorized(未认证)

    • 403 Forbidden(无权限)

    • 404 Not Found(资源不存在)

    • 500 Internal Server Error(服务器内部错误)

  • 版本控制 :在路径中加入版本,如 /api/v1/users

3. 前端如何调用接口?(Fetch 示例)

javascript

复制代码
// GET 请求
fetch('http://localhost:3000/api/books')
  .then(res => res.json())
  .then(data => console.log(data));

// POST 请求
fetch('http://localhost:3000/api/books', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ title: '朝花夕拾', author: '鲁迅' })
})
  .then(res => res.json())
  .then(newBook => console.log('新增成功', newBook));

四、总结

概念 核心职责 常见技术/形式
前端路由 控制页面视图切换(SPA) Hash/History + 组件渲染
后端路由 分发请求到具体控制器/函数 Express/Koa 路由、Spring MVC
RESTful 接口 以资源为中心,用 HTTP 方法操作 GET/POST/PUT/DELETE + JSON
GraphQL 接口 客户端按需查询,减少多次请求 单一端点 + 强类型 Schema

关键一句话

路由解决"去哪儿",接口解决"干什么"以及"怎么干"。

相关推荐
三十而立洋5 小时前
彻底搞懂跨域:原理、CORS、解决方案与实战避坑
前端
计算机魔术师5 小时前
ASR转录总出错?Google新模型WER降到2.6%,还能自动帮你改口误
前端
xcs194055 小时前
新版 IDEA(尤其是 2024/2025/2026)越来越臃肿
前端·人工智能·intellij-idea
Patrick_Wilson5 小时前
iOS 第三方浏览器图片下载失败问题
前端·ios·浏览器
菜鸟小前端在线卖艺5 小时前
因为找不到好用的前端占位图,于是我自己写了个谁都能用的占位图功能
前端·程序员·产品
Z小明5 小时前
第 1 章 Vite 项目初始化
前端·vue.js
IT_陈寒5 小时前
Redis大KEY删除慢到手抖,这几个方法让我少熬一夜
前端·人工智能·后端
AICoder码农王5 小时前
depcruise 实战:把架构约定变成可执行的检查
前端
paopaokaka_luck5 小时前
基于springboot3+vue3+uniapp的河南非遗数字图谱小程序(协同过滤算法、数字图谱展示、ECharts 图形化分析)
java·前端·spring boot·学习·小程序·uni-app·echarts
合橱瑰5 小时前
Vue3 与 ElementPlus 前端常见错误修复实践
前端·vue.js