我正在尝试根据文档通过Google API创建日历。我试图避免使用客户端库并通过自定义网络请求与 API 进行所有通信,到目前为止,这一直运行良好,但在这个特定的问题上,我正在努力解决"解析错误"。
请不要引用使用客户端库的解决方案(service.calendars().insert(...))。
这是我代码的愚蠢版本(仍然无法正常工作):
var url = string.Format
(
"https://www.googleapis.com/calendar/v3/calendars?key={0}",
application.Key
);
var httpWebRequest = HttpWebRequest.Create(url) as HttpWebRequest;
httpWebRequest.Headers["Authorization"] =
string.Format("Bearer {0}", user.AccessToken.Token);
httpWebRequest.Method = "POST";
httpWebRequest.ContentType = "application/json";
httpWebRequest.CookieContainer = new CookieContainer();
// Obviously the real code will serialize an object in our system.
// I'm using a dummy request for now,
// just to make sure that the problem is not the serialization.
var requestText =
"{" + Environment.NewLine
+ ""summary": "test123"" + Environment.NewLine
+ "}" + Environment.NewLine
;
using (var stream = httpWebRequest.GetRequestStream())
using (var streamWriter = new System.IO.StreamWriter(stream))
{
streamWriter.Write(System.Text.Encoding.UTF8.GetBytes(requestText));
}
// GetSafeResponse() is just an extension that catches the WebException (if any)
// and returns the WebException.Response instead of crashing the program.
var httpWebResponse = httpWebRequest.GetSafeResponse();
如您所见,我现在已经放弃了发送序列化对象,我只是想让它使用一个非常简单的虚拟请求:
{
"summary": "test123"
}
然而,回应仍然只是:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "parseError",
"message": "Parse Error"
}
],
"code": 400,
"message": "Parse Error"
}
}
访问令牌有效且未过期,应用程序密钥正确。
我做错了什么或错过了什么?
提前感谢,
我不确定这会解决您的问题,但需要注意几点在这种情况下,不要使用 Environment.NewLine,如果您的代码在 Windows、Mac 或 Linux 上运行,您的网络流量不应更改。Http 1.1 需要 CRLF
您将帖子的正文编码为 UTF-8,是的,您没有告诉服务器您正在使用哪种编码。你所有的追逐者都是低位ASCII,所以这无关紧要,但为了完整性,你的内容类型应该是
httpWebRequest.ContentType = "application/json ; charset=UTF-8";
除此之外,我看不到您的代码有问题,最好附加一个透明的回显代理(查尔斯或小提琴手),这样您就可以通过网络查看您的请求是什么样子的。从他们发送的日历示例中
请求
POST https://www.googleapis.com/calendar/v3/calendars?key={YOUR_API_KEY}
Content-Type: application/json
Authorization: Bearer ya29.AHES6ZR3F6ByTg1eKVkjegKyWIukodK8KGSzY-ea1miGKpc
X-JavaScript-User-Agent: Google APIs Explorer
{
"summary": "Test Calendar"
}
响应
200 OK
- Show headers -
{
"kind": "calendar#calendar",
"etag": ""NybCyMgjkLQM6Il-p8A5652MtaE/ldoGyKD2MdBs__AsDbQ2rHLfMpk"",
"id": "google.com_gqua79l34qk8v30bot94celnq8@group.calendar.google.com",
"summary": "Test Calendar"
}
希望有帮助,意识到它可能不会。
通了,让它工作了!
虽然大卫的建议本身并不是解决方案,但他告诉我使用数据包嗅探器,让我走上了正确的轨道(我最终使用了Wireshark,但这不是真正的重点)。
事实证明,我愚蠢的代码中有两个错误。一个如此明显,以至于让我脸红,一个稍微狡猾。
首先
using (var streamWriter = new StreamWriter(stream))
{
streamWriter.Write(Encoding.UTF8.GetBytes(requestText));
}
当然应该是
using (var streamWriter = new StreamWriter(stream, Encoding.UTF8))
{
streamWriter.Write(requestText);
}
就像 streamWriter.Write 在参数和 Byte[] 上执行 ToString() 操作。ToString() 只返回 "System.Byte[]"。尴尬!
其次,默认的UTF8编码添加了字节顺序标记\357\273\277,这也使内容在谷歌方面无效。我找到了如何在堆栈溢出上解决此问题的方法。
因此,对于任何为此苦苦挣扎的人来说,这是最终的解决方案。
var url = string.Format
(
"https://www.googleapis.com/calendar/v3/calendars?key={0}",
application.Key
);
var httpWebRequest = HttpWebRequest.Create(url) as HttpWebRequest;
httpWebRequest.Headers["Authorization"] =
string.Format("Bearer {0}", user.AccessToken.Token);
httpWebRequest.Method = "POST";
// added the character set to the content-type as per David's suggestion
httpWebRequest.ContentType = "application/json; charset=UTF-8";
httpWebRequest.CookieContainer = new CookieContainer();
// replaced Environment.Newline by CRLF as per David's suggestion
var requestText = string.Join
(
"rn",
"{",
" "summary": "Test Calendar 123"",
"}"
);
using (var stream = httpWebRequest.GetRequestStream())
// replaced Encoding.UTF8 by new UTF8Encoding(false) to avoid the byte order mark
using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(false)))
{
streamWriter.Write(requestText);
}
希望这对某人有所帮助!