我正在设置Google Contacts CardDAV API客户端。
使用oauth2client
的OAuth 2.0。
使用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"
}
}
========
答案 0 :(得分:0)
简短答案:PROPFIND
方法是通用的,必须包含一个主体,该主体指定服务器应返回的信息。您必须在请求的主体中传递XML有效载荷,以标识所请求的属性。
根据Google's CardDav API docs,您的第一个请求是完美的,并将您重定向到当前用户的通讯簿资源。这是Google对下一步的说明:
然后,您的客户端程序可以通过在
addressbook-home-set
上执行PROPFIND并查找addressbook
和collection
资源来发现主体地址簿。
让我解压此包:您的第二个请求应查询在您从第一个请求获得的位置找到的用户资源列表。要正确执行此查询,您需要通过PROPFIND
请求传递XML正文,如下所示:
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。循环遍历前一个查询的每个结果,并使用此有效负载在URI上发出另一个PROPFIND
请求:
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类型。电子名片是合法的联系记录。
现在,您可以按contenttype == 'text/vcard'
过滤这组结果,以获取指向用户通讯录中每个联系人的URI的新列表。
哦,天哪,我们越来越近了。
最后,将URI列表组装成实际的联系数据,并从服务器查询数据。
您将在此处发出addressbook-multiget
REPORT
请求,以检索列表中的一批联系人。 Google并未说明您可以在请求中包含多少个联系人URI。通常,我一次只能将请求限制在几百个。
例如
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>
响应将包含XML中打包的每个联系人的VCard数据。解析XML文本,然后解析VCard数据以最终检索您的联系方式。
完成!