Open3
gRPCでエラーの詳細を返す方法
gRPCでエラーの詳細を返却して、ユーザーにエラー内容を表示したい場合の方法を考えてみる。
例えばCSVの形式不正をユーザーに表示したい場合。
gRPCのerror detailsの機能を使うのがよさそう。
gRPC公式のerror detailsの定義があるが、エラーコードをprotoで定義できないので自分で同じようなものを定義したほうがよさそう
syntax = "proto3";
package greet.v1;
option go_package = "connect-go-example/gen/greet/v1;greetv1";
// エラーコードの定義
enum ErrorDetailCode {
UNKNOWN = 0;
INVALID_CSV = 1;
}
// error detailsの定義
message ErrorDetail {
ErrorDetailCode code = 1;
string message = 2;
string display_message = 3;
}
message GreetRequest {
string name = 1;
}
message GreetResponse {
string greeting = 1;
}
service GreetService {
rpc Greet(GreetRequest) returns (GreetResponse) {}
}
package main
import (
greetv1 "connect-go-example/gen/greet/v1"
"connect-go-example/gen/greet/v1/greetv1connect"
"context"
"errors"
"github.com/bufbuild/connect-go"
"net/http"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
type GreetServer struct{}
func (s *GreetServer) Greet(
_ context.Context,
_ *connect.Request[greetv1.GreetRequest],
) (*connect.Response[greetv1.GreetResponse], error) {
return nil, newCustomErrorDetails()
}
func main() {
greeter := &GreetServer{}
mux := http.NewServeMux()
path, handler := greetv1connect.NewGreetServiceHandler(greeter)
mux.Handle(path, handler)
http.ListenAndServe(
"localhost:8080",
h2c.NewHandler(mux, &http2.Server{}),
)
}
func newCustomErrorDetails() error {
err := connect.NewError(
connect.CodeInvalidArgument,
errors.New("some error"),
)
addCSVErrorDetail(err, "invalid csv header", "CSVのヘッダーが不正です。ヘッダーはxxx,yyyにしてください")
addCSVErrorDetail(err, "duplicate rows at 2 and 3", "2番目と3番目の列が重複しています")
return err
}
func addCSVErrorDetail(err *connect.Error, msg, displayMsg string) {
if errDetail, detailErr := connect.NewErrorDetail(&greetv1.ErrorDetail{
Code: greetv1.ErrorDetailCode_INVALID_CSV,
Message: msg,
DisplayMessage: displayMsg,
}); detailErr == nil {
err.AddDetail(errDetail)
}
}
curl \
--header "Content-Type: application/json" \
--data '{"name": "Jane"}' \
http://localhost:8080/greet.v1.GreetService/Greet
{
"code": "invalid_argument",
"message": "some error",
"details": [
{
"type": "greet.v1.ErrorDetail",
"value": "CAESEmludmFsaWQgY3N2IGhlYWRlchpPQ1NW44Gu44OY44OD44OA44O844GM5LiN5q2j44Gn44GZ44CC44OY44OD44OA44O844GveHh4LHl5eeOBq+OBl+OBpuOBj+OBoOOBleOBhA",
"debug": {
"@type": "type.googleapis.com/greet.v1.ErrorDetail",
"code": "INVALID_CSV",
"message": "invalid csv header",
"displayMessage": "CSVのヘッダーが不正ですyyyにしてください"
}
},
{
"type": "greet.v1.ErrorDetail",
"value": "CAESGWR1cGxpY2F0ZSByb3dzIGF0IDIgYW5kIDMaLzLnlarnm67jgagz55Wq55uu44Gu5YiX44GM6YeN6KSH44GX44Gm44GE44G+44GZ",
"debug": {
"@type": "type.googlea/greet.v1.ErrorDetail",
"code": "INVALID_CSV",
"message": "duplicate rows at 2 and 3",
"displayMessage": "2番目と3番目の列が重複しています"
}
}
]
}