以 RESTful 操作 DB

HTTPS Request 內容

發送 HTTPS Request 通常包含幾個部分:EndpointRequest HeaderRequest Body

1. 請求位置(Endpoint)

2. 請求 Header

再來為了讓 Cosmos DB 辨識存取資源的人之身份與相關資訊,我們必須加上 Header 欄位,首先我們看到 Request Header 的文件:https://docs.microsoft.com/en-us/rest/api/cosmos-db/common-cosmosdb-rest-request-headers

文件內寫出了必要的請求 Header 包含:

{
  Authorization: "產生相關 authorization token",
  Content-Type:  "請求格式",
  x-ms-date:     "UTC 格式的當前時間",
  x-ms-version:  "API 版本號碼",
}

接著我們要來從第一個 Header 欄位開始填寫。

Authorization 欄位

首先我們要產生 Header 內的 Authorization 欄位,必須為如下格式:

type={tokenType}&ver={tokenVersion}&sig={hashSignature}

tokenType 為 master 或是 resource tokenVersion 通常為 1.0

hashSignature 產生步驟: 1. 把 masterKey 轉為 base64 格式 2. 然後用 HMAC-SHA256做雜湊,其中 HMAC 的 key 為剛才第一步驟產生的 base64 值 3. HAMC-SHA256 的 body 內容為 verb, resourceType, resourceId, date, 串接的值,且每個欄位後要空行

看完後可能一頭霧水,不過沒關係,下面我們用程式來實做看看。

Authorization Header 程式範例:

function genAuthHeader(
  verb,
  resourceType,
  resourceId,
  date,
  master_Key
) {
  const key = new Buffer(master_Key, "base64");

  const text =
    (verb || "").toLowerCase() +
    "\n" +
    (resourceType || "").toLowerCase() +
    "\n" +
    (resourceId || "") +
    "\n" +
    date.toLowerCase() +
    "\n" +
    "" +
    "\n";
  const body = new Buffer(text, "utf8");
  const signature = crypto
    .createHmac("sha256", key)
    .update(body)
    .digest("base64");

  const tokenType = "master";
  const TokenVersion = "1.0";

  return encodeURIComponent(
    "type=" + tokenType + "&ver=" + TokenVersion + "&sig=" + signature
  );
}

如此我們就產生了第一個 Authorization 欄位,不過還有剩下三個欄位,我們可以用以下方式產生他們:

{
  Content-Type:  'application/json',
  x-ms-date:     new Date(Date.now()).toUTCString(),
  x-ms-version:  '2018-06-18',  // 照著填此值即可
}

如此我們就完成 Request Header 了,因為這個範例是 GET 請求,不會有 Request Body,所以我們有了 EndpointRequest Header,就可以來發送請求,我們馬上用以下範例試試看。

以下範例將會讀取一筆我們已經存放入資料庫的資料內容。

完整程式範例

const https = require("https");
const crypto = require("crypto");

const db_id = "TestDB";
const coll_id = "Fruits";
const resourceType = "docs";
const resourceLink = `dbs/${db_id}/colls/${coll_id}/docs/fd327d79-fb20-f5ab-fc81-6e28482670b3`;

const masterKey = "填上 Master Key";
const UTC_Date = new Date(Date.now()).toUTCString();
const method = "GET";

const options = {
  host: "yicheng.documents.azure.com",
  port: 443,
  path: `/` + resourceLink,
  method,
  headers: {
    authorization: genAuthHeader(
      method,
      resourceType,
      resourceLink,
      UTC_Date,
      masterKey
    ), 
    "x-ms-version": '2018-06-18',
    "x-ms-date": UTC_Date,
    'Accept': 'application/json',
    'Content-Type': 'application/json',
  }
};

function sendRequest() {
  const req = https.request(options, res => {
    console.log(res.statusCode);
    res.on("data", function(data) {
      console.log(data.toString());
    });
  });

  req.on("error", e => {
    console.error(e);
  });

  req.end();
}

sendRequest();

function genAuthHeader(
  verb,
  resourceType,
  resourceId,
  date,
  master_Key
) {
  const key = new Buffer(master_Key, "base64");

  const text =
    (verb || "").toLowerCase() +
    "\n" +
    (resourceType || "").toLowerCase() +
    "\n" +
    (resourceId || "") +
    "\n" +
    date.toLowerCase() +
    "\n" +
    "" +
    "\n";
    const body = new Buffer(text, "utf8");
  const signature = crypto
    .createHmac("sha256", key)
    .update(body)
    .digest("base64");

  const tokenType = "master";
  const TokenVersion = "1.0";
  return encodeURIComponent(
    "type=" + tokenType + "&ver=" + TokenVersion + "&sig=" + signature
  );
}

各位是否都成功接收到了回傳的資料了呢?接著我們來看看如果是使用 SDK 該如何做。

對應的 SDK 寫法:

const cosmos = require('@azure/cosmos');
const CosmosClient = cosmos.CosmosClient;

const endpoint = "https://yicheng.documents.azure.com:443/";
const masterKey = "填上金鑰";
const client = new CosmosClient({ endpoint, auth: { masterKey } });
const databaseDefinition = { id: 'TestDB' };
const collectionDefinition = { id: 'Fruits' };

async function createFruit() {
  const { body } = await client.database(databaseDefinition.id)
  .container(collectionDefinition.id)
  .item('fd327d79-fb20-f5ab-fc81-6e28482670b3').read();
};

createFruit().catch(err => {
  console.error(err);
});

後記:

參考資料:https://docs.microsoft.com/en-us/rest/api/cosmos-db/restful-interactions-with-cosmosdb-resources

Last updated