"Недостающее время окончания" с Календарем Google и Пакетом Google Ракетки
Я использую библиотеку Google API для Racket, чтобы попытаться обновить Календарь Google. (API неполон, поэтому я расширяю его по мере продвижения.)
Кажется, у меня проблемы с добавлением событий в календарь с помощью метода events.insert. Код, который выполняет вставку, выглядит следующим образом:
(define (insert-events calendar-id
event
#:token [t #f]
#:max-attendees [max-attendees #f]
#:send-notifications [send-notifications #f]
#:supports-attachments [supports-attachments #f])
(json-api-post (string->url
(format
"https://www.googleapis.com/calendar/v3/calendars/~a/events"
(form-urlencoded-encode calendar-id)))
event
#:token t))
(define (json-api-post u b
#:token [t #f]
#:headers [headers '()])
(define b* (jsexpr->bytes b))
(let retry ([t t]
[retry-counter 0])
(parse-json-response
(POST-string u b* (if t (cons (token->authorization-header t) headers) headers))
retry
t
retry-counter)))
(define (POST-string u b headers)
(port->string (post-pure-port u b headers)))
Однако независимо от того, как я использую вызов, я всегда получаю сообщение об ошибке 400 с сообщением: "Отсутствует время окончания". Я проверил этот вопрос, чтобы убедиться, что я отправил свой запрос правильно. Которым я являюсь. Для справки, объект JSON, который я отправляю:
{
"end": {
"dateTime": "2016-05-30T14:00:00-04:00"
},
"start": {
"dateTime": "2016-05-30T13:00:00-04:00"
}
}
Кроме того, чтобы убедиться, что у меня был правильный доступ к правильному ключу и идентификатору календаря, я настроил эхо-сервер для своей локальной машины и изменил URL с google.com
в localhost
мой ответ кажется нормальным:
POST /<Calendar-Id-Redacted> HTTP/1.1
Host: localhost
User-Agent: Racket/6.5.0.5 (net/http-client)
Content-Length: 116
Authorization: Bearer <Key-Redacted>
{
"end": {
"dateTime": "2016-05-30T14:00:00-04:00"
},
"start": {
"dateTime": "2016-05-30T13:00:00-04:00"
}
}
Кажется, я все делаю правильно. И даже если в моем коде Racket была ошибка, отправка точно такого же объекта JSON через веб-консоль разработчика Google, кажется, работает как задумано. Так почему отправка этого конкретного POST не работает?
1 ответ
Близко. Но на самом деле вам не хватает одного куска важных данных в поле заголовков вашего запроса POST. А именно, строка:
Content-Type: application/json
Делая весь ваш запрос:
POST /<Calendar-Id-Redacted> HTTP/1.1
Host: localhost
User-Agent: Racket/6.5.0.5 (net/http-client)
Content-Length: 116
Authorization: Bearer <Key-Redacted>
Content-Type: application/json
{
"end": {
"dateTime": "2016-05-30T14:00:00-04:00"
},
"start": {
"dateTime": "2016-05-30T13:00:00-04:00"
}
}
По какой-то причине Google не выдаст вам приятного сообщения об ошибке, если вы его пропустили, но если он у вас есть, Google будет следовать вызову API.
Это можно увидеть более четко, когда вы запускаете команду в curl, если вы запускаете ее так (где у вас есть файл с именем data.txt
в котором есть ваш объект json:
curl -X POST -d @data.txt https://www.googleapis.com/calendar/v3/calendars/<Calendar-Id-Redacted>/events --header 'Authorization: Bearer <Key-Redacted>'
Это не удастся. Но если вы добавите заголовок с --header "Content-Type: application/json"
API будет работать как положено:
curl -X POST -d @data.txt https://www.googleapis.com/calendar/v3/calendars/<Calendar-Id-Redacted>/events --header 'Authorization: Bearer <Key-Redacted>' --header "Content-Type: application/json"
Для добавления этого заголовка в имеющуюся библиотеку требуется одна модификация функции, которая генерирует ваш почтовый запрос:
(define (json-api-post u b
#:token [t #f]
#:headers [headers '("Content-Type: application/json")])
....)
Он точно такой же, как и предыдущий, за исключением того, что мы изменили поле заголовков, чтобы оно содержало (по умолчанию) список со строкой: "Content-Type: application/json"
, С этим изменением Google должен принять ваш API. (Обратите внимание, что есть много других способов изменить функцию заголовков, это просто простой способ сделать это.)
Собрав все вместе, ваш окончательный код должен выглядеть примерно так:
(define (insert-events calendar-id
event
#:token [t #f]
#:max-attendees [max-attendees #f]
#:send-notifications [send-notifications #f]
#:supports-attachments [supports-attachments #f])
(json-api-post (string->url
(format
"https://www.googleapis.com/calendar/v3/calendars/~a/events"
(form-urlencoded-encode calendar-id)))
event
#:token t))
(define (json-api-post u b
#:token [t #f]
#:headers [headers '("Content-Type: application/json")])
(define b* (jsexpr->bytes b))
(let retry ([t t]
[retry-counter 0])
(parse-json-response
(POST-string u b* (if t (cons (token->authorization-header t) headers) headers))
retry
t
retry-counter)))
(define (POST-string u b headers)
(port->string (post-pure-port u b headers)))
Надеюсь, это поможет.