尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从零跑通 Python + Flask 本地 Web 服务完整指南

从零跑通 Python + Flask 本地 Web 服务完整指南 Python Flask 本地启动一个服务从零跑通一个真正能用的 Web 接口很多人第一次接触 Flask都是因为一个很朴素的需求我手里有一段 Python 代码想让它变成一个能通过浏览器或者别的程序访问的服务或者我写了一个算法、一个模型、一个数据处理脚本想给别人提供一个 HTTP 接口来调用。这时候打开搜索引擎输入pythonflask本地启动一个服务跳出来一堆教程照着敲完python app.py浏览器打开127.0.0.1:5000看到 Hello World然后......就没有然后了。等真到了自己要写接口的时候发现路由、请求参数、JSON 返回、调试模式、端口占用每一个都能把人卡住半天。这篇文章我从一个实际写过不少 Flask 服务的人的角度把本地启动 Flask 服务的完整链路重新捋一遍。不光是让你跑通一个 Hello World而是让你理解每一步在干什么、为什么这么写、出问题了怎么排查。适合刚接触 Python Web 开发的新手也适合之前照着教程跑通但没搞懂原理、想系统梳理一遍的读者。1. 整体思路与方案选型为什么是 Flask1.1 Python Web 框架那么多为什么教程都在用 Flask如果你在搜索框里输入python Flask你其实是在问一个问题我想用 Python 对外提供 HTTP 服务该用什么工具。Python 生态里能干的 Web 框架不少Django、FastAPI、Tornado、Flask各有各的拥趸。但如果需求是本地启动一个服务Flask 几乎是所有场景下最顺手的那个。核心原因有三点都是我在实际项目里反复体会到的第一学习曲线极其平缓。一个最小的 Flask 服务就是几行代码创建Flask实例、定义路由函数、调用run()方法。没有复杂的项目结构要求没有强制性的目录规范你甚至可以在一个单文件里写完一个完整的小服务。对于我只是想让一段代码能通过 HTTP 被人调用这种诉求这种极简主义特别友好。第二依赖极轻。Flask 本身不强制绑定数据库、不强制绑定模板引擎、不强制绑定 ORM。你想用原始 SQL 操作数据库也行想用 SQLAlchemy 也行想直接返回 JSON 甚至返回纯文本都行。这种用什么你说了算的风格意味着你可以把 Flask 当作一个HTTP 转换壳把你已有的 Python 逻辑包装成网络接口而不需要重写你原来的代码。第三调试和排查足够直观。Flask 在开发模式下提供详细的错误页面、调试器、自动重载改完代码保存即生效这对本地开发调试来说是救命级别的体验。如果你只是临时验证一个想法、做一个内部小工具、或者给前端同事提供一个联调 mock 接口选 Flask 基本不会踩大坑。相比 FastAPI 在类型校验和接口文档上的自动生成能力Flask 更朴素但更灵活相比 Django 的重型全家桶Flask 更像一把螺丝刀简单直接地解决眼前问题。1.2 本地启动服务到底解决什么问题顺着热搜词往下看会发现大量搜索跟本地启动有关ragflow本地启动、本地计算机上的mysql服务启动后停止、本地计算机上的postgresql服务启动后停止。这些东西多少都和在本地跑起来一个服务这个动作绑定。搜索本身说明了一个很普遍的痛点很多工具、框架、中间件卡就卡在第一步——怎么让它在自己机器上跑起来。Flask 本地启动服务解决的是这个痛点里面最轻量的那一层不需要安装复杂的数据库不需要编排微服务架构不需要配置 Nginx只需要你的电脑里有 Python然后通过 pip 安装一个 Flask 包写十几行代码服务就能在你自己的机器上运行起来。你的浏览器就是客户端127.0.0.1就是地址5000 端口就是入口。这个能力看起来基础但它是后面所有进阶操作的地基。理解了本地启动你才能理解监听地址为什么是127.0.0.1而不是0.0.0.0才能理解为什么改了代码之后服务没生效需要重跑以及端口被占用时到哪里去看。2. 环境准备与安装先把 Python 和 Flask 安排明白2.1 Python 版本选哪个Flask 官方从 2.3 版本开始要求 Python 3.8 以上到了 Flask 3.0最低要求提升到 Python 3.8推荐直接使用 3.10 或更高版本。如果你现在才刚开始装 Python直接装 3.10 或者 3.11 以上版本就行没必要在 2.x 和 3.x 之间纠结。我在实际部署里遇到的大部分奇奇怪怪的问题几乎都源于 Python 环境太老或者装了两个版本的 Python 导致 pip 指向混乱。用一个简单的命令确认你的 Python 是否可用。打开终端输入python --version如果你看到输出里有 Python 3.x比如Python 3.10.12那基础环境没问题。如果你输入python提示找不到命令试试python3 --version。在部分 Linux 发行版和 macOS 上python默认指向 Python 2.x 或者干脆没装这时候用python3是比较保险的。Windows 上如果你装 Python 时勾选了Add Python to PATH那么python通常就能直接使用。注意如果你电脑里装了不止一个 Python 版本比如系统自带一个、后来自己又装了一个就会出现我用 pip 装好了 Flask但运行 python app.py 时提示导入不了 flask 这类诡异问题。解决办法也很简单下面讲虚拟环境的时候会一起说。2.2 虚拟环境新手最容易跳过的关键一步我见过太多新手的做法是直接pip install flask装到全局环境里然后就开始写代码。一次两次没问题但只要你同时做过两三个项目就会发现全局环境是一个早晚要炸的雷项目 A 需要 Flask 2.0项目 B 需要 Flask 3.0项目 C 可能只需要一个 requests装来装去就把全局环境弄得一团糟。所以我的建议是不管你以后玩不玩大项目从一开始就用虚拟环境。它做了一件很简单的事为当前这个项目单独开一个隔离的 Python 运行环境你在这个环境里装什么包都不会影响系统其他地方的 Python。创建虚拟环境的标准做法是# 进入你的项目目录 cd my_flask_app # 创建虚拟环境名字叫 venv放在当前目录下 python -m venv venv激活虚拟环境不同操作系统命令不一样Windows 终端venv\Scripts\activatemacOS / Linux 终端source venv/bin/activate激活之后你的命令行提示符前面通常会出现(venv)这样的前缀这就说明你已经在虚拟环境里了。这时候再执行pip install flaskFlask 和它依赖的 Werkzeug、Jinja2、MarkupSafe 都会被装进这个虚拟环境和你系统里其他项目互不干扰。以后再在这个项目里跑python app.py用的就是当前虚拟环境里的 Python 和 Flask。2.3 pip 安装 Flask 时常见的两个坑第一个坑是 pip 版本太老导致安装失败。如果你在执行pip install flask时看到类似 Consider using the--useroption or check permissions 的提示说明可能存在权限问题或者 pip 版本过低。先升级 pip 再装python -m pip install --upgrade pip第二个坑是网络问题安装到一半就报 timed out 或者 connection error。国内比较常见的解决办法是临时使用镜像源。我用的是清华的 PyPI 镜像实测下来很稳pip install flask -i https://pypi.tuna.tsinghua.edu.cn/simple装完后验证一下确认 Flask 真的装好了python -c import flask; print(flask.__version__)能看到版本号输出环境准备这一步就算走通了。3. 第一个 Flask 服务从代码到浏览器可访问的完整过程3.1 最小可运行服务的代码结构现在进入正题。在项目目录下新建一个文件名字随意比如app.py写入下面这段代码from flask import Flask # 创建 Flask 应用实例 app Flask(__name__) # 定义路由当访问根路径 / 时执行 index 函数 app.route(/) def index(): return Hello Flask! # 启动服务 if __name__ __main__: app.run()就这几行一个可访问的本地 Web 服务就已经写出来了。在命令行里确认虚拟环境已激活然后执行python app.py你会看到类似这样的输出* Serving Flask app app * Debug mode: off * Running on http://127.0.0.1:5000把浏览器地址栏输入http://127.0.0.1:5000回车看到页面显示Hello Flask!你的第一个本地服务就算跑起来了。不过这里有一个很典型的误解要澄清。很多初学者以为app.run()是 Flask 规定的神秘入口少了它服务就跑不起来。其实app.run()做的事可以理解为启动一个开发用的 HTTP 服务器持续监听来自客户端的请求。这行代码只在直接运行app.py这个文件时才执行所以外面套了一层if __name__ __main__:的判断。如果你将来用 gunicorn 或者其他 WSGI 服务器启动这个应用这个判断块就不重要了。3.2 run 方法的关键参数端口、调试模式、监听地址app.run()不是光秃秃写上去就完了。实际开发中我几乎每次都会带上参数if __name__ __main__: app.run(host127.0.0.1, port5000, debugTrue)逐个解释一下这些参数的含义host服务监听的主机地址。127.0.0.1表示只允许本机访问别人通过局域网 IP 访问不到。如果你想在同一局域网下让别人比如手机或者同事的电脑访问你的服务可以改成0.0.0.0表示监听所有网络接口。但注意0.0.0.0也会让你的服务暴露在局域网里安全性要自己把握好开发调试阶段我一般还是默认127.0.0.1。port服务监听的端口默认是 5000。如果你运行多个 Flask 服务就要换端口比如port5001。端口最大的坑是冲突后面专门讲。debugTrue打开调试模式。这个模式有两大功效一是代码保存之后服务自动重载你不需要每次手动重启二是程序报错时浏览器会显示一个带堆栈信息的调试页面。这两个功能在本地开发时非常好用但我强烈建议实际对外提供服务时把它关掉因为调试页面会暴露代码路径和内部变量有一定安全隐患。所以一个比较标准的本地启动姿势是python app.py看到输出Running on http://127.0.0.1:5000后打开浏览器访问。如果你改了代码想立即生效在 debug 模式下保存就行如果没开 debug 模式就需要 CtrlC 停掉服务再重新跑一遍。3.3 让服务真正对外可用理解 127.0.0.1 和 0.0.0.0 的区别有一个场景我遇到过好多次同事把 Flask 服务跑起来了然后用电脑的局域网 IP 发给别人结果别人访问不了。在本地自己浏览器打开127.0.0.1:5000好好的为什么别人访问不到原因就在host参数。127.0.0.1是环回地址它只在当前电脑内部生效。如果你想让人通过局域网 IP比如192.168.1.100:5000访问那就必须让服务监听在能够接收外部请求的网卡地址上。最简单的方式是app.run(host0.0.0.0, port5000, debugTrue)0.0.0.0不是某个具体的 IP而是代表本机所有 IP 地址。它表示服务对来自所有网卡的请求都进行监听。这样设置之后你本机用127.0.0.1:5000能访问局域网里的其他机器通过你的局域网 IP 加上冒号加端口号也能访问。但这里要提醒一句把服务绑定到0.0.0.0意味着任何能连到你电脑网络的设备都能尝试连接你的服务。如果你做的服务里面没有任何鉴权逻辑很容易被别人直接调用。开发阶段在可信网络里用一下没问题一旦要部署到公网服务器就一定要在前面加一层认证或者用反向代理做访问控制。4. 路由与请求处理从 Hello World 到真正能用的接口4.1 路由规则URL 和 Python 函数的对应关系光会返回一行 Hello 文本离能用的服务还差得很远。实际中你的服务要处理的是各种不同的请求路径和请求方式这就需要理解 Flask 的路由机制。路由可以理解成一张地图什么样的 URL 由哪一个 Python 函数来处理。app.route(/)这种写法是给index函数挂了一个当用户访问根路径时执行我的标签。你也可以定义更丰富的路径。比如我们现在要做一个用户信息的接口from flask import Flask, jsonify, request app Flask(__name__) app.route(/user/int:user_id) def get_user(user_id): return jsonify({user_id: user_id, name: Tom})这里面的int:user_id是一个动态参数。它表示访问/user/123时user_id会被为整数 123访问/user/abc时 Flask 会直接返回 404。这个写法比自己在函数里解析 URL 干净得多。除了int还有string、float、path等转换器path可以匹配包含斜杠的路径比如传文件路径之类的场景。4.2 请求参数的三种来源前端调用接口时通常不会只通过 URL 路径传参。更多情况是下面三种方式查询字符串也就是 URL 问号后面的部分GET /search?keywordpythonpage1在函数里接收app.route(/search) def search(): keyword request.args.get(keyword) page request.args.get(page, default1, typeint) return jsonify({keyword: keyword, page: page})表单数据常见于 HTML 表单提交app.route(/login, methods[POST]) def login(): username request.form.get(username) password request.form.get(password) return jsonify({username: username})JSON 数据现在前后端分离的项目里绝大多数接口都走这个app.route(/api/echo, methods[POST]) def echo(): data request.get_json() return jsonify({received: data})注意methods参数控制了接口允许的请求方式。Flask 默认只处理 GET 请求如果你要处理 POST必须显式加上methods[POST]。这是一个新手特别容易踩的点明明写了/login路由但是前端用 POST 请求一访问收到的却是 405 Method Not Allowed。4.3 返回 JSON最好统一的接口返回格式做后端接口时我不太建议随便return一个字符串或者一个字典。虽然没有硬性规定但一套统一的返回格式会给联调省很多事。我自己习惯的返回结构是{ code: 0, message: success, data: { ... } }code表示业务状态码0代表成功message是给前端看的提示信息data是实际业务数据。这样做的好处是前端拿到任何响应都能用同一套逻辑解析不用一个接口一个特殊处理。Flask 返回 JSON 最简单的方式是from flask import jsonify app.route(/api/info) def info(): return jsonify({code: 0, message: success, data: {version: 1.0}})jsonify会帮你设置响应头Content-Type: application/json并且自动把字典转成 JSON 格式。这里有个细节不要用return {key: value}这种简写吗实际 Flask 也支持直接返回字典会自动转成 JSON但从可读性和兼容性角度我更推荐团队里统一用jsonify。4.4 加上 CORS为什么你用浏览器调用接口总是被拦住本地开发时你可能会遇到另一个让人摸不着头脑的问题自己在浏览器里访问 Flask 接口没问题但是写了一个纯前端 HTML 页面用 fetch 或者 axios 调用这个接口控制台里报了一堆 CORS 相关的错误。这是因为浏览器的同源策略。你的前端页面如果是从http://127.0.0.1:5500打开的而 Flask 服务在http://127.0.0.1:5000这就属于跨域。浏览器默认禁止跨域读取响应数据。解决办法是在 Flask 端开启 CORS。我自己常用的是flask-cors这个扩展pip install flask-cors然后在代码里from flask_cors import CORS app Flask(__name__) CORS(app)这一行就足够让浏览器放行跨域请求了。默认情况下CORS(app)允许所有来源的请求开发阶段可以用上线时建议指定origins参数CORS(app, resources{r/api/*: {origins: [http://localhost:3000]}})只允许来自特定前端的跨域请求避免接口被任意网站调用。5. 调试模式与本地开发体验为什么 debugTrue 这么好用又能气死人5.1 自动重载和调试器本地启动服务时debugTrue带来的体验提升是很明显的。没有 debug 模式的情况下你每改一个函数、每换一个返回内容都要去终端 CtrlC 停掉服务再重新运行python app.py一天下来光这个动作就能重复几十次。而开了 debug 模式代码文件保存的瞬间服务会检测到文件变化自动用新的代码重启浏览器里直接刷新就能看到变化。另外一个 debug 模式的重要能力是交互式调试器。如果你的代码抛异常比如访问一个不存在的 key浏览器页面会显示一个黄色底、包含完整堆栈信息的错误页页面右下角会有一个调试器 PIN 码输入框输入终端里打印的 PIN 码之后你甚至可以在这个页面上执行一段 Python 代码来检查当时的变量值。这个功能对排查复杂逻辑问题帮助巨大。5.2 debug 模式在本地联调中的安全隐患说实话debug 模式功能本身很强大但它的调试器本质上是一个远程代码执行入口。如果让一个开着 debug 模式、绑定了0.0.0.0的 Flask 服务暴露在可被外部访问的环境里风险极高。我在实际项目中见过有人把 debug 模式的测试服务通过公网映射出去然后被人在调试器里执行了命令整个项目目录被翻了个底朝天。所以我的习惯是开发时开 debug环境切换或部署时务必关掉并且不要用app.run(host0.0.0.0, debugTrue)这种组合。如果确实需要外部访问你的本地调试服务尽量加一层访问控制或者用完立刻关闭。不过debugTrue还有一个让不少人抓狂的副作用它会启用自动重载器。有一些你初始化一次的全局变量、数据库连接池可能会因为重载而被初始化两次逻辑上出现怎么和我预期不一样的问题。遇到这种情况可以把重载器单独关掉保留调试器app.run(debugTrue, use_reloaderFalse)这样代码修改后不会自动重载但你仍然能在异常页面使用交互式调试器。6. 常见问题与排查技巧本地启动 Flask 的坑我都替你踩过了6.1 端口占用Address already in use这个是本地启动服务时最最常见的问题。错误信息通常是这样的OSError: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的相关尝试。或者 Linux 下OSError: [Errno 98] Address already in use出现这个问题的原因很简单你监听的端口已经被别的程序占了。可能是你之前启动的 Flask 服务忘了关闭也可能是其他应用占用了 5000 端口。排查方法分系统。Windows 下netstat -ano | findstr 5000看到占用 5000 端口的进程 PID 之后用任务管理器查一下是哪个程序确认没有用再结束它taskkill /PID 12345 /FLinux / macOS 下lsof -i :5000会列出监听 5000 端口的进程找到进程 PID 后用kill -9 PID结束。如果你不想杀进程还有一个更省事的办法换个端口启动。前面提过port参数就是干这个的。写死了用 5000 容易撞车自己临时调试时完全可以换到 5001、5002。6.2 改了代码没有效果很多新手折腾半天发现接口返回的内容始终是旧版本怎么改都不生效。这种问题绝大多数情况属于下面三种一是没有开 debug 模式服务没有自动重载。你改了代码但运行中的服务还是旧的一套刷新浏览器当然没用。解决方法是停掉服务重新启动或者改用 debug 模式。二是浏览器缓存。尤其是 GET 请求的接口浏览器可能对响应做了缓存。刷新页面前按 CtrlF5 强制刷新或者在接口 URL 后面加一个无意义的随机参数。三是启动的进程根本不是你这个项目的。有时候你在虚拟环境里激活了 A 项目却跑到了 B 项目的app.py或者终端 mac 的当前目录跟你以为的不一样。print(os.getcwd())打一下当前工作目录再print(app.root_path)看 Flask 实际读取的路径基本能定位问题。6.3 pip 安装 Flask 成功但运行时报 ModuleNotFoundError这个问题的典型场景是你在一个终端里pip install flask装好了然后在另一个终端里运行python app.py结果报ModuleNotFoundError: No module named flask大概率是pip对应的 Python 解释器跟python对应的解释器不是同一个。你在装 Flask 时用的可能是系统路径下的 pip而python app.py用的是虚拟环境里的解释器。这也是我前面强调一开始就用虚拟环境的原因——如果你全程在同一个虚拟环境里操作这个问题从一开始就不会出现。如果已经乱掉了一件紧急的排查方法是pip show flask看它装到了哪个路径然后再看python -c import sys; print(sys.executable)确认当前运行的 Python 解释器路径。两者不一致就进入项目虚拟环境重新pip install flask或者直接用虚拟环境里的python -m pip来安装保证解释器和包对应。6.4 浏览器能访问但 curl 或代码请求失败还有一种情况是浏览器打开http://127.0.0.1:5000正常但是用 curl、requests、或者前端代码请求时却失败。这类问题通常不是 Flask 本身的问题而是请求方式和参数不匹配。比如你定义了只能接受 POST 的接口却用浏览器地址栏直接访问浏览器发出的是 GET 请求自然 405。用命令行验证接口特别方便curl http://127.0.0.1:5000/api/echo发送 POST 请求的 JSON 数据curl -X POST http://127.0.0.1:5000/api/echo -H Content-Type: application/json -d {key:value}这样一眼就能看到 Flask 返回的响应内容比在浏览器里瞎猜高效得多。6.5 常见问题速查表问题描述可能原因快速解决端口被占用上一个服务未关闭或其他程序占用端口换端口启动或用 netstat/lsof 找到进程并结束修改代码不生效未开 debug或浏览器缓存重启服务CtrlF5 强制刷新ModuleNotFoundErrorpip 装到了别的环境确认虚拟环境重新安装 Flask局域网 IP 无法访问host 未设置为 0.0.0.0app.run(host0.0.0.0)前端跨域请求失败未开启 CORS安装 flask-corsCORS(app)POST 请求返回 405路由未声明 methods[POST]在 route 里加上 methods 参数JSON 请求拿不到字段没有用 request.get_json()改用request.get_json()获取请求体7. 实操完整示例搞一个本地可用的接口合集光说原理还不够我把本地启动 Flask 服务的完整姿势串成一个例子这个例子贴近日常开发中用一个服务暴露多个小功能的场景你直接照着敲一遍就能跑通。假设我们的需求是做一个最简版的待办事项服务支持查列表、新增一条、删除一条。from flask import Flask, request, jsonify app Flask(__name__) # 用一个列表模拟数据库存储 todos [ {id: 1, title: 学习和使用 Flask, done: False}, {id: 2, title: 编写第一个接口, done: True}, ] app.route(/todos, methods[GET]) def list_todos(): return jsonify({code: 0, message: success, data: todos}) app.route(/todos, methods[POST]) def add_todo(): data request.get_json() if not data or not data.get(title): return jsonify({code: 1, message: title is required, data: None}), 400 new_id max(item[id] for item in todos) 1 todos.append({id: new_id, title: data[title], done: False}) return jsonify({code: 0, message: created, data: {id: new_id}}), 201 app.route(/todos/int:todo_id, methods[DELETE]) def delete_todo(todo_id): global todos todos [item for item in todos if item[id] ! todo_id] return jsonify({code: 0, message: deleted, data: None}) if __name__ __main__: app.run(host127.0.0.1, port5000, debugTrue)启动前先激活虚拟环境用python app.py跑起来。然后先用浏览器访问http://127.0.0.1:5000/todos你应该能看到一个 JSON 数组。再用命令行验证新增和删除curl -X POST http://127.0.0.1:5000/todos -H Content-Type: application/json -d {title:用 curl 测试新增}curl -X DELETE http://127.0.0.1:5000/todos/3这个例子你可能会想数据存列表里服务一重启不就全没了吗确实所以它只适合做接口联调和演示。真实项目中数据库那一层就需要接 SQLite 或者 MySQL 了。但不管后端用什么存储Flask 这层处理 HTTP 请求和返回 JSON 的逻辑是一样的你掌握了这一层往后加数据库只是多几步操作的事。这个示例里还有两个细节值得注意。一个是return jsonify(...), 400这种写法逗号后面的数字是 HTTP 状态码表示这个请求在语义上是有问题的。前端拿到状态码 400 就能直接判断出参数错误不用拆 JSON 看业务码这是规范化的另一种体现。另一个是global todos的作用因为在函数内部对todos重新赋值了需要声明它是全局变量如果不声明Python 会把todos当成局部变量运行时会直接报引用错误。我加这个细节是想提醒你Flask 的路由函数表面上是写一个函数然后访问 URL但本质上它就是一个普通 Python 函数Python 作用域的规则在这里同样生效。8. 再进一步这个本地服务后续还能怎么用许多人的学习路径是本地启动 Flask 服务跑通了然后就没有然后了。但我的实际体会是本地启动这个动作的真正价值在于它打开了一扇门。跑通了这一步后面很多事情都是在这个基础上做加法。你可以把 Flask 服务当作一个本地接口工具用来 mock 数据让前端同事不用等后端接口写完就能先跑起来页面对接你可以把已有的算法代码包装成接口让其他程序调用而不是每次都改代码重新跑一遍数据你还可以在 Flask 里集成 SQLite 或者 MySQL把它做成一个完整的小型业务系统后端。如果你想更进一步把本地验证过的服务部署到服务器上就会接触到 gunicorn、uwsgi、Nginx 这些 WSGI 服务器和反向代理工具但那时候你对服务是怎么跑起来的已经有了完整的认识新学的只是部署层的细节。我个人在实际操作中的体会是别小看本地启动一个服务这四个字。它看起来简单但背后牵涉到环境隔离、进程管理、端口与网络、HTTP 协议、路由映射、请求与响应这些东西你踩一遍坑和没踩过坑后续学习任何 Web 框架都会有完全不同的理解深度。把这个基础打牢了后面不管用 Flask 写多大的项目还是切到 FastAPI、Django你都会觉得顺畅很多。最后再分享一个小技巧如果你经常要写各种一次性接口可以把最常用的模板存成一个代码片段下次新建服务的时候直接对照填充。我的模板就是文章开头那个最小服务只是多了host、port、debug三个参数然后根据需求往里面加路由。工具这种东西越简单越不容易出错真正复杂的从来都不是工具本身而是你手上的问题。把这个最基础的能力练熟了剩下的一切都好说。
返回列表