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

关键一句话

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

相关推荐
SomeB1oody1 小时前
【RustyML入门】2.10. 主成分分析
开发语言·后端·机器学习·rust·教程
不好听6131 小时前
React 的 useRef vs useState:响应式与非响应式的分界线
前端·react.js
我真是泰库辣1 小时前
用TraeWork制作应用 —— 从 0 到 1 · 手把手搭建 opencode 网页对话网关
前端·后端
RobinDevNotes1 小时前
轻量级动画引擎Anime.js迎来V4大版本更新
开发语言·前端·javascript·ecmascript·动画·c4前端
渣波1 小时前
React 性能优化实战:从 memo 到 Hooks 的底层原理与进阶封装
前端·javascript
触底反弹1 小时前
别再 Prop Drilling 了!一文彻底搞懂 React 组件通信的 5 种方案
前端·javascript·react.js
玉宇夕落1 小时前
受控与非受控组件以及一些表单的简单业务逻辑的理解
前端
两只羊ovo1 小时前
listToTree 速通:一维数组怎么变出多级菜单?Map 和 reduce 两种全解
前端·javascript
亚雷1 小时前
图解分布式架构:一个 Console,一群 Sidecar
后端·面试·程序员