百度 AIP 開放平臺使用 OAuth2.0 授權調用開放 API,調用 API 時必須在 URL 中帶上 access_token 參數。
請求 URL 數據格式
授權服務地址:https://aip.baidubce.com/oauth/2.0/token
請求參數如下:
- grant_type: 必須參數,固定爲 client_credentials;
- client_id: 必須參數,應用的 API Key;
- client_secret: 必須參數,應用的 Secret Key;
獲取結果
服務器返回的JSON文本參數如下:
- access_token: 要獲取的 Access Token;
- expires_in: Access Token 的有效期(秒爲單位,一般爲 1 個月);
- 其他參數忽略,暫時不用;
以下代碼爲示例:
{
"refresh_token": "25.b55fe1d287227ca97aab219bb249b8ab.315360000.1798284651.282335-8574074",
"expires_in": 2592000,
"scope": "public wise_adapt",
"session_key": "9mzdDZXu3dENdFZQurfg0Vz8slgSgvvOAUebNFzyzcpQ5EnbxbF+hfG9DQkpUVQdh4p6HbQcAiz5RmuBAja1JJGgIdJI",
"access_token": "24.6c5e1ff107f0e8bcef8c46d3424a0e78.2592000.1485516651.282335-8574074",
"session_secret": "dfac94a3489fe9fca7c3221cbf7525ff"
}
若請求錯誤,服務器將返回的 JSON 文本包含以下參數:
- error: 錯誤碼;關於錯誤碼的詳細信息請參考下方鑑權認證錯誤碼。
- error_description: 錯誤描述信息,幫助理解和解決發生的錯誤。
以下爲請求錯誤返回結果:
{
"error": "invalid_client",
"error_description": "unknown client id"
}
error | error_description | 解釋 |
---|---|---|
invalid_client | unknown client id | API Key不正確 |
invalid_client | Client authentication failed | Secret Key不正確 |
C++ 代碼
#include <curl/curl.h>
#include <string>
#include <map>
#include <iostream>
#include <fstream>
#include <json/json.h>
// callback function for curl
size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *userdata)
{
std::string *str = dynamic_cast<std::string *>((std::string *)userdata);
str->append((char *)ptr, size * nmemb);
return size * nmemb;
}
// get access token from server by get method
std::string getTokenKey() {
std::string url = "https://aip.baidubce.com/oauth/2.0/token";
std::string apikey = "這裏更改爲對應應用的 API Key";
std::string secritkey = "這裏更改爲對應應用的 Secrit Key";
std::map<std::string, std::string> params;
std::string response;
params["grant_type"] = "client_credentials";
params["client_id"] = apikey;
params["client_secret"] = secritkey;
// append url with parameters
for (auto it = params.begin(); it != params.end(); ++it) {
url += (it == params.begin() ? "?" : "&") + it->first + "=" + it->second;
}
CURL *curl = curl_easy_init();
struct curl_slist * slist = NULL;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); // set callback function
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); // set var to receive return info from callback function
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, true);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl, CURLOPT_VERBOSE, false);
int status_code = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_slist_free_all(slist);
Json::Value obj;
if (status_code != CURLcode::CURLE_OK) {
obj["curl_error_code"] = status_code;
return obj.toStyledString();
}
// parse json string
JSONCPP_STRING error;
Json::CharReaderBuilder builder;
const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
std::string access_token = obj["access_token"].asString();
return access_token;
}
// write messages to file
int write_string_to_file_append(const std::string & file_string, const std::string str)
{
std::ofstream OsWrite(file_string, std::ofstream::app);
OsWrite << str;
OsWrite << std::endl;
OsWrite.close();
return 0;
}
int main(int argc, char *argv[])
{
std::string tokenKey;
tokenKey = getTokenKey();
std::cout << "Token Key: " << tokenKey << "\n";
std::string filename = "tokenKey.db";
write_string_to_file_append(filename, tokenKey);
system("pause");
exit(EXIT_SUCCESS);
}
代碼分析
std::string url = "https://aip.baidubce.com/oauth/2.0/token";
std::string apikey = "這裏更改爲對應應用的 API Key";
std::string secritkey = "這裏更改爲對應應用的 Secrit Key";
std::map<std::string, std::string> params;
std::string response;
params["grant_type"] = "client_credentials";
params["client_id"] = apikey;
params["client_secret"] = secritkey;
// append url with parameters
for (auto it = params.begin(); it != params.end(); ++it) {
url += (it == params.begin() ? "?" : "&") + it->first + "=" + it->second;
}
上面這段代碼主要用於獲取最終的請求 URL。因爲這裏使用的是 get 方法來獲取 access token,所以需要將所有參數添加到 URL 中。params
用於存儲請求參數,response
表示請求結果。for
循環則是將各個參數和 URL 使用 ?
和 &
連接起來。最終 URL 的一個示例如下:
https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=Va5yQRHlA4Fq5eR3LT0vuXV4&client_secret=0rDSjzQ20XUj5itV6WRtznPQSzr5pVw2&
CURL *curl = curl_easy_init();
struct curl_slist * slist = NULL;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); // set callback function
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); // set var to receive return info from callback function
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, true);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl, CURLOPT_VERBOSE, false);
int status_code = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_slist_free_all(slist);
上面這段代碼主要是對 URL 請求進行設置,並請求,結果通過回調函數返回給 response
。關於這裏面的部分函數的使用方法可以參考:The Easy interface
curl_easy_init
函數必須是第一個要調用的函數,並且它返回一個 CURL
類型的簡易句柄,必須將該 CURL
簡易句柄用作 easy 接口中其他函數的輸入。
curl_easy_setopt
用來告訴libcurl如何表現。通過設置適當的選項,應用程序可以更改libcurl的行爲。這裏設置的幾個參數解釋如下:
options | 說明 |
---|---|
CURLOPT_URL | URL to work on |
CURLOPT_HTTPHEADER | Custom HTTP headers |
CURLOPT_WRITEFUNCTION | Callback for writing data |
CURLOPT_WRITEDATA | Data pointer to pass to the write callback |
CURLOPT_NOSIGNAL | Do not install signal handlers |
CURLOPT_SSL_VERIFYPEER | Verify the SSL certificate |
CURLOPT_SSL_VERIFYHOST | Verify the host name in the SSL certificate |
CURLOPT_VERBOSE | Display verbose information |
更多 Curl
的 options
參考 curl_easy_setopt。
// callback function for curl
size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *userdata)
{
std::string *str = dynamic_cast<std::string *>((std::string *)userdata);
str->append((char *)ptr, size * nmemb);
return size * nmemb;
}
上面這段代碼是回調函數,一旦收到需要保存的數據,libcurl 就會立即調用此回調函數。對於大多數傳輸,此回調將被調用多次,每次調用都會傳遞另一塊數據。ptr
指向傳遞的數據,該數據的大小爲 nmemb
;大小始終爲 1。關於該函數的使用說明可以參考 CURLOPT_WRITEFUNCTION explained 和 getinmemory.c。
Json::Value obj;
if (status_code != CURLcode::CURLE_OK) {
obj["curl_error_code"] = status_code;
return obj.toStyledString();
}
// parse json string
JSONCPP_STRING error;
Json::CharReaderBuilder builder;
const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
std::string access_token = obj["access_token"].asString();
上面這段代碼主要將字符串轉換爲 Json
格式,然後獲取 access_token
值。
另外我們在調用接口獲取到 access token 後,可以將其存儲到文件中,因爲每個 access token 的有效期爲 30 天,所以可以重複使用直到過期。以下代碼爲將字符串寫入文件和從文件讀入。
int writeFile(const std::string & fileString, const std::string &str) {
std::ofstream out(fileString, std::ios::binary);
if (out.is_open()) {
out << str;
out.close();
}
return 0;
}
int readFile(const std::string & fileString, std::string &str) {
std::ifstream in(fileString);
if (!in.is_open()) {
str = "";
return -1;
}
char buffer[256];
while (!in.eof()) {
in.getline(buffer, sizeof(buffer));
}
str = buffer;
return 0;
}