Visual Studio 2022调用POST接口遇JObject解析错误及415状态码问题
问题排查:POST接口调用415错误导致Json解析失败
问题现象
调用POST接口时,在jsonObj = JObject.Parse(result);代码处抛出Newtonsoft.Json.JsonReaderException: "Error reading JObject from JsonReader. Path '', line 0, position 0."异常,捕获到的API响应为415 Unsupported Media Type。
接口响应信息
{StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: { Transfer-Encoding: chunked X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Pragma: no-cache X-Frame-Options: DENY Referrer-Policy: no-referrer Cache-Control: no-store, must-revalidate, no-cache, max-age=0 Date: Wed, 17 Jul 2024 08:47:38 GMT Server: nginx/1.24.0 Content-Type: application/json Expires: 0 }}
相关代码
LoginToken方法
private List<Token> LoginToken(string username, string password) { string api = ""; List<Token> list = new List<Token>(); string url = ""; Dictionary<string, string> dics = new Dictionary<string, string> { { "username", username }, { "password", password }, { "loginType", "account" }, { "grantType", "password" } }; Task<string> task = Api.InvokeWebapi(url, api, "POST", dics); string result = task.Result; if (result != null) { JObject jsonObj = null; jsonObj = JObject.Parse(result); DataInfo info = new DataInfo(); info.statusCode = Convert.ToInt32(jsonObj["code"]); info.message = jsonObj["message"].ToString(); if (info.statusCode == 1) { JArray jlist = JArray.Parse(jsonObj["data"].ToString()); for (int i = 0; i < jlist.Count; ++i) { Token ver = new Token(); JObject tempo = JObject.Parse(jlist[i].ToString()); ver.access_token = tempo["access_token"].ToString(); ver.token_type = tempo["token_type"].ToString(); ver.expires_in = Convert.ToInt32(tempo["expires_in"]); ver.scope = tempo["scope"].ToString(); ver.name = tempo["name"].ToString(); ver.my_Corp = tempo["my_Corp-Code"].ToString(); ver.userId = Convert.ToInt32(tempo["userId"]); ver.username = tempo["username"].ToString(); ver.jti = tempo["jti"].ToString(); list.Add(ver); } } } return list; }
InvokeWebapi方法
public async Task<string> InvokeWebapi(string url, string api, string type, Dictionary<string, string> dics) { string result = string.Empty; HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Basic 1111"); client.BaseAddress = new Uri(url); client.Timeout = TimeSpan.FromSeconds(510); if (type.ToLower() == "put") { HttpResponseMessage response; if (dics.Keys.Contains("input")) { if (dics != null) { foreach (var item in dics.Keys) { api = api.Replace(item, dics[item]).Replace("{", "").Replace("}", ""); } } var contents = new StringContent(dics["input"], Encoding.UTF8, "application/json"); response = client.PutAsync(api, contents).Result; if (response.IsSuccessStatusCode) { result = await response.Content.ReadAsStringAsync(); return result; } return result; } var content = new FormUrlEncodedContent(dics); response = client.PutAsync(api, content).Result; if (response.IsSuccessStatusCode) { result = await response.Content.ReadAsStringAsync(); return result; } } else if (type.ToLower() == "post") { var content = new FormUrlEncodedContent(dics); HttpResponseMessage response = client.PostAsync(api, content).Result; if (response.IsSuccessStatusCode) { result = await response.Content.ReadAsStringAsync(); return result; } } else if (type.ToLower() == "get") { HttpResponseMessage response = client.GetAsync(api).Result; if (response.IsSuccessStatusCode) { result = await response.Content.ReadAsStringAsync(); return result; } } else { return result; } return result; }
原因分析
- 415错误根源:当前POST请求使用
FormUrlEncodedContent发送数据,请求头Content-Type为application/x-www-form-urlencoded,但目标API仅支持application/json格式的请求体,服务器因此返回415不支持的媒体类型错误。 - Json解析失败:请求返回错误时,
InvokeWebapi方法未读取错误响应内容,直接返回空字符串。后续调用JObject.Parse(result)时,传入空字符串触发JsonReaderException。
解决方法
1. 修改POST请求的内容格式
将POST请求的FormUrlEncodedContent替换为StringContent,以JSON格式发送数据并设置正确的Content-Type:
else if (type.ToLower() == "post") { // 将字典序列化为JSON字符串 string jsonBody = JsonConvert.SerializeObject(dics); var content = new StringContent(jsonBody, Encoding.UTF8, "application/json"); HttpResponseMessage response = await client.PostAsync(api, content); // 无论响应是否成功,都读取响应内容 result = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { return result; } // 错误时返回响应内容,便于上层处理 return result; }
2. 统一处理响应内容
修改InvokeWebapi方法,所有请求分支都读取响应内容,避免空字符串导致解析错误:
// 以GET分支为例,其他分支同理 else if (type.ToLower() == "get") { HttpResponseMessage response = await client.GetAsync(api); result = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { return result; } return result; }
3. 优化异步调用方式
避免使用.Result阻塞线程,将LoginToken改为异步方法并使用await调用:
private async Task<List<Token>> LoginToken(string username, string password) { string api = ""; List<Token> list = new List<Token>(); string url = ""; Dictionary<string, string> dics = new Dictionary<string, string> { { "username", username }, { "password", password }, { "loginType", "account" }, { "grantType", "password" } }; string result = await Api.InvokeWebapi(url, api, "POST", dics); if (!string.IsNullOrEmpty(result)) { JObject jsonObj = JObject.Parse(result); DataInfo info = new DataInfo(); info.statusCode = Convert.ToInt32(jsonObj["code"]); info.message = jsonObj["message"].ToString(); if (info.statusCode == 1) { JArray jlist = (JArray)jsonObj["data"]; // 直接反序列化,简化代码 foreach (var item in jlist) { Token ver = item.ToObject<Token>(); list.Add(ver); } } } return list; }
内容的提问来源于stack exchange,提问作者Wenbin Geng
相关产品推荐
相关产品推荐

