Ошибка 400 INVALID_ARGUMENT при запросе адресной книги - PullRequest
0 голосов
/ 18 июня 2019

Я настраиваю клиент Google Contacts CardDAV API.

OAuth 2.0 с использованием oauth2client.
Запрос с использованием requests.

from oauth2client import file, client, tools
import requests

SCOPES = 'https://www.googleapis.com/auth/carddav'
store = file.Storage('credentials.json')
creds = store.get()
if not creds or creds.invalid:
    flow = client.flow_from_clientsecrets('client_secret.json', SCOPES)
    creds = tools.run_flow(flow, store)
print(creds.access_token)

hed = {'Authorization': 'Bearer ' + creds.access_token}

response = requests.request('PROPFIND', 'https://www.googleapis.com/.well-known/carddav', headers=hed, allow_redirects=False)

if response.status_code == 301:
    location = response.headers['location']
    response = requests.request('PROPFIND', 'https://www.googleapis.com' + location, headers=hed)
    print(response.text)

Но когда я запрашиваю URL для получения адресной книги (я получаю его из заголовка Locationпервого запроса) возвращает ошибку:

{
  "error": {
    "code": 400,
    "message": "Request contains an invalid argument.",
    "status": "INVALID_ARGUMENT"
  }
}

Полная информация о запросах

Первый запрос

requests.request('PROPFIND', 'https://www.googleapis.com/.well-known/carddav', headers=hed, allow_redirects=False)

REQUEST
=======
endpoint: PROPFIND https://www.googleapis.com/.well-known/carddav
headers:
  User-Agent: python-requests/2.22.0
  Accept-Encoding: gzip, deflate
  Accept: */*
  Connection: keep-alive
  Authorization: Bearer ya29.***********************************************
  Content-Length: 0
=======
RESPONSE
========
status_code: 301
headers:
  Content-Type: text/plain; charset=UTF-8
  X-XSS-Protection: 1; mode=block
  X-Content-Type-Options: nosniff
  Expires: Mon, 01 Jan 1990 00:00:00 GMT
  Cache-Control: no-cache, no-store, max-age=0, must-revalidate
  X-Frame-Options: SAMEORIGIN
  Location: /carddav/v1/principals/<my_email>/lists/default/
  Pragma: no-cache
  Vary: Origin, X-Origin, Referer
  Date: Fri, 21 Jun 2019 11:43:23 GMT
  Server: ESF
  Content-Length: 0
  Alt-Svc: quic=":443"; ma=2592000; v="46,44,43,39"
========

Второй запрос

response = requests.request('PROPFIND', 'https://www.googleapis.com' + location, headers=hed)

REQUEST
=======
endpoint: PROPFIND https://www.googleapis.com/carddav/v1/principals/<my_email>/lists/default/
headers:
  User-Agent: python-requests/2.22.0
  Accept-Encoding: gzip, deflate
  Accept: */*
  Connection: keep-alive
  Authorization: Bearer ya29.***********************************************
  Content-Length: 0
=======
RESPONSE
========
status_code: 400
headers:
  Vary: Origin, X-Origin, Referer
  Content-Type: application/json; charset=UTF-8
  Date: Fri, 21 Jun 2019 11:43:23 GMT
  Server: ESF
  Content-Length: 127
  X-XSS-Protection: 0
  X-Frame-Options: SAMEORIGIN
  X-Content-Type-Options: nosniff
  Alt-Svc: quic=":443"; ma=2592000; v="46,44,43,39"
body:
  {
    "error": {
      "code": 400,
      "message": "Request contains an invalid argument.",
      "status": "INVALID_ARGUMENT"
    }
  }
========

1 Ответ

0 голосов
/ 22 июня 2019

Краткий ответ: метод PROPFIND является общим и должен включать тело, которое определяет, какую информацию сервер должен вернуть. Вы должны передать полезную нагрузку XML в теле запроса, который идентифицирует запрашиваемые вами свойства.

Получение URI адресных книг

В соответствии с Документами Google CardDav API ваш первый запрос является совершенным и перенаправит вас на ресурс адресной книги для текущего пользователя. Вот описание Google следующего шага:

Ваша клиентская программа может затем обнаружить основную адресную книгу, выполнив PROPFIND на addressbook-home-set и найдя ресурсы addressbook и collection.

Позвольте мне распаковать это: ваш второй запрос должен запросить список ресурсов пользователя, найденных в месте, которое вы получили из первого запроса. Чтобы правильно выполнить этот запрос, вам нужно передать тело XML с запросом PROPFIND, например:

    PROPFIND https://www.googleapis.com/carddav/v1/principals/<my_email>/lists/default/
    Authorization: Bearer ya29.***********************************************
    Depth: 1
    Content-Type: application/xml; charset=utf-8

    <D:propfind xmlns:D="DAV:">
      <D:prop>
         <D:resourcetype />
         <D:displayname />
      </D:prop>
    </D:propfind>

Здесь вы указываете свойства, которые вы хотите, чтобы сервер отвечал. Вы указываете свойство resourcetype, потому что вас интересуют только ресурсы addressbook или collection, которые содержат контакты.

Этот запрос вернет список URI для ресурсов, из которых вы можете выбрать любой, имеющий тип ресурса addressbook или collection.

На данный момент у вас нет контактов или даже URI контактов. У вас есть список URI для адресных книг пользователя или коллекций контактов. (Как правило, есть только один из них, но их может быть много.)

Вы не спрашивали, как получить контакты пользователя, но я предполагаю, что это ваша конечная цель, и мы продолжим выполнение следующих шагов.

Получение URI контактов

Ваш следующий набор запросов будет запрашивать каждый из URI адресной книги для URI их контактов. Выполните цикл по каждому результату из предыдущего запроса и выполните еще один запрос PROPFIND для URI с такой полезной нагрузкой:

    REPORT <addressbook_uri>
    Authorization: Bearer ya29.***********************************************
    Content-Type: application/xml; charset=utf-8

    <D:propfind xmlns:D="DAV:">
      <D:prop>
        <D:getetag />
        <D:getcontenttype />
      </D:prop>
    </D:propfind>

Здесь мы запрашиваем тип содержимого каждого элемента, чтобы мы могли определить, является ли он типом VCard. VCards являются законными контактными данными.

Теперь вы можете отфильтровать этот набор результатов по contenttype == 'text/vcard', чтобы получить новый список URI, указывающих на каждый контакт в адресной книге пользователя.

О, чувак, мы так близко.

Получить контактные карточки VCards

Наконец, соберите свой список URI с фактическими контактными данными и запросите данные с сервера.

Здесь вы сделаете запрос addressbook-multiget REPORT для получения пакета контактов в вашем списке. Google не сообщает, сколько контактных URI вы можете включить в свой запрос. Обычно я ограничиваю свой запрос несколькими сотнями за раз.

1059 * Е.Г. *

    REPORT <addressbook_uri>
    Authorization: Bearer ya29.***********************************************
    Content-Type: application/xml; charset=utf-8

    <C:addressbook-multiget xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav">
      <D:prop>
        <D:getetag/>
        <C:address-data>
          <C:allprop/>
        </C:address-data>
      </D:prop>
      <D:href>/carddav/v1/principals/<my_email>/lists/default/contact1.vcf</D:href>
      <D:href>/carddav/v1/principals/<my_email>/lists/default/contact2.vcf</D:href>
      ...
    </C:addressbook-multiget>

Ответ будет содержать данные VCard для каждого из контактов, упакованные в XML. Изучите текст XML, а затем проанализируйте данные VCard, чтобы, наконец, получить ваши контактные данные.

Готово!


Ресурсы:

...