1

I'm trying to create a Calendar through the Google API as per the documentation. I am trying to avoid using the Client libraries and do all communication with the API through custom webrequests and so far that has been working nicely but on this particular one I'm struggling with a "parse error".

Please do not refer to solutions that use the client libraries (service.calendars().insert(...)).

This is a dumbed down version of my code (still not working):

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();

As you can see, I've given up on sending serialized objects for now and I'm just trying to get it working with a very simple dummy request:

{
"summary": "test123"
}

Yet the response is still just:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "parseError",
    "message": "Parse Error"
   }
  ],
  "code": 400,
  "message": "Parse Error"
 }
}

The accessToken is valid and not expired, the application key is correct.

What am I doing wrong or missing?

Thanks in advance,

Jan Van Herck
  • 2,254
  • 17
  • 15
  • 1
    Hi there just out of interest why are you adverse to using the client libraries? – David Waters Dec 08 '12 at 00:51
  • Because it just adds another layer of complexity to an already quite complicated project, because we'd have to include code that is not under our control and that we perhaps can't fix ourselves when it bugs and/or breaks, and because REST services are, by definition, supposed to be simple and straightforward. Our code also synchronizes with FB and Twitter using the same no-nonsense approach, it is not difficult at all unless, like in this case, the documentation is wrong. – Jan Van Herck Dec 10 '12 at 07:47

2 Answers2

2

I'm not sure this will fix your problem but a couple of things to note In this case don't use Environment.NewLine, your network traffic should not change if your code is running on Windows, Mac or Linux. Http 1.1 requires CRLF

You are encoding the body of the post as UTF-8 yes you are not telling the server which encoding you are using. All your charecters are low bit ASCII so this should not matter but for completeness your Content type should be

 httpWebRequest.ContentType = "application/json ; charset=UTF-8";

Other then that I can not see issue with your code, it may be good to attache a transparent echoing proxy (Charles or fiddler) so you can see what your request looks like over the wire. From Calendars examples they are sending

Request

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"
}

Response

200 OK

- Show headers -

{

 "kind": "calendar#calendar",
 "etag": "\"NybCyMgjkLQM6Il-p8A5652MtaE/ldoGyKD2MdBs__AsDbQ2rHLfMpk\"",
 "id": "google.com_gqua79l34qk8v30bot94celnq8@group.calendar.google.com",
 "summary": "Test Calendar"
}

Hope that helps, aware that it may not.

David Waters
  • 11,979
  • 7
  • 41
  • 76
  • Thank you for the suggestions. This project has been put on hold for a little while but I'll get back to it in a few days and post an update. – Jan Van Herck Dec 11 '12 at 07:36
1

I figured it out and got it working!

While David's suggestions weren't the solution by themselves, he put me on the right track by telling me to use a packet sniffer (I ended up using Wireshark but that's not really the point).

As it turns out, there were two errors in my dumbed down code. One so glaringly obvious that it makes me blush, one slightly more devious.

First of all,

using (var streamWriter = new StreamWriter(stream))
{
    streamWriter.Write(Encoding.UTF8.GetBytes(requestText));
}

should of course be

using (var streamWriter = new StreamWriter(stream, Encoding.UTF8))
{
    streamWriter.Write(requestText);
}

as streamWriter.Write does a ToString() on the parameter and Byte[].ToString() just returns "System.Byte[]". Embarassing!

Secondly, the default UTF8 encoding adds the byte order mark \357\273\277, which also renders the content invalid as far as google is concerned. I found how to fix this problem here on stackoverflow.

So for anyone struggling with this, here is the final solution.

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
(
    "\r\n",
    "{",
    " \"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);
}

Hope this helps someone!

Community
  • 1
  • 1
Jan Van Herck
  • 2,254
  • 17
  • 15