mirror of
https://github.com/go-kratos/kratos.git
synced 2025-03-07 15:10:52 +02:00
parent
7755bd36dc
commit
81f96ee74d
errors
examples
transport/http
@ -25,14 +25,9 @@ func (e *Error) Error() string {
|
||||
return fmt.Sprintf("error: code = %d reason = %s message = %s metadata = %v", e.Code, e.Reason, e.Message, e.Metadata)
|
||||
}
|
||||
|
||||
// StatusCode return an HTTP error code.
|
||||
func (e *Error) StatusCode() int {
|
||||
return int(e.Code)
|
||||
}
|
||||
|
||||
// GRPCStatus returns the Status represented by se.
|
||||
func (e *Error) GRPCStatus() *status.Status {
|
||||
s, _ := status.New(httputil.GRPCCodeFromStatus(e.StatusCode()), e.Message).
|
||||
s, _ := status.New(httputil.GRPCCodeFromStatus(int(e.Code)), e.Message).
|
||||
WithDetails(&errdetails.ErrorInfo{
|
||||
Reason: e.Reason,
|
||||
Metadata: e.Metadata,
|
||||
|
@ -1,7 +1,8 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package errors;
|
||||
import "errors.proto";
|
||||
|
||||
import "errors/errors.proto";
|
||||
|
||||
// 多语言特定包名,用于源代码引用
|
||||
option go_package = "github.com/go-kratos/kratos/examples/blog/api/v1;v1";
|
||||
|
@ -1,18 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package errors;
|
||||
|
||||
option go_package = "github.com/go-kratos/kratos/v2/errors;v1";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.github.kratos.errors";
|
||||
option objc_class_prefix = "KratosErrors";
|
||||
|
||||
import "google/protobuf/descriptor.proto";
|
||||
|
||||
extend google.protobuf.EnumOptions {
|
||||
int32 default_code = 1108;
|
||||
}
|
||||
|
||||
extend google.protobuf.EnumValueOptions {
|
||||
int32 code = 1109;
|
||||
}
|
@ -2,13 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/go-kratos/kratos/examples/errors/api"
|
||||
"github.com/go-kratos/kratos/v2/errors"
|
||||
"log"
|
||||
|
||||
"github.com/go-kratos/kratos/examples/errors/api"
|
||||
pb "github.com/go-kratos/kratos/examples/helloworld/helloworld"
|
||||
transgrpc "github.com/go-kratos/kratos/v2/transport/grpc"
|
||||
transhttp "github.com/go-kratos/kratos/v2/transport/http"
|
||||
"github.com/go-kratos/kratos/v2/errors"
|
||||
"github.com/go-kratos/kratos/v2/transport/grpc"
|
||||
"github.com/go-kratos/kratos/v2/transport/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -17,9 +17,9 @@ func main() {
|
||||
}
|
||||
|
||||
func callHTTP() {
|
||||
conn, err := transhttp.NewClient(
|
||||
conn, err := http.NewClient(
|
||||
context.Background(),
|
||||
transhttp.WithEndpoint("127.0.0.1:8000"),
|
||||
http.WithEndpoint("127.0.0.1:8000"),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -39,9 +39,9 @@ func callHTTP() {
|
||||
}
|
||||
|
||||
func callGRPC() {
|
||||
conn, err := transgrpc.DialInsecure(
|
||||
conn, err := grpc.DialInsecure(
|
||||
context.Background(),
|
||||
transgrpc.WithEndpoint("127.0.0.1:9000"),
|
||||
grpc.WithEndpoint("127.0.0.1:9000"),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -3,12 +3,12 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-kratos/kratos/v2/errors"
|
||||
"log"
|
||||
|
||||
"github.com/go-kratos/kratos/examples/errors/api"
|
||||
"github.com/go-kratos/kratos/examples/helloworld/helloworld"
|
||||
"github.com/go-kratos/kratos/v2"
|
||||
"github.com/go-kratos/kratos/v2/errors"
|
||||
"github.com/go-kratos/kratos/v2/transport/grpc"
|
||||
"github.com/go-kratos/kratos/v2/transport/http"
|
||||
)
|
||||
|
65
examples/http/errors/main.go
Normal file
65
examples/http/errors/main.go
Normal file
@ -0,0 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
stdhttp "net/http"
|
||||
|
||||
"github.com/go-kratos/kratos/v2"
|
||||
"github.com/go-kratos/kratos/v2/transport/http"
|
||||
)
|
||||
|
||||
// HTTPError is an HTTP error.
|
||||
type HTTPError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e *HTTPError) Error() string {
|
||||
return fmt.Sprintf("HTTPError code: %d message: %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
// FromError try to convert an error to *HTTPError.
|
||||
func FromError(err error) *HTTPError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if se := new(HTTPError); errors.As(err, &se) {
|
||||
return se
|
||||
}
|
||||
return &HTTPError{Code: 500}
|
||||
}
|
||||
|
||||
func errorEncoder(w stdhttp.ResponseWriter, r *stdhttp.Request, err error) {
|
||||
se := FromError(err)
|
||||
codec, _ := http.CodecForRequest(r, "Accept")
|
||||
body, err := codec.Marshal(se)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/"+codec.Name())
|
||||
w.WriteHeader(se.Code)
|
||||
w.Write(body)
|
||||
}
|
||||
|
||||
func main() {
|
||||
httpSrv := http.NewServer(
|
||||
http.Address(":8000"),
|
||||
http.ErrorEncoder(errorEncoder),
|
||||
)
|
||||
router := httpSrv.Route("/")
|
||||
router.GET("home", func(ctx http.Context) error {
|
||||
return &HTTPError{Code: 400, Message: "request error"}
|
||||
})
|
||||
app := kratos.New(
|
||||
kratos.Name("mux"),
|
||||
kratos.Server(
|
||||
httpSrv,
|
||||
),
|
||||
)
|
||||
if err := app.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ import (
|
||||
"github.com/go-kratos/kratos/v2/encoding"
|
||||
"github.com/go-kratos/kratos/v2/errors"
|
||||
"github.com/go-kratos/kratos/v2/internal/httputil"
|
||||
"github.com/go-kratos/kratos/v2/transport/http/binding"
|
||||
)
|
||||
|
||||
// SupportPackageIsVersion1 These constants should not be referenced from any other code.
|
||||
@ -24,17 +23,15 @@ type EncodeErrorFunc func(http.ResponseWriter, *http.Request, error)
|
||||
|
||||
// DefaultRequestDecoder decodes the request body to object.
|
||||
func DefaultRequestDecoder(r *http.Request, v interface{}) error {
|
||||
if codec, ok := CodecForRequest(r, "Content-Type"); ok {
|
||||
data, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return errors.BadRequest("CODEC", err.Error())
|
||||
}
|
||||
if err := codec.Unmarshal(data, v); err != nil {
|
||||
return errors.BadRequest("CODEC", err.Error())
|
||||
}
|
||||
return nil
|
||||
codec, ok := CodecForRequest(r, "Content-Type")
|
||||
if !ok {
|
||||
return errors.BadRequest("CODEC", r.Header.Get("Content-Type"))
|
||||
}
|
||||
if err := binding.BindForm(r, v); err != nil {
|
||||
data, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return errors.BadRequest("CODEC", err.Error())
|
||||
}
|
||||
if err = codec.Unmarshal(data, v); err != nil {
|
||||
return errors.BadRequest("CODEC", err.Error())
|
||||
}
|
||||
return nil
|
||||
@ -48,17 +45,13 @@ func DefaultResponseEncoder(w http.ResponseWriter, r *http.Request, v interface{
|
||||
return err
|
||||
}
|
||||
w.Header().Set("Content-Type", httputil.ContentType(codec.Name()))
|
||||
if sc, ok := v.(interface {
|
||||
StatusCode() int
|
||||
}); ok {
|
||||
w.WriteHeader(sc.StatusCode())
|
||||
}
|
||||
_, _ = w.Write(data)
|
||||
w.Write(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultErrorEncoder encodes the error to the HTTP response.
|
||||
func DefaultErrorEncoder(w http.ResponseWriter, r *http.Request, se error) {
|
||||
func DefaultErrorEncoder(w http.ResponseWriter, r *http.Request, err error) {
|
||||
se := errors.FromError(err)
|
||||
codec, _ := CodecForRequest(r, "Accept")
|
||||
body, err := codec.Marshal(se)
|
||||
if err != nil {
|
||||
@ -66,13 +59,7 @@ func DefaultErrorEncoder(w http.ResponseWriter, r *http.Request, se error) {
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", httputil.ContentType(codec.Name()))
|
||||
if sc, ok := se.(interface {
|
||||
StatusCode() int
|
||||
}); ok {
|
||||
w.WriteHeader(sc.StatusCode())
|
||||
} else {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
w.WriteHeader(int(se.Code))
|
||||
w.Write(body)
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,12 @@ package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/go-kratos/kratos/v2/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
nethttp "net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kratos/kratos/v2/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDefaultRequestDecoder(t *testing.T) {
|
||||
@ -46,40 +47,34 @@ func (w *mockResponseWriter) WriteHeader(statusCode int) {
|
||||
}
|
||||
|
||||
type dataWithStatusCode struct {
|
||||
statusCode int
|
||||
A string `json:"a"`
|
||||
B int64 `json:"b"`
|
||||
}
|
||||
|
||||
func (d *dataWithStatusCode) StatusCode() int {
|
||||
return d.statusCode
|
||||
A string `json:"a"`
|
||||
B int64 `json:"b"`
|
||||
}
|
||||
|
||||
func TestDefaultResponseEncoder(t *testing.T) {
|
||||
w := &mockResponseWriter{header: make(nethttp.Header)}
|
||||
w := &mockResponseWriter{StatusCode: 200, header: make(nethttp.Header)}
|
||||
req1 := &nethttp.Request{
|
||||
Header: make(nethttp.Header),
|
||||
}
|
||||
req1.Header.Set("Content-Type", "application/json")
|
||||
|
||||
v1 := &dataWithStatusCode{statusCode: 201, A: "1", B: 2}
|
||||
v1 := &dataWithStatusCode{A: "1", B: 2}
|
||||
err := DefaultResponseEncoder(w, req1, v1)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "application/json", w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, 201, w.StatusCode)
|
||||
assert.Equal(t, 200, w.StatusCode)
|
||||
assert.NotNil(t, w.Data)
|
||||
}
|
||||
|
||||
func TestDefaultResponseEncoderWithError(t *testing.T) {
|
||||
w := &mockResponseWriter{header: make(nethttp.Header)}
|
||||
req1 := &nethttp.Request{
|
||||
req := &nethttp.Request{
|
||||
Header: make(nethttp.Header),
|
||||
}
|
||||
req1.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
v1 := &errors.Error{Code: 511}
|
||||
err := DefaultResponseEncoder(w, req1, v1)
|
||||
assert.Nil(t, err)
|
||||
se := &errors.Error{Code: 511}
|
||||
DefaultErrorEncoder(w, req, se)
|
||||
assert.Equal(t, "application/json", w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, 511, w.StatusCode)
|
||||
assert.NotNil(t, w.Data)
|
||||
|
Loading…
x
Reference in New Issue
Block a user