资讯动态

mongoose 实现 http web 跨域问题:把 CORS 响应头改到 TaoToken 统一出口

发布时间:2026/10/13 0:19:37 来源:尧图企业网站定制
1. 浏览器里 POST 报 CORS errormongoose 内置 HTTP 服务跨域排查用 mongoose 起一个 HTTP 服务Postman 里 GET、POST 都通换到浏览器里 GET 也正常唯独 POST 直接红字CORS errorStatus 一栏写着(failed)Network 面板里那条请求的 Response Headers 里翻不到Access-Control-Allow-Origin。这个场景在本地调试和内嵌 Web 控制台里特别常见后端是 mongoose 的mg_http_listen前端是浏览器里跑的页面两边端口不一样浏览器就按同源策略把响应拦下来了。先把概念说清楚不然后面配置容易瞎加。同源指的是协议、域名、端口三者完全一致。你的页面在http://127.0.0.1:5500接口在http://127.0.0.1:8000端口不同就是跨域。浏览器对跨域请求分两类处理简单请求直接发出去但会检查响应头里有没有Access-Control-Allow-Origin非简单请求比如Content-Type: application/json的 POST会先发一个OPTIONS预检请求问服务器“我能不能用这个方法和这些头来请求你”服务器必须正确回应这个 OPTIONS浏览器才发真正的 POST。mongoose 内置的 HTTP 服务本身不区分请求方法它把每个请求都交给你的回调处理。问题就出在这很多人只在处理 GET/POST 的回调里加了响应头但预检的 OPTIONS 请求进来时回调里可能直接走了业务逻辑分支没返回任何 CORS 头或者返回了 404浏览器一看预检失败真正的 POST 根本不会发出去。所以你会看到 Postman 正常Postman 不做同源检查也不发预检浏览器 POST 挂掉。这篇面向的是本地调试和内嵌 Web 控制台场景目标很明确让 mongoose 的 HTTP 服务正确响应 CORS包括预检 OPTIONS让浏览器里的 POST 从失败变成 200。我会给出可复制的 mongoose 配置片段和响应头注入代码再用 curl 和 DevTools 演示验证过程。如果你后面要把请求统一走一个出口做鉴权和转发也可以把 CORS 头集中在那层处理思路是一样的。适合谁看用 mongoose 写过 HTTP 服务、被浏览器跨域卡住的人做嵌入式设备 Web 控制台、前端页面直接调设备接口的人以及想把 CORS 处理从业务代码里抽出来、统一到出口层的人。下面从最小可复现的例子开始一步步把问题拆开。2. TaoToken 前置统一出口与 Key 准备在动手改 mongoose 代码之前先说清楚为什么要把 CORS 响应头“改到统一出口”。本地调试时你可以在每个 mongoose 回调里手写Access-Control-Allow-Origin但一旦接口多了、方法多了、还要处理预检散落在各处的响应头很容易漏。更稳的做法是mongoose 服务只负责业务所有跨域相关的头在一个统一的地方注入或者干脆让请求先经过一个统一出口由出口决定放行哪些来源、哪些方法、哪些头。如果你只是本地调试统一出口可以就是 mongoose 回调入口处的一个函数所有请求先过它。如果你要把请求转发到模型服务做进一步处理比如内嵌控制台里让用户直接问模型、或者做代码补全那这个出口就可以是 TaoToken 的 API 网关。它的作用是给你一个稳定的 Base URL 和 Key请求统一从这走CORS、鉴权、模型路由都在这一层收敛mongoose 那边只需要把响应头透传好。TaoToken 的官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 这个地址后面不加 UTM 参数。你需要准备的东西就三样Base URL、API Key、Model ID。这三件套在后面的配置片段里会反复出现尤其是当你要把 mongoose 服务和模型调用串起来的时候。拿 Key 的路径进控制台找到 API Keys 页面新建一个 Key复制出来存好。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite API Keys 页面是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。如果你要验证模型能不能通可以用模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 直接发一条消息试试。长期做编码或 Agent 的话Coding Plan 页面在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。这里要强调一点TaoToken 是正常的 API 服务出口不是任何形式的非法中转所有请求都走官方 API 地址。你在 mongoose 里配置的时候Base URL 就写https://taotoken.net/apiKey 用你刚建的那个Model ID 按文档里给的填。这三样东西配好后面无论是本地调试还是内嵌控制台请求都能统一从这走。为什么要在讲 CORS 的文章里提这个因为很多人解决 CORS 的方式是“在每个接口里加头”加着加着就乱了。更清晰的分层是mongoose 负责把 CORS 头正确返回给浏览器统一出口负责鉴权和转发。两层各司其职调试的时候也容易定位——浏览器报 CORS就看 mongoose 这层的响应头请求到了出口报 401就看 Key。下面进入具体配置。3. 可复制配置mongoose 响应头注入与统一出口先给一个最小可复现的 mongoose HTTP 服务然后在这个基础上加 CORS 处理。假设你的服务监听 8000 端口前端页面在 5500 端口。#include mongoose.h static const char *s_listen_on http://0.0.0.0:8000; // 统一注入 CORS 响应头 static void add_cors_headers(struct mg_connection *c) { mg_http_reply(c, 200, NULL, ); // 占位实际用下面的方式 }上面这个写法是错的mg_http_reply会直接把响应发出去没法先加头再发。正确做法是用mg_printf手动构造响应或者用mg_http_reply的 headers 参数。mongoose 的mg_http_reply签名是mg_http_reply(c, status_code, headers, body_fmt, ...)headers 是一个字符串可以放多行头。所以统一注入可以这样写static void send_cors_reply(struct mg_connection *c, int status, const char *body) { mg_http_reply(c, status, Access-Control-Allow-Origin: *\r\n Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n Access-Control-Allow-Headers: Content-Type, Authorization\r\n Access-Control-Max-Age: 86400\r\n Content-Type: application/json\r\n, %s, body); }注意这里Access-Control-Allow-Origin用了*本地调试够用。生产环境建议换成具体来源比如http://127.0.0.1:5500因为带凭证的请求不允许用*。Access-Control-Allow-Methods列出你实际用到的方法Access-Control-Allow-Headers列出前端会带的头Access-Control-Max-Age让浏览器缓存预检结果减少 OPTIONS 请求。然后在事件处理里所有请求先判断是不是 OPTIONS是的话直接返回 204 加 CORS 头不进业务逻辑static void fn(struct mg_connection *c, int ev, void *ev_data) { if (ev MG_EV_HTTP_MSG) { struct mg_http_message *hm (struct mg_http_message *) ev_data; // 预检请求直接回 204带 CORS 头 if (mg_strcmp(hm-method, mg_str(OPTIONS)) 0) { mg_http_reply(c, 204, Access-Control-Allow-Origin: *\r\n Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n Access-Control-Allow-Headers: Content-Type, Authorization\r\n Access-Control-Max-Age: 86400\r\n, ); return; } // 业务路由 if (mg_strcmp(hm-uri, mg_str(/api/echo)) 0 mg_strcmp(hm-method, mg_str(POST)) 0) { send_cors_reply(c, 200, {\ok\:true}); return; } // 其他请求也带上 CORS 头避免 GET 被拦 send_cors_reply(c, 404, {\error\:\not found\}); } }主函数里正常起服务int main(void) { struct mg_mgr mgr; mg_mgr_init(mgr); mg_http_listen(mgr, s_listen_on, fn, NULL); for (;;) mg_mgr_poll(mgr, 1000); mg_mgr_free(mgr); return 0; }编译运行gcc -o server server.c mongoose.c -I. ./server到这里mongoose 这层的 CORS 就处理好了。接下来是统一出口的配置。如果你要把请求转发到 TaoToken可以在业务路由里加一段转发逻辑把 Base URL、Key、Model ID 三件套用上。配置片段用 JSON 表示路径按你项目实际来比如config/taotoken.json{ base_url: https://taotoken.net/api, api_key: sk-你的Key, model_id: 你的ModelID, timeout_ms: 30000 }如果你用的是 Claude Code 这类工具配置通常放在~/.claude/settings.json或项目里的.claude/settings.json片段长这样{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key, ANTHROPIC_MODEL: 你的ModelID } }Codex 的话auth.json里放 Key配置里放 Base URL 和 Model ID{ base_url: https://taotoken.net/api, api_key: sk-你的Key, model: 你的ModelID }Cline 的 MCP 配置也是同样的三件套Base URL 写https://taotoken.net/apiKey 和 Model ID 按文档填。这里的关键是无论你用哪种工具Base URL、Key、Model ID 这三样必须齐全缺一个就会报 401 或者模型找不到。mongoose 那边把 CORS 头透传好出口这边把鉴权和转发做好浏览器里的请求就能从 CORS error 变成 200。4. 验证请求curl 与 DevTools 看跨域从失败到 200配置写完必须验证。先用 curl 看响应头因为 curl 不做同源检查能直接看到服务器返回了什么。先测预检 OPTIONScurl -i -X OPTIONS http://127.0.0.1:8000/api/echo \ -H Origin: http://127.0.0.1:5500 \ -H Access-Control-Request-Method: POST \ -H Access-Control-Request-Headers: Content-Type期望看到HTTP/1.1 204 No Content Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 86400如果Access-Control-Allow-Origin缺失说明 OPTIONS 分支没走到检查你的mg_strcmp(hm-method, mg_str(OPTIONS))判断。如果返回 404说明 OPTIONS 被当成业务请求处理了没进预检分支。再测真正的 POSTcurl -i -X POST http://127.0.0.1:8000/api/echo \ -H Origin: http://127.0.0.1:5500 \ -H Content-Type: application/json \ -d {msg:hello}期望看到 200 和Access-Control-Allow-Origin: *。curl 这关过了说明服务器端配置没问题。然后到浏览器验证。打开 DevTools 的 Network 面板勾选 Preserve log在前端页面触发 POST。你会看到两条请求第一条是 OPTIONS第二条是 POST。点开 OPTIONS看 Response Headers 里有没有Access-Control-Allow-OriginStatus 是不是 204。点开 POSTStatus 应该是 200Response Headers 里同样有 CORS 头。如果 OPTIONS 是 204 但 POST 还是 CORS error检查 POST 的响应头里Access-Control-Allow-Origin是不是漏了——很多人只在 OPTIONS 分支加了头POST 分支忘了加。Console 面板里失败时你会看到类似Access to fetch at http://127.0.0.1:8000/api/echo from origin http://127.0.0.1:5500 has been blocked by CORS policy: Response to preflight request doesnt pass access control check。成功时这条红字消失fetch 的 promise resolve你能拿到{ok:true}。如果你把请求转发到 TaoToken验证方式类似curl 直接打https://taotoken.net/api的对应端点带上Authorization: Bearer sk-你的Key看返回是不是 200。浏览器里则看 Network 里那条请求的 Status 和 Response。注意 API 地址是https://taotoken.net/api不要加 UTM 参数UTM 只加在官网和 deep link 上。实测下来最容易出问题的是预检请求的响应码。有些服务器对 OPTIONS 返回 200 带 body浏览器也能接受但规范推荐 204 无 body。另外Access-Control-Allow-Headers如果前端带了Authorization这里必须列出来否则预检会失败报Request header field authorization is not allowed by Access-Control-Allow-Headers。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth跨域配好之后请求能到出口了但出口这层还会报别的错。下面按真实报错逐个排查。401 Unauthorized。最常见。原因就三个Key 没填、Key 填错、Key 没带上。检查你的配置里api_key是不是sk-开头请求头里有没有Authorization: Bearer sk-你的Key。如果你用的是 Claude Code 的 settings.json确认ANTHROPIC_API_KEY拼写正确没有多余空格。Codex 的auth.json里 Key 字段名要对不同版本可能叫api_key或openai_api_key按文档来。401 的响应体通常会写invalid api key或missing authorization header照着改。local proxy failed。这个报错一般出现在你本地起了代理层、但代理层连不上上游的时候。检查你的 Base URL 是不是写成了https://taotoken.net/api有没有多写斜杠或者少写/api。如果你在 mongoose 里做转发检查转发目标 URL 拼接对不对别把路径重复拼了。另外确认你的网络能正常访问https://taotoken.net/api用 curl 直接打一下最直观。reading choices 报错。这个通常出现在解析模型响应的时候报错信息类似cannot read property choices of undefined或者reading choices。原因是响应体不是预期的 JSON 结构可能是 401 返回了错误信息你的代码却当成正常响应去解析choices。排查方法先把原始响应打印出来看是不是{error:...}。如果是先解决 401。另外确认 Model ID 填对了模型不存在时返回的结构也不一样。OAuth 相关报错。如果你用的是需要 OAuth 的工具报错可能是OAuth token expired或invalid_grant。这类问题跟 CORS 无关是鉴权层的事。检查你的 token 有没有过期重新走一遍授权流程。如果你用的是 API Key 方式就不会遇到 OAuth 问题建议本地调试优先用 Key。还有一个容易忽略的CORS 头加了但浏览器还是报错提示The value of the Access-Control-Allow-Origin header in the response must not be the wildcard * when the requests credentials mode is include。这是因为前端 fetch 带了credentials: include此时Access-Control-Allow-Origin不能是*必须写成具体来源比如http://127.0.0.1:5500同时还要加Access-Control-Allow-Credentials: true。本地调试如果不需要带 cookie把credentials去掉就行。对照这些报错你会发现大部分问题不在 CORS 本身而在出口层的鉴权和配置。把 Base URL、Key、Model ID 三件套对齐401 和 reading choices 基本就没了。CORS 那层只要 OPTIONS 和 POST 都正确返回头浏览器就不会拦。6. 统一出口的 CTA 与接入路径把 CORS 头收到统一出口之后mongoose 那边的代码会干净很多业务回调只管业务跨域和鉴权都在出口层处理。本地调试时你可以先用*放行所有来源快速验证功能等功能稳定了再把Access-Control-Allow-Origin换成具体的前端地址加上Access-Control-Allow-Credentials收紧权限。如果你要把请求接到模型服务按场景选入口。只是排障和接入先看 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 拿 Key再看接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 对齐 Base URL 和 Model ID。想先验证模型通不通用模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 发一条消息最快。长期做编码或 Agent直接看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。Claude Code 相关的接入看 https://taotoken.net/claude-code?utm_sourcetaotoken_aicg_blog_endutm_contentclaude-codeutm_campaignrewrite Anthropic 兼容接入看 https://taotoken.net/anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentanthropicutm_campaignrewrite 。最后留一个实用技巧在 mongoose 里加一个调试开关把每个请求的方法、URI、Origin 头和返回的 CORS 头打到日志里。这样浏览器一报错你直接看服务端日志就知道 OPTIONS 有没有进来、头有没有加上比在 DevTools 里来回翻快得多。CORS 这个问题配一次理顺了后面基本不会再卡。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑