0
0

More than 1 year has passed since last update.

GolangでNCMBにリクエストを送る

Posted at

NCMBでは各種言語向けにSDKを提供しています。公式ではない、コミュニティSDKを入れると10以上あるかと思います。

今回はGo言語でNCMBへのリクエストを送信してみました。問題になるのは署名生成プロセスなので、そこを関数化したことで、今後のSDK化も考えられそうです。

必要なライブラリ

今回利用したライブラリは以下の通りです。

import (
	"fmt"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"net/url"
	"sort"
	"time"
	"encoding/json"
	"github.com/go-resty/resty/v2"
	"github.com/tidwall/gjson"
)

定数の定義

利用した、基本となる定数です。

// 定数の定義
const (
	ApplicationKey     = "YOUR_APPLICATION_KEY"
	SignatureMethod    = "HmacSHA256"
	SignatureVersion   = "2"
	FQDN               = "mbaas.api.nifcloud.com"
	ClientKey          = "YOUR_CLIENT_KEY"
)

main関数の流れ

main関数は以下のようになっています。今回はobjectIdやdefinePathは空ですが、汎用的な署名生成時には使える予定です。

func main() {
	t := time.Now()
	where := map[string]string{
		"testKey": "testValue",
	}
	bytes, _ := json.Marshal(where)
	query := map[string]string{
		"where": string(bytes),
	}
	objectId := ""
	definePath := ""
	path := getPath("TestClass", objectId, definePath)
	signature, _ := createSignature("GET", "TestClass", t, query, objectId, definePath)

	// URLを作成
	partofUrl := &url.URL{
		Scheme: "https",
		Host: FQDN,
		Path: path,
		RawQuery: mapToQuery(query),
	}

	// Restyクライアントの生成
	client := resty.New()
	// リクエストの作成と送信
	resp, err := client.R().
      SetHeader("Content-Type", "application/json").
			SetHeader("X-NCMB-Application-Key", ApplicationKey).
			SetHeader("X-NCMB-Signature", signature).
			SetHeader("X-NCMB-Timestamp", t.Format("2006-01-02T15:04:05.999Z0700")).
      Get(partofUrl.String())
	if err != nil {
		fmt.Println(err)
	}
	// レスポンスの解析
	value := gjson.Get(resp.String(), "results")
	fmt.Println(value)
}

署名生成関数

createSignature の内容は以下のようになります。処理内容については REST API リファレンス : シグネチャの生成方法 | ニフクラ mobile backend を参照してください。

// 署名を作成する関数
func createSignature(method, className string, time time.Time, queries map[string]string, objectId string, definePath string) (string, error) {
	// パスの取得
	var path = getPath(className, objectId, definePath)
	// baseInfoの定義
	baseInfoMap := map[string]string{
		"X-NCMB-Application-Key": ApplicationKey,
		"SignatureMethod":        SignatureMethod,
		"SignatureVersion":       SignatureVersion,
		"X-NCMB-Timestamp":       time.Format("2006-01-02T15:04:05.999Z0700"),
	}

	// クエリが存在する場合は、それをbaseInfoMapに追加
	if queries != nil {
		for k, v := range queries {
			baseInfoMap[k] = url.QueryEscape(v)
		}
	}

	// 自然順序でソート
	var keys []string
	for k := range baseInfoMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	// baseInfoの作成
	var baseInfo string
	for _, k := range keys {
		if baseInfo != "" {
			baseInfo += "&"
		}
		baseInfo += k + "=" + baseInfoMap[k]
	}

	// 署名文字列の作成
	signatureString := method + "\n" + FQDN + "\n" + path + "\n" + baseInfo
	// HMACエンコーディング
	h := hmac.New(sha256.New, []byte(ClientKey))
	h.Write([]byte(signatureString))

	// Base64エンコーディング
	return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
}

その他

その他、用意した関数です。

// パスを取得する関数
func getPath(className string, objectId string, definePath string) string {
	if definePath != "" {
		return definePath
	} else if objectId != "" {
		return fmt.Sprintf("/2013-09-01/classes/%s/%s", className, objectId)
	} else {
		return fmt.Sprintf("/2013-09-01/classes/%s", className)
	}
}

// map型のクエリをstring型に変換する関数
func mapToQuery(queries map[string]string) string {
	var query string
	for k, v := range queries {
		if query != "" {
			query += "&"
		}
		query += k + "=" + url.QueryEscape(v)
	}
	return query
}

コード全体

この流れで、NCMBのデータストアを検索する処理ができます。データ投稿などは未対応ですが、GETをPOSTに変えればいけるでしょう。

package main

import (
	"fmt"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"net/url"
	"sort"
	"time"
	"encoding/json"
	"github.com/go-resty/resty/v2"
	"github.com/tidwall/gjson"
)

// 定数の定義
const (
	ApplicationKey     = "YOUR_APPLICATION_KEY"
	SignatureMethod    = "HmacSHA256"
	SignatureVersion   = "2"
	FQDN               = "mbaas.api.nifcloud.com"
	ClientKey          = "YOUR_CLIENT_KEY"
)

// パスを取得する関数
func getPath(className string, objectId string, definePath string) string {
	if definePath != "" {
		return definePath
	} else if objectId != "" {
		return fmt.Sprintf("/2013-09-01/classes/%s/%s", className, objectId)
	} else {
		return fmt.Sprintf("/2013-09-01/classes/%s", className)
	}
}

// map型のクエリをstring型に変換する関数
func mapToQuery(queries map[string]string) string {
	var query string
	for k, v := range queries {
		if query != "" {
			query += "&"
		}
		query += k + "=" + url.QueryEscape(v)
	}
	return query
}

// 署名を作成する関数
func createSignature(method, className string, time time.Time, queries map[string]string, objectId string, definePath string) (string, error) {
	// パスの取得
	var path = getPath(className, objectId, definePath)
	// baseInfoの定義
	baseInfoMap := map[string]string{
		"X-NCMB-Application-Key": ApplicationKey,
		"SignatureMethod":        SignatureMethod,
		"SignatureVersion":       SignatureVersion,
		"X-NCMB-Timestamp":       time.Format("2006-01-02T15:04:05.999Z0700"),
	}

	// クエリが存在する場合は、それをbaseInfoMapに追加
	if queries != nil {
		for k, v := range queries {
			baseInfoMap[k] = url.QueryEscape(v)
		}
	}

	// 自然順序でソート
	var keys []string
	for k := range baseInfoMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	// baseInfoの作成
	var baseInfo string
	for _, k := range keys {
		if baseInfo != "" {
			baseInfo += "&"
		}
		baseInfo += k + "=" + baseInfoMap[k]
	}

	// 署名文字列の作成
	signatureString := method + "\n" + FQDN + "\n" + path + "\n" + baseInfo
	// HMACエンコーディング
	h := hmac.New(sha256.New, []byte(ClientKey))
	h.Write([]byte(signatureString))

	// Base64エンコーディング
	return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
}

func main() {
	t := time.Now()
	where := map[string]string{
		"testKey": "testValue",
	}
	bytes, _ := json.Marshal(where)
	query := map[string]string{
		"where": string(bytes),
	}
	objectId := ""
	definePath := ""
	path := getPath("TestClass", objectId, definePath)
	signature, _ := createSignature("GET", "TestClass", t, query, objectId, definePath)

	// URLの一部を作成
	partofUrl := &url.URL{
		Scheme: "https",
		Host: FQDN,
		Path: path,
		RawQuery: mapToQuery(query),
	}

	// Restyクライアントの生成
	client := resty.New()
	// リクエストの作成と送信
	resp, err := client.R().
      SetHeader("Content-Type", "application/json").
			SetHeader("X-NCMB-Application-Key", ApplicationKey).
			SetHeader("X-NCMB-Signature", signature).
			SetHeader("X-NCMB-Timestamp", t.Format("2006-01-02T15:04:05.999Z0700")).
      Get(partofUrl.String())
	if err != nil {
		fmt.Println(err)
	}
	// レスポンスの解析
	value := gjson.Get(resp.String(), "results")
	fmt.Println(value)
}

まとめ

Go言語はWebサービスであったり、CLIなどでも活用されています。NCMBと連携すれば、データを保存・取得できる場所として活用できるでしょう。

関数レベルではありますが、NCMBとGo言語を組み合わせる際の参考にしてください。

mBaaSでサーバー開発不要! | ニフクラ mobile backend

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0