调用gemini api提示User location is not supported for the API use.
众所周知,我的博客已经成功迁移到了越南的优质空间 Dataonline.vn。
伴随着这次迁移,我决定重新启用一个备受喜爱的老功能——AI 课代表。
这个功能的灵感来源于 冰剑 的 Gemini 助手,经过我的深度定制开发,已经成为博客的一个重要特色。
回顾它的技术演进历程,颇有些意思。
第一阶段:HEXO 时代的异步 JavaScript
最初,这个插件部署在 HEXO 博客上,托管在赛博大善人CloudFlare的怀抱中。那时候,我们用纯 JavaScript 异步调用来实现功能,简单而有效。
第二阶段:TYPECHO 的 PHP 尝试
后来转向 TYPECHO 平台后,我想着既然是 PHP 后端,何不用 PHP 来统一解决问题呢?于是开始了 PHP 版本的探索之旅。
第三阶段:遭遇地域限制的困境
前阵子在 ct8.pl 部署时,我采用了 冰剑的优雅解决方案,这个方案在gemini支持区域上运行得相当完美
新的挑战:地域限制的真面目
当我将服务迁移到 Dataonline.vn 后,却遭遇了意想不到的挫折。
系统返回了一个令人沮丧的错误:
User location is not supported for the API use.
对应的状态码是:
FAILED_PRECONDITION
这时我才恍然大悟:原来通过 CloudFlare 做跳转的方法,只能绕过网络访问限制(俗称"翻墙"),却无法绕过 Gemini API 自身的地域限制。这是两个完全不同层面的问题!
新的解决方案:Deno Deploy 闪亮登场
面对这个技术挑战,我决定祭出另一位技术界的大善人——Deno.com,让它来充当我们的技术跳板。
部署步骤详解
- 快速登录:可以直接使用 Google 账号或 GitHub 账号一键登录,非常便捷。
- 创建新项目:点击右上角醒目的蓝色 "New Playground" 按钮,创建一个全新的后端服务。
- 安全升级:添加 Token 验证保护, 如果按照这个思路就这样直接使用,任何知道你 API 地址的人都可以滥用你的服务。这显然不是我们想要的结果,为了保护自己的 API 接口,我们需要添加一个 Token 验证机制。
- 代码实现:系统会打开代码编辑器,将以下精心调试的代码复制进去:
// Gemini API 代理服务 - 带Token验证 - 适配 Deno Deploy Playground
// 直接复制此代码到 Deno Deploy 的 New Playground 中
import { serve } from "https://deno.land/[email protected]/http/server.ts";
// Gemini 官方接口地址
const GEMINI_API_BASE = "https://generativelanguage.googleapis.com";
// 你的私有 Token - 请设置一个只有你知道的值
const REQUIRED_TOKEN = "[这里自己设置一个别人不知道的值]";
// 处理 CORS 跨域问题
function corsHeaders() {
return {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization, x-goog-api-key",
"Access-Control-Max-Age": "86400",
};
}
// Token 验证函数
function validateToken(url: URL): boolean {
const token = url.searchParams.get('token');
return token === REQUIRED_TOKEN;
}
// 主处理函数
async function handler(req: Request): Promise<Response> {
const url = new URL(req.url);
// 处理 CORS 预检请求
if (req.method === "OPTIONS") {
return new Response(null, {
status: 200,
headers: corsHeaders(),
});
}
// 健康检查端点不需要 Token 验证
if (url.pathname === "/" || url.pathname === "/health") {
return new Response(
JSON.stringify({
status: "healthy",
message: "Gemini API 代理服务正在运行",
timestamp: new Date().toISOString(),
note: "API调用需要token参数",
usage: {
format: "?token=YOUR_TOKEN&key=YOUR_API_KEY",
example: `${url.origin}/v1/models/gemini-pro:generateContent?token=YOUR_TOKEN&key=YOUR_API_KEY`
}
}, null, 2),
{
status: 200,
headers: {
"Content-Type": "application/json",
...corsHeaders(),
},
}
);
}
// Token 验证 - 如果验证失败,直接返回 403 状态码
if (!validateToken(url)) {
return new Response(null, {
status: 403,
headers: corsHeaders(),
});
}
try {
// 构建目标 URL - 移除 token 参数,只保留原始 API 参数
const targetUrl = new URL(`${GEMINI_API_BASE}${url.pathname}`);
// 复制查询参数,但排除 token 参数
for (const [key, value] of url.searchParams.entries()) {
if (key !== 'token') {
targetUrl.searchParams.append(key, value);
}
}
// 准备请求头
const headers: Record<string, string> = {};
// 复制原始请求头,排除一些可能有问题的头部
for (const [key, value] of req.headers.entries()) {
const lowerKey = key.toLowerCase();
if (![
"host", "origin", "referer", "cf-ray", "cf-connecting-ip",
"cf-visitor", "x-forwarded-for", "x-forwarded-proto"
].includes(lowerKey)) {
headers[key] = value;
}
}
// 确保有 User-Agent
if (!headers["user-agent"] && !headers["User-Agent"]) {
headers["User-Agent"] = "Deno-Gemini-Proxy/1.0";
}
console.log(`代理请求: ${req.method} ${targetUrl.toString()}`);
// 构建代理请求选项
const requestOptions: RequestInit = {
method: req.method,
headers,
};
// 处理请求体
if (req.method !== "GET" && req.method !== "HEAD") {
requestOptions.body = req.body;
}
// 发送请求到 Gemini API
const response = await fetch(targetUrl.toString(), requestOptions);
console.log(`响应状态: ${response.status}`);
// 构建响应头
const responseHeaders: Record<string, string> = {};
// 复制响应头
for (const [key, value] of response.headers.entries()) {
responseHeaders[key] = value;
}
// 添加 CORS 头
Object.assign(responseHeaders, corsHeaders());
// 返回响应
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: responseHeaders,
});
} catch (error) {
console.error("代理请求失败:", error);
const errorResponse = {
error: {
code: 500,
message: `代理服务器错误: ${error.message}`,
status: "INTERNAL_ERROR",
timestamp: new Date().toISOString()
}
};
return new Response(JSON.stringify(errorResponse, null, 2), {
status: 500,
headers: {
"Content-Type": "application/json",
...corsHeaders(),
},
});
}
}
// 启动服务
serve(handler, { port: 8000 });
console.log("🚀 Gemini API 代理服务已启动!");
console.log("🔐 已启用Token验证保护");
console.log("📡 监听端口: 8000");
console.log("🔗 使用方法:");
console.log(" 需要在URL中添加token参数");
console.log(` 例如: https://your-app.deno.dev/v1/models/gemini-pro:generateContent?token=${REQUIRED_TOKEN}&key=YOUR_API_KEY`);
最终使用方法
有了 Token 保护后,你的 API 调用格式变成:
https://your-app.deno.dev/v1/models/{model}:generateContent?key={key}&token=<你填的那个一个别人不知道的值>
这样就可以安全地替代原来的:
https://generativelanguage.googleapis.com/v1/models/{model}:generateContent?key={key}
总结
通过这次技术升级,我们不仅解决了地域限制的问题,还加强了 API 的安全性。Deno Deploy 作为一个现代化的边缘计算平台,为我们提供了稳定可靠的代理服务。
AI 课代表重新上线啦! 🎉
现在你可以在任何地区愉快地使用 Gemini API 了。记得保护好你的 Token,不要泄露给不相关的人哦~
Enjoy coding! 🚀
已有 2 条评论