惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 最新话题
云风的 BLOG
云风的 BLOG
博客园 - 三生石上(FineUI控件)
人人都是产品经理
人人都是产品经理
美团技术团队
V
Visual Studio Blog
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
Hugging Face - Blog
Hugging Face - Blog
博客园 - 司徒正美
D
Docker
宝玉的分享
宝玉的分享
小众软件
小众软件
U
Unit 42
A
About on SuperTechFans
I
InfoQ
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
F
Fortinet All Blogs
Microsoft Security Blog
Microsoft Security Blog
月光博客
月光博客
G
Google Developers Blog
The Cloudflare Blog
H
Help Net Security
B
Blog
The GitHub Blog
The GitHub Blog
T
The Blog of Author Tim Ferriss
I
Intezer
P
Privacy International News Feed
V
Vulnerabilities – Threatpost
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Cyberwarzone
Cyberwarzone
C
Cyber Attacks, Cyber Crime and Cyber Security
Blog — PlanetScale
Blog — PlanetScale
C
Cisco Blogs
Project Zero
Project Zero
腾讯CDC
Help Net Security
Help Net Security
Latest news
Latest news
A
Arctic Wolf
T
The Exploit Database - CXSecurity.com
B
Blog RSS Feed
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Hacker News
The Hacker News
P
Palo Alto Networks Blog
AI
AI
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
P
Proofpoint News Feed
J
Java Code Geeks
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

掘金

Win 安装Claude Code FastAPI 的 CORSMiddleware 跨域中间件 Java 自研 ReAct Agent 半年后,我用 LangGraph 验证了这些设计取舍 🚀AI编程工作流终极形态:GitNexus!零Token消耗实现代码知识图谱化!让Claude Code和Codex拥有上帝视角彻底告别盲目改代码,复杂项目重 LeetCode 72. 编辑距离:动态规划经典题解 被The Graph的GraphQL查询坑了三天,我用一个真实DeFi项目把链上数据索引彻底搞懂了 (AI) 编写简单 AI 助手 (ds-agent) 别再让 pnpm 跟着 nvm 跑了!独立安装终极指南 Claude Code 为什么这么顺?Anthropic 最新复盘:真正撑住它的不是模型,而是缓存 从 /simplify 指令深挖 Claude Code 多 Agent 协同机制 Function-Calling与工具使用 新手上路(六):Claude code装上ECC全家桶:38 个子代理、156 个技能、生产级 Hooks 与 Rules 体系 我在 Claude、Kimi、opencode 三个 AI 之间搭了一条自动协作管道 【技能篇】OpenClaw Skill 详解:给 AI 装上"专业外挂" wagmi v2 多链钱包切换:一个 Uniswap 仿盘项目让我踩了三天坑 两周浅学 RAG 我把 Python re 模块比喻成摸金手套 新手上路(三):Claude Code Skills 装了一堆没用?20+ 个 Skill 横向对比 + 三套组合方案,按需抄 K2.6、DeepSeek V4、GPT-5.5 都来了,组合拳打起来 Claude Code 进阶之路:从记忆系统到子代理编排 [java] 编译之后的记录类(Record Classes)长什么样子(上) 国产大模型能力大比拼,社区有话说 我研读了 500 个 Spring Boot 生产级代码库,90% 都犯了这 7 个致命错误 JAVA重点难点 转发-中央网信办部署开展“清朗·整治AI应用乱象”专项行动 合同同步逻辑 【合并已排序数组的三种实现策略,哪一种更可取?】 30天减20斤挑战:少一斤发100红包(2) 我竟然被JavaScript的隐式类型转换坑了三天! 二十五.Electron 初体验与进阶 本地到生产,解决 AI 全栈最后一公里——构建&部署&运维 程序员创业半年:顺的事、不顺的事,和我一直没想清楚的事 UI组件库elementplus 像使用 Redis 一样操作 LocalStorage 向量检索的流程是怎样的?Embedding 和 Rerank 各自的作用? LangChain DeepAgents 速通指南(七)—— DeepAgents使用Agent Skill 为什么越来越多的大厂抛弃MCP,转向CLI? 【节点】[SquareRoot节点]原理解析与实际应用 「AI学习笔记」RNN从 RNN 简单介绍 在 Transformer 出现之前,序列建模领域的主角长期是 RNN 及其 juejin.cn 从 “存得下” 到 “算得快”:工业物联网需要新一代时序数据平台越来越多工业用户开始意识到一个问题:**数据是存下来了, - 掘金 放弃 Claude 订阅?我用 8 年前的服务器,强跑 Google 最强开源模型 Gemma 4 真实测评! Python开发者狂喜!200+课时FastAPI全栈实战合集,10大模块持续更新中🔥 从 Claw-Code 看 AI 驱动的大型项目开发:2 人 + 10 个自治 Agent 如何产出 48K 行 Rust 代码 秒级创建实例,火山引擎 Milvus Serverless 让 AI Agent 开发更快更省火山引擎MilvusSer MediaPlayer 播放器架构:NuPlayer 的 Source/Decoder/Renderer 三驾马车 juejin.cn 【OpenClaw】通过 Nanobot 源码学习架构---(7)Memory【OpenClaw】通过 Nanobot - 掘金 ArkClaw:以 SLI 度量驱动,构建新一代 Agent 全链路可观测体系针对AI Agent系统黑盒化问题,提出了 juejin.cn juejin.cn juejin.cn OrbStack:一键将你的 Mac 变为本地服务器 NginxPulse:Nginx日志监控革命!实时洞察Web流量与安全态势的智能利器引言:当Nginx日志成为运维的“数 - 掘金 juejin.cn 大V说’AI替代不了你’,但现实是——用AI的人正在替代你2026年是AI落地的元年,自从Claude Code爆火之后 - 掘金 一套能落地的"防 Bug"习惯:不用加班也能少出错 你以为是技术问题,其实是流程问题:工程效率的真相引言 在软件工程领域,效率问题始终是团队管理者和工程师们关注的焦点。当项 - 掘金 大模型工程三驾马车:Prompt Engineering、Context Engineering 与 Harness Engineering 深度解析 SpringBoot里的这个坑差点让我加班到天亮SpringBoot里的这个坑差点让我加班到天亮** -- 引言 Spr - 掘金 4.响应式系统基础:从发布订阅模式的角度理解 Vue3 的数据响应式原理本文从发布订阅模式的核心思想出发,深入剖析了 V - 掘金 慌了!Android 17 取消图标文字,你的 App 可能要找不到了用户终于可以隐藏桌面图标下面的文字了。 这个功能在 栗子前端技术周刊第 124 期 - ESLint v10.2.0、React Native 0.85、Node.js 25.9.0... 我用 AI 搓了一个"比谁更持久"的微信小游戏,AI实现只用了一天,微信审核却用了一个月!!!起因:一个沙雕想法的诞生 - 掘金 juejin.cn 第12章 工具(Tools)与函数调用(LangChain实战)在前几章中,我们搭建的RAG系统、对话链,核心能力局限 - 掘金 juejin.cn CmComposeUI —— 基于 Kotlin Multiplatform Compose 的 UI 组件库 Android 开发的 AI coding 与 AI debugging在目前整个行业都在大规模使用 AI coding 第四章:我是如何扒开 Claude Code 记忆与上下文压缩机制的大家好。今天,我们将来到 Claude Code 源 【从0到1构建一个ClaudeAgent】规划与协调-技能 这里解决了 Agent 开发中的一个核心痛点:**上下文窗口 - 掘金 Laravel13 + Vue3 的免费可商用 PHP 管理后台 CatchAdmin V5.2.0 发布Laravel juejin.cn 一文搞懂Harness Engineering与Meta-Harness 越用越强不是广告语:拆解 Hermes Agent 的三层学习机制 P2G-Python字符串方法完全指南-split、join、strip、replace的Python编程利器 AI 周刊【2026.04.06-04.12】:Anthropic 藏起最强模型、AI 社会矛盾激化、"欢乐马"登顶 从 AI Skills 学实战技能(六):让 AI 帮你总结网页、PDF、视频 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 详解 karpathy 的 microgpt:实现一个浏览器运行的 gpt 不用 Tailscale:3 步把 Mac mini 通过 FRP 暴露到公网(稳定开机自启) P2B-Python可迭代对象完全指南-从列表到生成器的Python编程利器 手把手带你部署本地模型,让你Token自由(小白专属) juejin.cn 10分钟掌握 JSON-RPC 协议,面试加分、设计不踩坑 ReAct:让大模型学会边想边做 聊聊AI的发展史,AI的爆发并不是偶然 Python的列表推导式里藏了个坑,差点让我加班到凌晨 重排、重绘与合成——浏览器渲染性能的底层逻辑 podman与docker的区别和生产环境最佳实践 juejin.cn ConcurrentHashMap线程安全实现原理全解析 juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn OpenAI Codex深度解析:终端里的AI代码特工,一个指令重构整个项目 UE5.6 Cesium 插件编译踩坑记录(UE 5.6 + MSVC 14.38 + CMake 3.31)
Django 视图(View)与路由(URL):处理用户请求的完整流程
XovH · 2026-05-15 · via 掘金

IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在公众号、今日头条持续发布最新文章,助你少走弯路。

这是一篇关于 Web 开发核心机制——“路由与视图”的深度文章。我们从一个小问题开始:在浏览器地址栏敲下回车后,服务器里究竟发生了什么?我将带你从框架的使用者,逐步走进它内部的实现原理,并用大量的代码和控制台打印,还原一个请求的完整生命周期。


你或许每天都在写这样的代码:

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('hello/', views.hello, name='hello'),
]

# views.py
from django.http import HttpResponse

def hello(request):
    return HttpResponse("Hello, world!")

当访问 /hello/ 时,页面显示 Hello, world!。这短短三行配置背后,隐藏着一套精密的调度系统——路由(URLconf)负责把来访的 URL 指引到正确的处理函数,视图(View)负责接收请求并生成响应。 它们是 Web 应用的“交通指挥”和“业务大厅”。

这篇文章会深入浅出地拆解这两个概念:既有新手友好的类比与示例,也有进阶的源码级模拟实现。更重要的是,我们在每个关键节点加入 print(),让你在控制台亲眼看到程序的执行轨迹。


1. 化繁为简:一个比喻

想象你去一栋大型政务中心办事:

  • 路由:一楼大厅的咨询台。你递上写着“申请护照”的纸条,咨询员扫一眼,告诉你:“请上三楼 302 房间。”

  • 视图:302 房间的办事员。她接过你的申请表(请求数据),审核、盖章,然后交给你一本护照(HTTP 响应)。

在 Web 世界里,URL 就是那张纸条。路由系统负责解读纸条上的路径,找到对应的视图函数;视图函数则包含所有业务逻辑,最终返回 HTML、JSON 或重定向指令。


2. 路由的魔法:URL 模式匹配

我们以 Django 为例(Flask 的思路几乎一致),深入看看路由如何工作。

2.1 静态路由

最直接的情况:一个路径对一个视图。

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('about/', views.about),
    path('contact/', views.contact),
]

控制台没有太多可打印的,路由匹配由 Django 内部完成。但我们可以让视图“说话”:

# views.py
def about(request):
    print(">>> 进入 about 视图")
    return HttpResponse("关于我们")

def contact(request):
    print(">>> 进入 contact 视图")
    return HttpResponse("联系我们")

启动开发服务器并访问 /about/,终端会实时打印:

这就是最基础的请求分发。

2.2 动态路由:捕获路径参数

现实中 URL 往往包含变量,比如用户 ID、文章标题。路由系统可以像捕兽夹一样“夹住”这些部分,传递给视图。

# urls.py
urlpatterns = [
    path('user/<int:user_id>/', views.user_profile),
    path('article/<slug:slug>/', views.article_detail),
]
  • <int:user_id> 只匹配数字,并转化为 int 类型。

  • <slug:slug> 匹配字母、数字、下划线、连字符。

  • 常用的还有 <str:name><uuid:uid> 等。

视图函数则多出对应的参数:

def user_profile(request, user_id):
    print(f"查询用户 ID = {user_id},类型 = {type(user_id)}")
    # 模拟数据库查询
    return HttpResponse(f"用户 {user_id} 的个人主页")

def article_detail(request, slug):
    print(f"请求文章:{slug}")
    return HttpResponse(f"正在阅读《{slug}》")

当访问 /user/42/ 时,控制台输出:

查询用户 ID = 42,类型 = <class 'int'>

Django 的路由转换器已经帮你做了类型转换和格式校验,这就是 “路径参数” 的优雅之处。

2.3 更自由的匹配:正则表达式

如果内置转换器不够用(比如需要匹配四位年份),可以使用 re_path

from django.urls import re_path

urlpatterns = [
    re_path(r'^archive/(?P<year>[0-9]{4})/$', views.archive),
]

视图:

def archive(request, year):
    print(f"归档年份:{year}")
    return HttpResponse(f"{year} 年文章归档")

访问 /archive/2025/,控制台打印 归档年份:2025。注意,正则捕获的 year 是字符串,需要自行转换。

2.4 路由的“分叉”与“命名”

项目变大后,你会用 include 把 URL 分发到各个子模块,并用 name 给路由起别名,方便反向生成 URL。

# 项目级 urls.py
from django.urls import include, path

urlpatterns = [
    path('blog/', include(('blog.urls', 'blog'), namespace='blog')),
]

# blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('post/<int:pk>/', views.post_detail, name='post_detail'),
]

现在可以在任何地方用 reverse('blog:post_detail', args=[42]) 生成 /blog/post/42/。这避免了在模板或代码里硬编码 URL,是大型应用的基石。


3. 视图的多面性:处理请求与返回响应

路由找到了视图,剩下的事情就交给它了。视图的本质是一个可调用对象,接收 HttpRequest,返回 HttpResponse

3.1 函数视图:一切的原点

一个最全信息的打印版视图:

from django.http import HttpResponse
import json

def debug_view(request):
    print("=" * 40)
    print(f"请求方法:{request.method}")
    print(f"请求路径:{request.path}")
    print(f"GET 参数:{request.GET}")
    print(f"POST 参数:{request.POST}")
    print(f"请求头:{dict(request.headers)}")
    # 如果是 POST,尝试打印 body
    if request.method == 'POST':
        print(f"原始 body:{request.body}")
    print("=" * 40)

    if request.method == 'GET':
        name = request.GET.get('name', '匿名用户')
        return HttpResponse(f"Hello, {name}!")
    elif request.method == 'POST':
        data = json.loads(request.body) if request.body else {}
        return HttpResponse(f"收到数据:{data}")
    else:
        return HttpResponse("不支持的请求方法", status=405)

访问 http://127.0.0.1:8000/debug/?name=Alice,终端输出:

========================================
请求方法:GET
请求路径:/debug/
GET 参数:<QueryDict: {'name': ['Alice']}>
POST 参数:<QueryDict: {}>
请求头:{'Content-Length': '', 'Content-Type': 'text/plain', ...}
========================================

页面显示 Hello, Alice!。这种“打日志”的调试方法,是理解请求对象最直接的途径。

3.2 返回各种“口味的响应”

视图必须返回一个 HttpResponse 或其子类,Django 才会停止处理并将数据发给客户端。

  • HttpResponse:纯文本或 HTML。

  • JsonResponse:自动序列化字典,设置 Content-Type: application/json

  • render:结合模板和上下文返回 HTML。

  • redirect:返回 302 重定向。

示例:

from django.shortcuts import render, redirect
from django.http import JsonResponse

def test_response(request, fmt):
    print(f"请求格式:{fmt}")
    if fmt == 'json':
        data = {'status': 'ok', 'message': '这是一段 JSON'}
        return JsonResponse(data)
    elif fmt == 'html':
        return render(request, 'test.html', {'title': '测试页面'})
    elif fmt == 'redirect':
        print("执行重定向到 /about/")
        return redirect('/about/')
    else:
        return HttpResponse("未知格式", status=400)

访问 /response/json/,响应为 {"status": "ok", ...};访问 /response/redirect/,浏览器跳转到 /about/,且控制台看到“执行重定向到 /about/”。

3.3 类视图:组织代码的另一种选择

当你的视图需要处理多种 HTTP 方法时,基于类的视图(CBV)能让代码更清晰。

from django.views import View
from django.http import JsonResponse

class UserAPI(View):
    def get(self, request):
        print("处理 GET 请求")
        return JsonResponse({"method": "GET", "users": []})

    def post(self, request):
        print("处理 POST 请求")
        return JsonResponse({"method": "POST", "created": True}, status=201)

路由配置稍有不同:

path('api/users/', views.UserAPI.as_view()),

每次请求到达时,as_view() 会实例化类,并根据请求方法分发到 get()post()。控制台会打印对应的方法,这是面向对象风格在视图层的优雅体现。


4. 一个请求的完整生命周期(附时间轴打印)

为了让你看到请求穿过的每一层,我们可以在一个视图里埋下全局的“观察点”。

# views.py
import time
from django.http import HttpResponse

def timeline_view(request):
    print(f"[{time.strftime('%H:%M:%S')}] 1. 请求进入 WSGI 服务器")
    print(f"[{time.strftime('%H:%M:%S')}] 2. Django 中间件栈开始处理")

    # 模拟中间件做的事情
    print(f"[{time.strftime('%H:%M:%S')}] 3. 路由分发:URL 匹配到 timeline_view")
    print(f"[{time.strftime('%H:%M:%S')}] 4. 视图开始执行业务逻辑")
    time.sleep(0.5)  # 假装查了数据库
    print(f"[{time.strftime('%H:%M:%S')}] 5. 构建 HTTP 响应对象")
    response = HttpResponse("全链路时间线展示")

    print(f"[{time.strftime('%H:%M:%S')}] 6. 响应穿过中间件栈返回")
    print(f"[{time.strftime('%H:%M:%S')}] 7. WSGI 服务器发送响应给客户端")
    return response

访问 /timeline/,终端打印(时间会略有差异):

[14:23:01] 1. 请求进入 WSGI 服务器
[14:23:01] 2. Django 中间件栈开始处理
[14:23:01] 3. 路由分发:URL 匹配到 timeline_view
[14:23:01] 4. 视图开始执行业务逻辑
[14:23:01] 5. 构建 HTTP 响应对象
[14:23:01] 6. 响应穿过中间件栈返回
[14:23:01] 7. WSGI 服务器发送响应给客户端

如果你在 Django 的中间件里也加上 print(比如 process_requestprocess_view),就能看到更完整的洋葱皮模型。但核心步骤就是这七步,路由和视图正处于第 3、4 环。

404 和异常处理也会经过类似流程。当路由找不到匹配时,Django 会触发 process_exception 或直接返回 NotFound 响应。你可以在 urls.py 最下面放一个 re_path(r'^.*$', views.catch_all) 来捕获所有未匹配的 URL,并打印:“路由未匹配,返回404”。


5. 进阶:自己动手实现一个迷你路由视图系统

纸上得来终觉浅。我们抛开框架,用 50 行 Python 实现一个极简的 WSGI 应用,它拥有自己的路由匹配、动态参数、视图调用和响应返回。所有的匹配过程都会用 print 显示出来。

import re
from wsgiref.simple_server import make_server

# 路由表:每项是一个 (正则模式, 视图函数)
routes = []

def route(pattern, view_func):
    """注册路由的装饰器"""
    compiled = re.compile(pattern)
    routes.append((compiled, view_func))
    return view_func

# 定义两个视图
@route(r'^/hello/(?P<name>\w+)/$')
def hello(request, name):
    return f"你好,{name}!"

@route(r'^/$')
def home(request):
    return "欢迎来到迷你框架"

# 简易请求对象
class Request:
    def __init__(self, environ):
        self.path = environ['PATH_INFO']
        self.method = environ['REQUEST_METHOD']

# WSGI 应用核心
def application(environ, start_response):
    request = Request(environ)
    print(f"\n{'='*50}")
    print(f"收到请求:{request.method} {request.path}")

    # 遍历路由表,寻找匹配
    for pattern, view_func in routes:
        print(f"尝试匹配模式:{pattern.pattern}")
        match = pattern.match(request.path)
        if match:
            print(f">>> 匹配成功!调用视图:{view_func.__name__}")
            # 将捕获的命名参数传给视图
            kwargs = match.groupdict()
            print(f"    捕获参数:{kwargs}")
            response_body = view_func(request, **kwargs)
            break
    else:
        print(">>> 没有匹配的路由,返回 404")
        start_response('404 Not Found', [('Content-Type', 'text/plain')])
        return [b'404 Not Found']

    # 正常响应
    start_response('200 OK', [('Content-Type', 'text/plain; charset=utf-8')])
    return [response_body.encode('utf-8')]

if __name__ == '__main__':
    print("启动开发服务器于 http://127.0.0.1:8000")
    server = make_server('127.0.0.1', 8000, application)
    server.serve_forever()

将这段代码保存为 mini_framework.py 并运行。打开浏览器分别访问:

  • http://127.0.0.1:8000/

  • http://127.0.0.1:8000/hello/Pythonista/

终端输出会非常有趣:

启动开发服务器于 http://127.0.0.1:8000

==================================================
收到请求:GET /
尝试匹配模式:^/hello/(?P<name>\w+)/$
尝试匹配模式:^/$
>>> 匹配成功!调用视图:home
    捕获参数:{}
127.0.0.1 - - [15/May/2026 10:05:12] "GET / HTTP/1.1" 200 15

==================================================
收到请求:GET /hello/Pythonista
尝试匹配模式:^/hello/(?P<name>\w+)/$
>>> 匹配成功!调用视图:hello
    捕获参数:{'name': 'Pythonista'}
尝试匹配模式:^/$
127.0.0.1 - - [15/May/2026 10:05:20] "GET /hello/Pythonista HTTP/1.1" 200 18

第一次请求 / 时,系统先尝试匹配 hello 模式失败,然后匹配 home 成功。第二次,第一个模式就捕获到了 name=Pythonista,后面的模式不再尝试(因为已经 break)。这就是路由匹配的本质:一个按序尝试的正则表达式列表。

这个迷你框架没有模板引擎、没有中间件、没有数据库,但它清晰展示了 Web 框架最核心的循环:解析 URL → 遍历路由表 → 调用视图 → 返回响应。 你日常使用的 Django、Flask、FastAPI,不过是在这个骨架上增加了无数魔法细节。


6. 常见陷阱与最佳实践

最后,总结一些实际开发中容易踩的坑:

  • URL 尾部斜杠:Django 默认设置 APPEND_SLASH=True,如果你访问 /hello,它会自动重定向到 /hello/。但在 API 设计时要统一风格,避免意外的 301。

  • 路由顺序很重要path('<str:name>/', ...) 会匹配任何单级路径。如果你把它放在最前面,后面的 path('about/') 永远不会被匹配。应该把更具体的模式放在前面。

  • 视图不要做太多事:视图应该薄,只负责调用业务逻辑、构造响应。把复杂逻辑放到 models、services 或 utils 中。

  • 善用 print() 和日志:在开发阶段,print(request.GET) 这样的“土办法”能帮你快速定位 99% 的路径或参数问题。进入生产环境后,换成标准 logging 模块。


结束语

路由与视图,就像人体的神经中枢和肌肉。路由把外界的刺激精准传递到对应的功能单元,视图执行动作并给出反馈。理解了它们的协作流程和底层原理,你就不再只是敲打配置的“调参师”,而是能在心里模拟整个请求生命周期、轻松诊断各类错误的工程师。

从今天起,试着在视图中多打几行 print,甚至动手写一个自己的路由表吧。你会发现,那些曾经神秘的框架源码,正在慢慢向你敞开大门。

还可以去公众号、今日头条搜索「IT策士」,一起升级 IT 思维 !