1. 项目概述为什么“curl发送不同的请求方法”是每个终端用户绕不开的基本功你有没有过这样的经历在调试一个API接口时后端同事说“你用POST发个JSON试试”你打开Postman点几下就完事了可一到服务器上、CI/CD流水线里、或者写自动化脚本时Postman就彻底失能——这时候真正扛起大旗的永远是那个其貌不扬、命令行里敲几下就跑起来的curl。它不是玩具而是Linux/macOS系统里最硬核的HTTP协议探针是DevOps工程师排查服务连通性的第一把手术刀是运维同学验证证书链是否完整的默认工具更是安全人员做基础渗透测试时最常调用的轻量级武器。“curl发送不同的请求方法”这个标题看似简单但它背后承载的是对HTTP协议本质的理解深度。GET、POST、HEAD、PUT这些动词从来不只是“发个请求”这么轻飘飘它们各自携带语义约束、缓存策略、幂等性要求、数据编码规则和服务器处理逻辑。比如你用curl -X GET https://api.example.com/users?id123能拿到用户信息但若把-X GET换成-X POST哪怕URL和参数完全一样服务器大概率会返回405 Method Not Allowed——这不是curl错了是HTTP协议在告诉你“你用错了动词”。再比如curl -I即HEAD请求常被用来检查资源是否存在、响应头是否含CORS配置、SSL证书是否过期而不用下载整个body这对带宽受限或高并发探测场景至关重要。我曾在一次线上告警中用一条curl -I -k https://payment-gateway.internal:8443/health在3秒内确认了网关TLS握手失败比重启服务、查日志快了整整8分钟。这个内容适合三类人一是刚接触命令行的开发新手需要摆脱图形界面依赖建立终端直觉二是运维/测试工程师日常要批量验证上百个微服务端点必须掌握高效、可脚本化的HTTP交互方式三是安全与合规人员需精准构造特定methodheader组合来模拟攻击载荷或验证防护策略。它不教你怎么写前端页面也不讲Kubernetes编排它只聚焦一件事让你在没有GUI、没有IDE、甚至没有网络浏览器的纯文本世界里依然能像呼吸一样自然地与任何HTTP服务对话。接下来的内容我会从设计思路、核心细节、实操步骤到真实踩坑记录全部展开——不讲虚的只说你在终端里敲下回车后到底发生了什么。2. 核心设计思路与方案选型逻辑为什么是curl而不是其他工具2.1 为什么选择curl而非wget、httpie或Python requests很多人会问wget也能发GETPython requests更灵活Postman界面更友好为什么还要死磕curl答案藏在三个不可替代的底层优势里零依赖、协议保真度、原子化控制。首先“零依赖”意味着开箱即用。在绝大多数Linux发行版CentOS/RHEL/Ubuntu/Debian、macOS预装甚至Windows 10/11通过WSL或原生curl中curl都是系统级内置工具。你不需要pip install httpie不需要apt-get install wget更不需要启动一个Python解释器。我在一次紧急故障排查中面对一台因磁盘满导致包管理器瘫痪的生产服务器唯一能运行的HTTP工具就是curl——它静态链接了所有必要库连/lib目录损坏都不影响基本功能。而wget虽然也广泛预装但它天生为“下载文件”设计对非GET/HEAD请求支持薄弱比如无法原生发送带JSON body的PUT请求必须靠--post-file这种变通方式且无法精细控制Content-Type头。其次“协议保真度”指curl对HTTP标准的严格遵循。它不像某些高级封装库会自动帮你加Accept: */*、User-Agent或悄悄重定向302——curl默认不做任何假设。你用curl -X POST -H Content-Type: application/json -d {name:test} https://api.example.com发出的原始HTTP请求就是你写的每一个字节。这种“所见即所得”在调试跨域问题、认证头缺失、或服务端严格校验User-Agent时价值巨大。我曾遇到一个API只接受User-Agent: MyApp/1.0其他一律403。用Python requests发请求时它默认带python-requests/2.x改起来要写三行代码而curl只需加一个-H User-Agent: MyApp/1.0一秒搞定。最后“原子化控制”体现在对每个协议层的独立开关能力。curl允许你单独启用/禁用SSL验证-kvs--cacert单独设置超时--connect-timeout 5vs--max-time 30单独指定DNS解析--resolve example.com:443:192.168.1.100甚至单独控制TCP连接复用-H Connection: close。这种粒度在自动化脚本中极为关键。比如在CI环境中你可能需要强制跳过证书验证-k以绕过自签名证书但又必须确保不忽略SSL握手错误--fail这时curl -k --fail就能精准满足需求而wget没有--fail这种语义明确的失败开关。提示curl的-X或--request参数是万能钥匙但它不是万能解药。当你看到curl -X POST ...时请记住-X只是覆盖请求行中的method字段它不会自动帮你设置Content-Type或Content-Length。很多初学者以为加了-X POST就万事大吉结果服务器收不到body——因为没配-H Content-Type: ...也没用-d或--data-binary传数据。这是curl最经典的认知陷阱。2.2 不同HTTP方法的核心语义与适用边界HTTP方法不是随意命名的标签而是经过RFC 7231严格定义的语义契约。理解它们才能避免“用POST干GET的事”这类低级错误。GET语义是“获取资源表示”。它必须是安全的safe、幂等的idempotent、可缓存的。这意味着1不应产生副作用如修改数据库2重复请求结果一致3浏览器/代理可缓存响应。实际使用中GET参数必须放在URL query string里?keyvaluekey2value2且长度受浏览器和服务器限制通常2KB以内。我见过最离谱的案例某前端团队把Base64编码的图片数据拼进GET URL导致Nginx直接返回414 Request-URI Too Large。POST语义是“向指定资源提交数据请求服务器进行处理如提交表单、上传文件”。它既不安全也不幂等——每次POST都可能创建新资源或触发状态变更。数据通过request body发送无长度限制受服务器配置约束。关键点在于POST本身不规定body格式application/x-www-form-urlencoded表单默认、application/jsonAPI主流、multipart/form-data文件上传都是合法的但必须显式声明Content-Type头否则服务器无法正确解析。HEAD是GET的“轻量版”只返回响应头不返回body。它继承GET的所有语义安全、幂等、可缓存常用于1检查资源是否存在curl -I https://file.example.com/report.pdf返回200则存在2获取Content-Length预估下载大小3验证Last-Modified或ETag做条件请求。注意HEAD响应头必须与对应GET请求完全一致RFC 7231 4.3.2所以它是测试缓存策略的黄金标准。PUT语义是“用请求body完全替换目标资源”。它必须是幂等的——多次PUT同一URI同一body结果等价于一次。这使它天然适合“更新整个资源”场景如PUT /api/users/123替换用户全部字段。与POST的关键区别在于PUT的URI指向具体资源实例而POST的URI通常指向资源集合POST /api/users创建新用户。实践中PUT常用于配置同步、固件升级等需要强一致性的操作。DELETE语义是“删除指定资源”。它也是幂等的删一次和删十次效果相同但不安全有副作用。注意HTTP标准未规定DELETE是否必须带body但RESTful实践普遍认为DELETE应无body参数全放URI中DELETE /api/posts/456?forcetrue。注意不要滥用-X伪造方法。有些老系统用GET模拟DELETEGET /delete?id123这是反模式。现代API应严格遵循HTTP语义否则会破坏缓存、代理、CDN等基础设施的正常工作。curl的强大恰恰在于它能帮你验证服务端是否真正遵守了这些契约。3. 核心细节解析与实操要点从命令结构到参数陷阱3.1 curl命令的原子化构成与执行流程一条典型的curl命令如curl -v -X POST -H Content-Type: application/json -d {id:1} https://api.example.com/users其内部执行并非简单拼接字符串而是一套严谨的协议栈组装过程。理解这个流程是避开90%常见错误的前提。第一步URL解析与协议协商。curl先解析URL schemehttp/https决定使用HTTP/1.1还是HTTP/2现代curl默认启用HTTP/2 over TLS。对于HTTPS它立即进入TLS握手阶段验证证书链除非-k、协商加密套件、建立安全通道。若证书无效curl默认报错退出curl: (60) SSL certificate problem...这是安全设计不是bug。第二步请求行Request Line生成。根据-X参数或默认GET确定methodURL path作为request-targetHTTP版本由curl自动选择。这里有个隐藏陷阱-X POST只改method不改其他。如果你写curl -X POST https://api.example.comcurl会发POST / HTTP/1.1但服务器可能期望POST /api/v1/导致404。务必确保URL path完整。第三步请求头Headers注入。curl按顺序处理-H参数并自动添加必要头User-Agent: curl/version可被-H User-Agent: xxx覆盖Host: api.example.com自动从URL提取不可覆盖Accept: */*默认可被-H Accept: application/json覆盖Content-Length: n当有body时自动计算不可手动设置否则curl报错关键点-H是追加模式不是覆盖模式。curl -H Content-Type: text/plain -H Content-Type: application/json会发送两个Content-Type头违反HTTP标准多数服务器拒绝处理。正确做法是只用一个-H。第四步请求体Body组装。取决于数据来源-d data将字符串作为body自动添加Content-Type: application/x-www-form-urlencoded并计算Content-Length。--data-binary file.bin读取二进制文件不转换换行符适合上传图片、PDF。-F fieldfile.txt构建multipart/form-data自动处理boundary和headers专为文件上传设计。第五步响应处理。curl默认输出body到stdout。-I只输出headers-s静默模式-o file保存body到文件。响应头中的Content-Encoding: gzip会被自动解压除非--no-decompress这是curl的贴心设计但有时会干扰调试如想看原始gzip流此时需--raw。实操心得用-vverbose是调试灵魂。它会打印完整的请求行、所有请求头、响应行、所有响应头以及body摘要。但注意-v不显示请求body明文防敏感信息泄露只显示 data 。要查看真实发送的body用-v --trace-ascii /dev/stdout它会输出十六进制dump适合抓包级分析。3.2 GET请求的深层细节与常见误区GET看似最简单却是误解最多的。核心误区有三参数编码、长度限制、缓存干扰。参数编码URL中只能包含ASCII字符空格、中文、特殊符号如、、/必须URL编码Percent-encoding。curl会自动编码-d参数但不会自动编码URL中的query string例如curl https://api.example.com/search?qhello world会失败因空格非法正确写法是curl https://api.example.com/search?qhello%20world或让shell帮你curl https://api.example.com/search?q$(printf %s hello world | jq -sRr uri)。更安全的做法是用--get --data-urlencode组合curl --get --data-urlencode qhello world --data-urlencode page1 https://api.example.com/searchcurl会自动拼接URL并编码所有参数。长度限制GET的query string长度受多方制约浏览器Chrome约8KB、Web服务器Nginx默认4K可调large_client_header_buffers、反向代理Cloudflare 8KB。超长会导致414错误。我曾调试一个搜索API前端传了200个ID用逗号分隔?idsid1,id2,...,id200总长超10KBNginx直接拦截。解决方案改用POST传JSON数组或分页查询。缓存干扰GET默认可缓存浏览器/CDN可能返回旧响应。调试时务必加-H Cache-Control: no-cache或时间戳参数?t$(date %s)强制刷新。生产环境则要善用ETag和If-None-Match头做条件请求减少带宽消耗。注意curl -G或--get参数常被误用。它不是“发GET请求”而是“将-d参数转为query string并拼到URL后”。curl -G -d qtest https://api.example.com等价于curl https://api.example.com?qtest。它适合构造复杂query但不解决编码问题仍需手动处理特殊字符。3.3 POST请求的三种数据格式与选型指南POST的body格式决定服务器如何解析选错格式等于发送乱码。三大主流格式及curl实现如下1. application/x-www-form-urlencoded表单格式这是HTMLform默认格式键值对用连接值用分隔空格变特殊字符URL编码。curl最简写法-d key1value1key2value2。但手动拼接易错推荐--data-urlencodecurl -X POST \ -H Content-Type: application/x-www-form-urlencoded \ --data-urlencode usernameadmin \ --data-urlencode password123!# \ --data-urlencode bioHello World More \ https://login.example.com--data-urlencode会自动编码每个值且支持filename读取文件内容编码安全可靠。2. application/jsonAPI主流JSON格式简洁、结构化强是RESTful API事实标准。curl需显式设Content-Type并用-d传JSON字符串curl -X POST \ -H Content-Type: application/json \ -d {name:Alice,age:30,tags:[dev,curl]} \ https://api.example.com/users关键陷阱JSON字符串中的双引号必须转义\或用单引号包裹整个字符串bash中单引号内不解析变量。更健壮的做法是用jq生成echo {name:Alice,age:30} | jq -c . | xargs -I {} curl -X POST -H Content-Type: application/json -d {} https://api.example.com/users3. multipart/form-data文件上传用于混合文本字段和文件。curl用-F参数curl -X POST \ -F text_fieldHello \ -F file/path/to/photo.jpg;typeimage/jpeg \ -F descriptionMy photo \ https://upload.example.com-F自动1生成随机boundary2为每个字段添加Content-Disposition: form-data; namexxx3为文件添加Content-Type可手动指定type4计算总Content-Length。这是唯一能正确上传文件的方式-d或--data-binary会失败。实操心得用-H Content-Type: ...时务必确认服务器真的支持该类型。我曾因服务器只接受application/json;charsetUTF-8而curl发了application/json导致415 Unsupported Media Type。解决方案要么改服务器要么curl加全称-H Content-Type: application/json;charsetUTF-8。4. 实操过程与核心环节实现从入门到生产级脚本4.1 基础请求方法实操GET/HEAD/POST/PUT/DELETE逐个击破我们用一个模拟的TODO APIhttps://jsonplaceholder.typicode.com作为靶场它免费、稳定、支持所有HTTP方法是练习curl的完美沙盒。GET获取资源列表与单个资源# 获取所有待办事项200条 curl https://jsonplaceholder.typicode.com/todos # 获取ID为1的待办事项返回单个JSON对象 curl https://jsonplaceholder.typicode.com/todos/1 # 带查询参数获取用户1的所有待办事项 curl https://jsonplaceholder.typicode.com/todos?userId1 # 用--get --data-urlencode自动编码安全 curl --get \ --data-urlencode userId1 \ --data-urlencode completedfalse \ https://jsonplaceholder.typicode.com/todos观察响应GET返回完整JSON数组或对象Content-Type: application/json; charsetutf-8Content-Length明确。HEAD验证资源元信息# 检查资源是否存在且可访问无body极快 curl -I https://jsonplaceholder.typicode.com/todos/1 # 输出HTTP/2 200 HeadersDate, Content-Type, Content-Length, ETag等 # 检查ETag变化判断资源是否更新 curl -I -H If-None-Match: \1\ https://jsonplaceholder.typicode.com/todos/1 # 若ETag匹配返回304 Not Modified节省带宽 # 检查SSL证书有效期关键运维技能 curl -I -k --cert-status https://jsonplaceholder.typicode.com # --cert-status会输出证书详细信息包括notBefore/notAfterHEAD的妙用在于“零成本探测”。在部署前用curl -I --fail https://myapp.prod/api/health检查健康端点失败则中断CI流程比等待超时高效得多。POST创建新资源# 创建新待办事项application/json curl -X POST \ -H Content-Type: application/json \ -d {title:Learn curl,userId:1,completed:false} \ https://jsonplaceholder.typicode.com/todos # 创建新用户application/x-www-form-urlencoded curl -X POST \ -H Content-Type: application/x-www-form-urlencoded \ --data-urlencode nameJohn Doe \ --data-urlencode emailjohnexample.com \ https://jsonplaceholder.typicode.com/users # 文件上传multipart/form-data- 需本地有文件 # echo Hello from curl test.txt # curl -X POST -F filetest.txt https://httpbin.org/post注意POST成功后服务器通常返回201 Created并在Location头中给出新资源URI。curl不会自动跳转需手动解析响应头。PUT完全替换资源# 替换ID为1的待办事项必须提供完整对象否则缺失字段被清空 curl -X PUT \ -H Content-Type: application/json \ -d {id:1,title:Updated title,userId:1,completed:true} \ https://jsonplaceholder.typicode.com/todos/1 # 验证幂等性重复执行结果不变 curl -X PUT -H Content-Type: application/json -d {id:1,title:Same,userId:1,completed:false} https://jsonplaceholder.typicode.com/todos/1 curl -X PUT -H Content-Type: application/json -d {id:1,title:Same,userId:1,completed:false} https://jsonplaceholder.typicode.com/todos/1 # 两次响应完全一致证明幂等PUT的“完全替换”特性要求客户端掌握资源全貌。若只想更新部分字段应使用PATCHcurl需-X PATCH但PATCH非所有API支持。DELETE安全移除资源# 删除ID为1的待办事项 curl -X DELETE https://jsonplaceholder.typicode.com/todos/1 # 验证删除结果应返回200或204GET应404 curl https://jsonplaceholder.typicode.com/todos/1 # 返回[] 或 404 Not FoundDELETE的幂等性在此体现删一次和删十次资源状态都是“不存在”。4.2 进阶技巧认证、超时、重试与脚本化封装生产环境远比沙盒复杂。以下技巧让curl从玩具变成生产力工具。认证AuthenticationBasic Authcurl -u user:pass https://api.example.comcurl自动加Authorization: Basic base64(user:pass)。Bearer Tokencurl -H Authorization: Bearer token https://api.example.com。Token常从登录响应中提取# 登录获取token TOKEN$(curl -s -X POST -H Content-Type: application/json \ -d {username:admin,password:123} \ https://auth.example.com/login | jq -r .token) # 用token访问API curl -H Authorization: Bearer $TOKEN https://api.example.com/dataAPI Keycurl -H X-API-Key: abc123 https://api.example.com。注意Header名因API而异X-Api-Key,Api-Key,key。超时与重试Resilience网络不稳定是常态curl提供精细控制--connect-timeout 5建立TCP连接超时5秒DNS解析TCP握手。--max-time 30整个请求连接传输响应最长30秒。--retry 3失败时重试3次默认仅对网络错误重试。--retry-delay 2每次重试间隔2秒。--retry-all-errors对所有错误包括HTTP 4xx/5xx重试慎用。生产脚本示例带重试的健康检查#!/bin/bash # health_check.sh URLhttps://myapp.prod/health MAX_RETRY3 for i in $(seq 1 $MAX_RETRY); do echo Attempt $i to check $URL... if curl -s -f -o /dev/null --connect-timeout 3 --max-time 10 $URL; then echo ✓ Health check passed exit 0 else echo ✗ Attempt $i failed if [ $i -lt $MAX_RETRY ]; then sleep 2 fi fi done echo ❌ All $MAX_RETRY attempts failed. Exiting. exit 1-f--fail是关键它让curl在HTTP错误码4xx/5xx时返回非零退出码使if判断生效。没有-fcurl默认把404当作成功脚本永远不报错。SSL/TLS高级控制-k--insecure跳过证书验证仅测试用生产禁用。--cacert /path/to/ca.pem指定自定义CA证书内网服务常用。--cert /path/to/client.crt --key /path/to/client.key双向TLS认证mTLS。--tlsv1.2强制使用TLS 1.2禁用老旧TLS 1.0/1.1。诊断SSL问题# 查看详细SSL握手过程 curl -v --tlsv1.2 https://api.example.com 21 | grep -E (SSL|subject|issuer) # 检查证书链完整性 curl -v --cacert /etc/ssl/certs/ca-certificates.crt https://api.example.com4.3 生产级脚本一个可复用的API测试框架将零散命令组织成可维护脚本是专业性的分水岭。以下是一个精简但功能完备的api-tester.sh框架#!/bin/bash # api-tester.sh - A production-ready curl-based API tester # Usage: ./api-tester.sh [get|post|put|delete] [endpoint] [data_file] set -euo pipefail # 严格错误处理 # Configuration API_BASEhttps://jsonplaceholder.typicode.com TIMEOUT10 RETRY2 VERBOSEfalse CERT_FILE # Set to /path/to/ca.pem for custom CA # Parse args METHOD${1:-get} ENDPOINT${2:-/todos} DATA_FILE${3:-} # Helper: log with timestamp log() { echo [$(date %Y-%m-%d %H:%M:%S)] $* } # Helper: send request with retry and error handling send_request() { local method$1 local url$2 local data$3 log Sending $method request to $url # Build curl command local cmdcurl -s -f --connect-timeout $TIMEOUT --max-time $TIMEOUT --retry $RETRY if [ $VERBOSE true ]; then cmd$cmd -v fi if [ -n $CERT_FILE ]; then cmd$cmd --cacert $CERT_FILE else cmd$cmd -k # Only for testing! fi # Add method and data case $method in get|head) cmd$cmd -X ${method^^} # Uppercase ;; post|put|delete) cmd$cmd -X ${method^^} if [ -n $data ]; then if [[ $data *.json ]]; then cmd$cmd -H Content-Type: application/json -d $data elif [[ $data *.txt ]]; then cmd$cmd -H Content-Type: text/plain -d $data else cmd$cmd -d $data fi fi ;; *) log Error: Unsupported method $method exit 1 ;; esac # Execute eval $cmd $url 2/dev/null } # Main logic case $METHOD in get) send_request get $API_BASE$ENDPOINT ;; head) send_request head $API_BASE$ENDPOINT ;; post) send_request post $API_BASE$ENDPOINT $DATA_FILE ;; put) send_request put $API_BASE$ENDPOINT $DATA_FILE ;; delete) send_request delete $API_BASE$ENDPOINT ;; *) log Usage: $0 [get|head|post|put|delete] [endpoint] [data_file] log Example: $0 post /posts post_data.json exit 1 ;; esac使用示例# 测试GET ./api-tester.sh get /todos/1 # 测试POST需先创建post_data.json echo {title:Test,body:From curl,userId:1} post_data.json ./api-tester.sh post /posts post_data.json # 启用详细日志 VERBOSEtrue ./api-tester.sh get /users这个框架的价值在于可维护性配置集中API_BASE, TIMEOUT修改一处全局生效。可扩展性新增方法只需在case分支中添加。健壮性set -euo pipefail确保任何错误立即退出避免静默失败。生产就绪支持自定义CA、重试、超时可直接集成到CI/CD。提示真正的生产环境会用更专业的工具如Newman for Postman, k6但curl脚本是快速验证、故障排查、以及在受限环境如容器内无Node.js下的终极备选方案。我管理的20微服务每个都有这样一个test.sh脚本放在项目根目录新成员入职第一天就能跑通所有API。5. 常见问题与排查技巧实录那些年踩过的坑5.1 经典错误代码详解与修复方案curl错误码是调试的第一线索。以下是高频错误及其根因错误码错误信息示例根本原因修复方案3URL rejected: port number was not a decimal number between 0 and 65535URL中端口号非法如http://example.com:abc或http://example.com:65536检查URL确保端口是0-65535间的整数用-v看curl解析的URL6Could not resolve host: example.comDNS解析失败检查/etc/resolv.conf、网络连通性用nslookup example.com验证临时加--resolve example.com:443:8.8.8.87Failed to connect to 127.0.0.1 port 8080: Connection refused目标端口无服务监听用netstat -tuln | grep :8080或lsof -i :8080检查服务是否启动确认服务绑定0.0.0.0而非127.0.0.122The requested URL returned error: 404 Not FoundHTTP 404资源不存在检查URL path是否正确用curl -I确认HEAD是否也404确认API版本/v1/vs/v2/28Operation timed out after 10000 milliseconds with 0 bytes received超时但已建立连接服务处理慢或网络拥塞增大--max-time用-v看卡在哪个阶段连接后接收body35SSL connect error或schannel: next InitializeSecurityContext failedTLS握手失败证书过期/不信任/域名不匹配用curl -v --tlsv1.2 https://host更新CA证书sudo update-ca-certificates52Empty reply from server服务崩溃或未返回HTTP响应服务进程是否存活日志是否有panic用telnet host port测试TCP连通性56Recv failure: Connection reset by peer服务主动断开连接服务端异常OOM kill、配置错误检查服务日志确认请求body大小未超限实操心得遇到错误第一步永远是curl -v。它会显示curl解析的URL、所有请求头、响应头以及错误发生前的最后状态。90%的问题看-v输出就能定位。第二步用curl -I做最小化测试排除body干扰。第三步用telnet host port或nc -zv host port确认TCP层连通性。层层剥离问题无处遁形。5.2 请求头与Body的隐形战争Content-Type与编码冲突最隐蔽的bug往往发生在“看起来成功”的请求里。典型场景服务器返回200但业务逻辑没执行——因为Content-Type不匹配。案例1POST JSON却没设Content-Type# 错误curl默认设Content-Type为application/x-www-form-urlencoded curl -X POST -d {name:test} https://api.example.com/users # 服务器收到的是URL编码的字符串%7B%22name%22%3A%22test%22%7D解析失败 # 正确显式声明 curl -X POST -H Content-Type: application/json -d {name:test} https://api.example.com/users案例2中文参数在GET中乱码# 错误直接拼入URL curl https://api.example.com/search?q你好世界 # 服务器收到乱码 # 正确URL编码 curl https://api.example.com/search?q%E4%BD%A0%E5%A5%BD%E4