レスポンダー
このセクションでは、レスポンダーの使い方について説明します。
はじめに
レスポンダーの役割は、レスポンスデータの構造を統一し、HTTPステータスコードやエラーコード、エラーメッセージなどのデータを一元的かつ効率的に処理することです。
APIオプション
前回の記事で述べたように、レスポンダーはHTTPステータスコード、エラーコード、エラーメッセージの処理を担当し、その処理方針は設定項目によって決まります。これらの設定項目は開発者に公開されており、より柔軟に利用ニーズに対応できるようになっています。
これらの設定内容はAPIオプションと呼ばれ、開発者は以下のように設定できます。
import (
"github.com/keepchen/go-sail/v3/constants"
"github.com/keepchen/go-sail/v3/http/api"
"github.com/keepchen/go-sail/v3/sail/config"
)
const ErrNone = sailConstants.CodeType(200)
var (
conf = &config.Config{}
option = &api.Option{
Timezone: constants.DefaultTimeZone,
ErrNoneCode: ErrNone,
ErrNoneCodeMsg: "SUCCESS",
ForceHttpCode200: true,
}
)
func main() {
sail.
WakeupHttp("go-sail", conf).
SetupApiOption(option).
Hook(registerRoutes, nil, nil).
Launch()
}
現在、APIオプションでサポートされている設定項目は以下の通りです:
| 項目 | 説明 | デフォルト値 |
|---|---|---|
| Timezone | 時刻情報のタイムゾーン | "Aisa/Shanghai" |
| ErrNoneCode | エラーなし時のエラーコード | 0 |
| ErrNoneCodeMsg | エラーなし時のエラーメッセージ | "SUCCESS" |
| ErrRequestParamsInvalidCode | リクエストパラメータエラー時のエラーコード | 100000 |
| ErrRequestParamsInvalidCodeMsg | リクエストパラメータエラー時のエラーメッセージ | "Bad request parameters" |
| ErrAuthorizationTokenInvalidCode | 認証情報無効時のエラーコード | 100001 |
| ErrAuthorizationTokenInvalidCodeMsg | 認証情報無効時のエラーメッセージ | "Authorization token invalid" |
| ErrInternalServerErrorCode | サーバー内部エラー時のエラーコード | 999999 |
| ErrInternalServerErrorCodeMsg | サーバー内部エラー時のエラーメッセージ | "Internal server error" |
| ForceHttpCode200 | HTTPステータスコード200を強制的に返すか | false |
| DetectAcceptLanguage | クライアントの言語を検出するか | false |
| LanguageCode | 言語コード | "en" |
| FuncBeforeWrite | レスポンス書き込み前の処理関数 | nil |
| EmptyDataStruct | 空のdataフィールドのデータ表現 | nil |
Timezone
Timezoneオプションは、レスポンダーが時刻を処理する際のタイムゾーンを決定します。現在は時刻情報としてタイムスタンプのみが使用されていますが、この設定項目は今後のアップデートで大きな効果を持つようになります。
ErrNone および ErrNoneCodeMsg
No Errorは、エラーが発生していない(すなわち処理が成功した)場合のコード値とコードメッセージを表します。デフォルトでは、コード値は0、コードメッセージはSUCCESSです。
レスポンスでこの値と等しいエラーコードが指定され、かつ ForceHttpCode200 オプションが true でない場合、HTTPステータスコードは200以外に設定されます。
ErrRequestParamsInvalidCode および ErrRequestParamsInvalidCodeMsg
リクエストパラメータが無効な場合のエラーコードとエラーメッセージです。ForceHttpCode200 が true でない場合、HTTPステータスコードは400に設定されます。
ErrAuthorizationTokenInvalidCode および ErrAuthorizationTokenInvalidCodeMsg
認証情報が無効な場合のエラーコードとエラーメッセージです。ForceHttpCode200 が true でない場合、HTTPステータスコードは401に設定されます。
ErrInternalServerErrorCode および ErrInternalServerErrorCodeMsg
サーバー内部エラーが発生した場合のエラーコードとエラーメッセージです。ForceHttpCode200 が true でない場合、HTTPステータスコードは500に設定されます。
ForceHttpCode200
この値を true に設定すると、HTTPステータスコードはエラーコードに関係なく常に200になります。
このオプションは、厳密なHTTPステータスコードのレスポンスが求められる場合や、エラーコードのみで判定する設計方針の場合に有用です。
異なるインターフェース設計思想を表します。
DetectAcceptLanguage
レスポンダーがクライアントの言語を検出し、指定されたクライアント言語の言語コードテーブルから言語を抽出するかどうかを指定します。
このオプションは、ミドルウェア DetectUserAgentLanguage の協力が必要です。ソースコードは http/middleware/detectuseragentlanguage.go にあります。
LanguageCode
LanguageCodeオプションは、レスポンダーがエラーメッセージを返す際の言語を指定します。DetectAcceptLanguage が機能しなかった場合のフォールバックとして使われます。
FuncBeforeWrite
レスポンス書き込み前の処理関数を設定することで、開発者はレスポンス内容をフックして処理できます。なお、ここで渡されるレスポンス内容は実際のレスポンスのコピーであり、実際のレスポンダーのレスポンス内容を変更することはできませんが、ログ出力などは可能です。
EmptyDataStruct
通常、data フィールドが空の場合は null としてシリアライズされますが、クライアントによってはこの挙動を期待しない場合があります。その場合、このオプションで空配列([])、空オブジェクト({})、数値、真偽値など、特定の空データ構造を指定できます。
エラーコード
実際の業務開発では、多くのエラーコードやエラーメッセージが定義されます。レスポンダーがこれらのエラーコードやエラーメッセージを正しく認識するためには、事前に登録しておく必要があります。
登録方法
以下のように登録するのが一般的で簡単な方法です。
type ErrorCode int
func (v ErrorCode) Int() int {
return int(v)
}
const (
ErrUserNotExist ErrorCode = 1000
ErrUserAlreadyExist ErrorCode = 1001
ErrUsernameAndPasswordNotMatch ErrorCode = 1002
)
var codeMsgMap = map[ErrorCode]string{
ErrUserNotExist: "User not exist",
ErrUserAlreadyExist: "User already exist",
ErrUsernameAndPasswordNotMatch: "Username and password not match",
}
var once sync.Once
func init() {
once.Do(func() {
time.AfterFunc(time.Second*2, func() {
for code, msg := range codeMsgMap {
sail.Code().Register("en", code.Int(), msg)
}
})
})
}
使用方法
この後、以下のように使用できます:
sail.Response(c).Bundle(ErrUserNotExist.Int(), nil).Send()
同様に、他の言語でもこの方法でエラーコードやエラーメッセージを登録できます。
ラッパー
レスポンダーは現在、さまざまなシナリオに対応する3種類のラッパーを提供しています。
以下のデータ構造定義を前提とします:
import "github.com/keepchen/go-sail/http/pojo/dto"
type UserInfo struct {
dto.Base
Data struct {
Nickname string `json:"nickname" validate:"required" format:"string"`
Age number `json:"nickname" validate:"required" format:"number"`
} `json:"data" validate:"required" format:"object"`
}
func (v UserInfo) GetData() interface{} {
return v.Data
}
type SimpleUser struct {
Nickname string `json:"nickname" validate:"required" format:"string"`
Age number `json:"nickname" validate:"required" format:"number"`
}
Builder
Builderラッパーのエラーコードのパラメータ型はGo-Sailのconstants.ICodeTypeである必要があり、レスポンスデータの型はdto.IResponseである必要があります。
var userInfo UserInfo
sail.Response(c).Builder(constants.XX, resp).Send()
Wrap
Wrapラッパーのエラーコードのパラメータ型はGo-Sailのconstants.ICodeTypeであり、レスポンスデータの型はinterfaceである必要があります。
var userInfo SimpleUser
sail.Response(c).Wrap(constants.XX, resp).Send()
Bundle
Bundleラッパーのエラーコードのパラメータ型はintである必要があり、レスポンスデータの型はinterfaceである必要があります。使いやすさの観点では、Bundleが最も簡単です。
var userInfo SimpleUser
sail.Response(c).Wrap(200, resp).Send()
これら3種類のラッパーは、それぞれ異なるレスポンスデータ構造やエラーコードの呼び出し方法を表しています。
UserInfoデータ構造は完全なデータ定義であり、SimpleUserは主要なデータ定義です。Swaggerドキュメントを生成する際、開発者の選択によって挙動が異なります。
レスポンスの送信
現在、レスポンスを送信するためのシンタックスシュガーがいくつか用意されています。
Send
var userInfo SimpleUser
sail.Response(c).Wrap(200, resp).Send()
Data
この関数は成功時のデータを返します。
var userInfo SimpleUser
sail.Response(c).Data(resp)
Success
この関数は空の成功データを返します。Data(nil)と同等です。
sail.Response(c).Success()
SendWithCode
この関数はHTTPステータスコードを強制的に返すことができ、特定のケースで非常に便利です。
var userInfo SimpleUser
sail.Response(c).Wrap([someErrorCode], resp).SendWithCode(500)
Failure
この関数は、Go-Sailに組み込まれているエラーコード constants.ErrRequestParamsInvalid の場合にエラーメッセージを返し、HTTPステータスコードはAPIオプションによって決定されます。
sail.Response(c).Failure()
Failure200
この関数はエラーが発生した場合にエラーメッセージを返します。エラーコードは呼び出し元が指定します。HTTPステータスコードは200です。
sail.Response(c).Failure200([code], nil)
Failure400
この関数はエラーが発生した場合にエラーメッセージを返します。エラーコードは呼び出し元が指定します。HTTPステータスコードは400です。
sail.Response(c).Failure400([code], nil)
Failure500
この関数はエラーが発生した場合にエラーメッセージを返します。エラーコードは呼び出し元が指定します。HTTPステータスコードは500です。
sail.Response(c).Failure500([code], nil)