Unity调用OpenAI API持续出现HTTP 400错误的求助
Unity调用OpenAI API出现HTTP 400 Bad Request错误排查与解决
问题描述
在Unity新项目中调用OpenAI API,无论使用直接JSON还是WWWForm方式,均返回HTTP/1.1 400 Bad Request错误。终端使用-k参数调用API可正常工作,但Unity中始终报错。以下是两个版本的测试代码:
版本1(调用/v1/completions接口)
using UnityEngine; using UnityEngine.Networking; using System.Collections; using System.Collections.Generic; public class OpenAICommunication : MonoBehaviour { private string apiKey = "sk-....."; // 隐藏密钥 private string apiUrl = "https://api.openai.com/v1/completions"; private void Update() { if(Input.GetButtonDown("Jump")) AskQuestion("Hola! Este es un juego de unity!"); } IEnumerator SendQuestion(string question) { MessagesList messages = new MessagesList(); messages.list = new Message[] { new Message() }; messages.list[0].role = "user"; messages.list[0].content = question; ApiCall apiCall = new ApiCall(); apiCall.model = "text-davinci-003"; apiCall.prompt = question; string json = JsonUtility.ToJson(apiCall); Debug.Log("JSON: " + json); // 配置授权头 Dictionary<string, string> headers = new Dictionary<string, string> { { "Content-Type", "application/json" }, { "Authorization", "Bearer " + apiKey } }; // 发送POST请求到OpenAI API using (UnityWebRequest www = UnityWebRequest.PostWwwForm(apiUrl, json)) { foreach (var header in headers) { Debug.Log("HEADER: " + header.Key + header.Value); www.SetRequestHeader(header.Key, header.Value); } yield return www.SendWebRequest(); if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError) { Debug.LogError("请求错误: " + www.error); } else { string responseJson = www.downloadHandler.text; // 处理响应JSON(提取并显示助手回复) Debug.Log("响应: " + responseJson); } } } public void AskQuestion(string question) { StartCoroutine(SendQuestion(question)); } } [System.Serializable] public class Message { public string role; public string content; } public class MessagesList { public Message[] list; } public class ApiCall { public string model; public string prompt; }
版本2(调用/v1/chat/completions接口)
using UnityEngine; using UnityEngine.Networking; using System.Collections; using System.Collections.Generic; public class OpenAICommunication : MonoBehaviour { private string apiKey = "sk-...."; // 隐藏密钥 private string apiUrl = "https://api.openai.com/v1/chat/completions"; private void Update() { if (Input.GetButtonDown("Jump")) AskQuestion("Hola! Este es un juego de unity!"); } IEnumerator SendQuestion(string question) { // 配置授权头 Dictionary<string, string> headers = new Dictionary<string, string> { { "Content-Type", "application/json" }, { "Authorization", "Bearer " + apiKey } }; MessagesList messages = new MessagesList(); messages.list = new Message[] { new Message() }; messages.list[0].role = "user"; messages.list[0].content = question; string json = JsonUtility.ToJson(messages); // 删除不需要的字符串部分以格式化JSON // 删除的是:{ list: .... 和最后的 } json = json.Substring(8, json.Length - 8 - 1); // 验证格式正确 Debug.Log("JSON: " + json); // 准备POST请求数据,同时尝试chat接口但无变化... WWWForm form = new WWWForm(); form.AddField("model", "gpt-3.5-turbo"); form.AddField("messages", json); // 发送POST请求到OpenAI API using (UnityWebRequest www = UnityWebRequest.Post(apiUrl, form)) { foreach (var header in headers) { Debug.Log("HEADER: " + header.Key + header.Value); www.SetRequestHeader(header.Key, header.Value); } yield return www.SendWebRequest(); if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError) { Debug.LogError("请求错误: " + www.error); } else { string responseJson = www.downloadHandler.text; // 处理响应JSON(提取并显示助手回复) Debug.Log("响应: " + responseJson); } } } public void AskQuestion(string question) { StartCoroutine(SendQuestion(question)); } } [System.Serializable] public class Message { public string role; public string content; } public class MessagesList { public Message[] list; }
错误信息
请求错误: HTTP/1.1 400 Bad Request 0x00007ff62ae8229d (Unity) StackWalker::GetCurrentCallstack 0x00007ff62ae87249 (Unity) StackWalker::ShowCallstack 0x00007ff62be533a1 (Unity) GetStacktrace 0x00007ff62c514c02 (Unity) DebugStringToFile 0x00007ff629d98736 (Unity) DebugLogHandler_CUSTOM_Internal_Log 0x0000025ca0dfc363 (Mono JIT Code) (wrapper managed-to-native) UnityEngine.DebugLogHandler:Internal_Log (UnityEngine.LogType,UnityEngine.LogOption,string,UnityEngine.Object) 0x0000025ca0dfc27b (Mono JIT Code) UnityEngine.DebugLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[]) 0x0000025ca0dfbfc0 (Mono JIT Code) UnityEngine.Logger:Log (UnityEngine.LogType,object) 0x0000025c7c685ab5 (Mono JIT Code) UnityEngine.Debug:LogError (object) 0x0000025c7c67b363 (Mono JIT Code) OpenAICommunication/<SendQuestion>d__3:MoveNext () (at D:/Projects/Unity/AI_Project/Assets/Scripts/OpenAICommunication.cs:57) 0x0000025c7c67ab00 (Mono JIT Code) UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr) 0x0000025c7c67ac2f (Mono JIT Code) (wrapper runtime-invoke) <Module>:runtime_invoke_void_object_intptr (object,intptr,intptr,intptr) 0x00007ffcf45ee0d4 (mono-2.0-bdwgc) mono_jit_runtime_invoke (at C:/build/output/Unity-Technologies/mono/mono/mini/mini-runtime.c:3445) 0x00007ffcf452eb74 (mono-2.0-bdwgc) do_runtime_invoke (at C:/build/output/Unity-Technologies/mono/mono/metadata/object.c:3066) 0x00007ffcf452ed0c (mono-2.0-bdwgc) mono_runtime_invoke (at C:/build/output/Unity-Technologies/mono/mono/metadata/object.c:3113) 0x00007ff62ad99724 (Unity) scripting_method_invoke 0x00007ff62ad77944 (Unity) ScriptingInvocation::Invoke 0x00007ff62ad4041a (Unity) Coroutine::Run 0x00007ff62ad3de0f (Unity) Coroutine::ContinueCoroutine 0x00007ff62a9fb653 (Unity) AsyncOperation::InvokeCoroutine 0x00007ff62b33d7bc (Unity) UnityWebRequestAsyncOperation::InvokeCoroutine 0x00007ff62b33d9a1 (Unity) UnityWebRequestProto<UnityWebRequestTransport,AtomicRefCounter,RedirectHelper,ResponseHelper,DownloadHandler,UploadHandler,CertificateHandler,HeaderHelper,AsyncOperation>::Job_InvokeCoroutine 0x00007ff62a9aed9a (Unity) BackgroundJobQueue::ExecuteMainThreadJobs 0x00007ff62aa3127c (Unity) `InitPlayerLoopCallbacks'::`2'::EarlyUpdateExecuteMainThreadJobsRegistrator::Forward 0x00007ff62aa1133a (Unity) ExecutePlayerLoop 0x00007ff62aa114c6 (Unity) ExecutePlayerLoop 0x00007ff62aa17d85 (Unity) PlayerLoop 0x00007ff62b9dac6f (Unity) PlayerLoopController::InternalUpdateScene 0x00007ff62b9e784d (Unity) PlayerLoopController::UpdateSceneIfNeededFromMainLoop 0x00007ff62b9e5b51 (Unity) Application::TickTimer 0x00007ff62be59cda (Unity) MainMessageLoop 0x00007ff62be5f540 (Unity) WinMain 0x00007ff62d248bae (Unity) __scrt_common_main_seh 0x00007ffd46ea7614 (KERNEL32) BaseThreadInitThunk 0x00007ffd47c026b1 (ntdll) RtlUserThreadStart
排查与解决方案
1. 版本1的核心问题:PostWwwForm误用
UnityWebRequest.PostWwwForm会自动将请求内容格式化为application/x-www-form-urlencoded,但你手动设置了Content-Type: application/json,导致请求格式混乱。OpenAI API无法解析这种混合格式的请求,返回400错误。
修复方式:
替换PostWwwForm为UnityWebRequest.Post并手动设置上传处理器为JSON数据:
// 替换原有的UnityWebRequest创建代码 byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json); using (UnityWebRequest www = UnityWebRequest.Post(apiUrl, "")) { www.uploadHandler = new UploadHandlerRaw(jsonBytes); www.SetRequestHeader("Content-Type", "application/json"); www.SetRequestHeader("Authorization", "Bearer " + apiKey); yield return www.SendWebRequest(); // 后续错误处理和响应解析逻辑不变 }
2. 版本2的核心问题:WWWForm与JSON格式冲突
WWWForm会自动编码表单数据,而你将JSON字符串作为表单字段值传入,导致最终发送的请求体是URL编码后的内容,并非纯JSON。同时手动截取JSON字符串的操作容易出错,增加格式风险。
修复方式:
放弃WWWForm,直接构造符合Chat Completions接口要求的JSON请求体,并使用上传处理器发送纯JSON:
首先修正数据模型,直接创建符合OpenAI要求的结构:
[System.Serializable] public class ChatRequest { public string model; public Message[] messages; } [System.Serializable] public class Message { public string role; public string content; }
然后修改SendQuestion方法:
IEnumerator SendQuestion(string question) { ChatRequest request = new ChatRequest(); request.model = "gpt-3.5-turbo"; request.messages = new Message[] { new Message { role = "user", content = question } }; string json = JsonUtility.ToJson(request); Debug.Log("JSON: " + json); byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json); using (UnityWebRequest www = UnityWebRequest.Post(apiUrl, "")) { www.uploadHandler = new UploadHandlerRaw(jsonBytes); www.SetRequestHeader("Content-Type", "application/json"); www.SetRequestHeader("Authorization", "Bearer " + apiKey); yield return www.SendWebRequest(); if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError) { // 关键:打印完整响应内容,OpenAI会返回具体的错误原因 Debug.LogError("请求错误: " + www.error + "\n响应内容: " + www.downloadHandler.text); } else { string responseJson = www.downloadHandler.text; Debug.Log("响应: " + responseJson); } } }
3. 通用排查步骤
- 打印完整响应内容:400错误时,OpenAI的响应体中会包含具体的错误描述(比如缺少参数、格式错误),务必打印
www.downloadHandler.text,这是定位问题的关键。 - 验证JSON格式:将
Debug.Log输出的JSON复制到Postman或终端中测试,确认格式符合OpenAI接口文档要求。 - 检查API密钥权限:确保密钥有对应接口的调用权限,且额度充足。
- 避免手动修改JSON字符串:使用
JsonUtility或第三方JSON库自动序列化,减少手动操作导致的格式错误。
内容的提问来源于stack exchange,提问作者Esteban Gabriel Fernandez
相关产品推荐
相关产品推荐

