This is an automated email from the ASF dual-hosted git repository.

tianxiaoliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-service-center.git


The following commit(s) were added to refs/heads/master by this push:
     new 26d6283  SCB-2176 Add exception and route Handler (#1001)
26d6283 is described below

commit 26d6283f02d92317ded1b7e2b66fa85fcda0d398
Author: little-cui <[email protected]>
AuthorDate: Sun May 23 07:29:09 2021 +0800

    SCB-2176 Add exception and route Handler (#1001)
    
    * SCB-2176 Add response Handler
    
    * SCB-2176 Upgrade CARI
    
    * SCB-2176 Add exception handler
    
    * SCB-2176 Remove X-Response-Status Header
    
    * SCB-2176 Remove WriteJSON function
---
 datasource/etcd/client/embedded/embededetcd.go     |  4 +-
 datasource/etcd/client/remote/etcd.go              |  4 +-
 etc/conf/app.yaml                                  |  4 +-
 go.mod                                             |  2 +-
 go.sum                                             |  2 +
 pkg/chain/chain_test.go                            | 19 ++---
 pkg/chain/invocation.go                            | 36 ++++++---
 pkg/dump/dump.go                                   |  2 +-
 pkg/dump/types.go                                  |  6 +-
 pkg/errors/error.go                                |  2 +-
 pkg/rest/common.go                                 |  4 +-
 pkg/rest/router.go                                 | 28 +------
 server/bootstrap/bootstrap.go                      | 12 +--
 server/broker/brokerpb/broker.go                   | 12 +--
 server/broker/controller.go                        | 26 ++-----
 server/handler/accesslog/handler.go                |  5 +-
 server/handler/auth/auth.go                        | 29 +++++---
 server/handler/cache/cache.go                      | 69 -----------------
 server/handler/context/context.go                  | 56 ++++++++++----
 server/handler/context/v3.go                       |  4 +-
 server/handler/context/v4.go                       |  4 +-
 server/handler/exception/exception.go              | 85 +++++++++++++++++++++
 server/handler/exception/writer.go                 | 77 +++++++++++++++++++
 server/handler/exception/writer_test.go            | 59 +++++++++++++++
 .../{response/response.go => route/route.go}       | 20 ++++-
 server/handler/tracing/tracing.go                  | 11 +--
 server/interceptor/interceptors.go                 |  2 +-
 server/metrics/http.go                             | 17 ++---
 server/resource/v1/gov_resource.go                 | 86 +++++++++-------------
 server/resource/v4/auth_resource.go                | 61 ++++++---------
 server/resource/v4/role_resource.go                | 46 +++++-------
 server/rest/admin/controller_v4.go                 | 15 +---
 server/rest/controller/rest_util.go                | 45 ++++-------
 server/rest/controller/v3/main_controller.go       |  2 +-
 server/rest/controller/v4/dependency_controller.go |  8 +-
 server/rest/controller/v4/instance_controller.go   | 18 +----
 server/rest/controller/v4/main_controller.go       |  6 +-
 .../rest/controller/v4/microservice_controller.go  | 20 ++---
 server/rest/controller/v4/query_rule_controller.go |  8 +-
 server/rest/controller/v4/schema_controller.go     | 14 ++--
 server/rest/controller/v4/tag_controller.go        |  4 +-
 server/rest/govern/controller_v4.go                | 19 +----
 server/service/gov/kie/kie_distributor.go          |  4 +-
 43 files changed, 507 insertions(+), 450 deletions(-)

diff --git a/datasource/etcd/client/embedded/embededetcd.go 
b/datasource/etcd/client/embedded/embededetcd.go
index 7948b02..f3fd195 100644
--- a/datasource/etcd/client/embedded/embededetcd.go
+++ b/datasource/etcd/client/embedded/embededetcd.go
@@ -393,7 +393,7 @@ func (s *EtcdEmbed) LeaseRenew(ctx context.Context, leaseID 
int64) (int64, error
                if err.Error() == rpctypes.ErrLeaseNotFound.Error() {
                        return 0, err
                }
-               return 0, errorsEx.RaiseError(err)
+               return 0, errorsEx.Internal(err)
        }
        return ttl, nil
 }
@@ -408,7 +408,7 @@ func (s *EtcdEmbed) LeaseRevoke(ctx context.Context, 
leaseID int64) error {
                if err.Error() == rpctypes.ErrLeaseNotFound.Error() {
                        return err
                }
-               return errorsEx.RaiseError(err)
+               return errorsEx.Internal(err)
        }
        return nil
 }
diff --git a/datasource/etcd/client/remote/etcd.go 
b/datasource/etcd/client/remote/etcd.go
index 311c232..151bb86 100644
--- a/datasource/etcd/client/remote/etcd.go
+++ b/datasource/etcd/client/remote/etcd.go
@@ -656,7 +656,7 @@ func (c *Client) LeaseRenew(ctx context.Context, leaseID 
int64) (int64, error) {
                if err.Error() == rpctypes.ErrLeaseNotFound.Error() {
                        return 0, err
                }
-               return 0, errorsEx.RaiseError(err)
+               return 0, errorsEx.Internal(err)
        }
        log.NilOrWarnf(start, "registry client renew lease %d", leaseID)
        return etcdResp.TTL, nil
@@ -680,7 +680,7 @@ func (c *Client) LeaseRevoke(ctx context.Context, leaseID 
int64) error {
                if err.Error() == rpctypes.ErrLeaseNotFound.Error() {
                        return err
                }
-               return errorsEx.RaiseError(err)
+               return errorsEx.Internal(err)
        }
        log.NilOrWarnf(start, "registry client revoke lease %d", leaseID)
        return nil
diff --git a/etc/conf/app.yaml b/etc/conf/app.yaml
index 1c05844..310cfc4 100644
--- a/etc/conf/app.yaml
+++ b/etc/conf/app.yaml
@@ -155,8 +155,8 @@ discovery:
 
 rbac:
   enable: false
-  privateKeyFile:
-  publicKeyFile:
+  privateKeyFile: ./private.key
+  publicKeyFile: ./public.key
 
 metrics:
   enable: true
diff --git a/go.mod b/go.mod
index 24733a4..d3f52d5 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
        github.com/dgrijalva/jwt-go v3.2.0+incompatible
        github.com/elithrar/simple-scrypt v1.3.0
        github.com/ghodss/yaml v1.0.0
-       github.com/go-chassis/cari v0.3.1-0.20210519092219-69f9f0fc3452
+       github.com/go-chassis/cari v0.4.1-0.20210522033018-d228d49d7d61
        github.com/go-chassis/foundation v0.3.1-0.20210513015331-b54416b66bcd
        github.com/go-chassis/go-archaius v1.5.1
        github.com/go-chassis/go-chassis/v2 v2.1.2-0.20210310004133-c9bc42149a18
diff --git a/go.sum b/go.sum
index 12d91a7..042672b 100644
--- a/go.sum
+++ b/go.sum
@@ -201,6 +201,8 @@ github.com/go-chassis/cari 
v0.3.1-0.20210508100214-a13e083de04e h1:YLXfK7pSRsYK7
 github.com/go-chassis/cari v0.3.1-0.20210508100214-a13e083de04e/go.mod 
h1:Ie2lW11Y5ZFClY9z7bhAwK6BoNxqGSf3fYGs4mPFs74=
 github.com/go-chassis/cari v0.3.1-0.20210519092219-69f9f0fc3452 
h1:G2Qlpg17t0oULhz0Eu3NQgkxKDcNbpGpmgtMR6RZvwk=
 github.com/go-chassis/cari v0.3.1-0.20210519092219-69f9f0fc3452/go.mod 
h1:av/19fqwEP4eOC8unL/z67AAbFDwXUCko6SKa4Avrd8=
+github.com/go-chassis/cari v0.4.1-0.20210522033018-d228d49d7d61 
h1:gVgeg1lmX1mpra0lyKHiSlc9DImjIDT0vQiSaFHrzMs=
+github.com/go-chassis/cari v0.4.1-0.20210522033018-d228d49d7d61/go.mod 
h1:av/19fqwEP4eOC8unL/z67AAbFDwXUCko6SKa4Avrd8=
 github.com/go-chassis/foundation v0.2.2-0.20201210043510-9f6d3de40234/go.mod 
h1:2PjwqpVwYEVaAldl5A58a08viH8p27pNeYaiE3ZxOBA=
 github.com/go-chassis/foundation v0.2.2/go.mod 
h1:2PjwqpVwYEVaAldl5A58a08viH8p27pNeYaiE3ZxOBA=
 github.com/go-chassis/foundation v0.3.0/go.mod 
h1:2PjwqpVwYEVaAldl5A58a08viH8p27pNeYaiE3ZxOBA=
diff --git a/pkg/chain/chain_test.go b/pkg/chain/chain_test.go
index 01faacb..0681f42 100644
--- a/pkg/chain/chain_test.go
+++ b/pkg/chain/chain_test.go
@@ -88,19 +88,19 @@ type mockHandler struct {
 }
 
 func (h *mockHandler) Handle(i *Invocation) {
-       x := i.Context().Value(util.CtxKey("x")).(int)
-       switch x {
+       caseN := i.Context().Value(util.CtxKey("x")).(int)
+       switch caseN {
        case 1:
-               i.Success(x)
+               i.Success(caseN)
        case 2:
-               i.Fail(errors.New("error"), x)
+               i.Fail(errors.New("error"), caseN)
        case 3:
                i.Next(WithFunc(func(r Result) {
-                       i.WithContext("x", x*x)
+                       i.WithContext("x", caseN*caseN)
                }))
        case 4:
                i.Next(WithAsyncFunc(func(r Result) {
-                       i.WithContext("x", x*x)
+                       i.WithContext("x", caseN*caseN)
                        ch, _ := i.Context().Value(util.CtxKey("ch")).(chan 
struct{})
                        ch <- struct{}{}
                }))
@@ -115,7 +115,7 @@ func (h *mockHandler) Handle(i *Invocation) {
                        panic(errors.New("error"))
                }))
        default:
-               i.WithContext("x", x-1)
+               i.WithContext("x", caseN-1)
                i.Next()
        }
 }
@@ -163,11 +163,12 @@ func TestChain_Next(t *testing.T) {
        i = NewInvocation(context.Background(), ch)
        i.WithContext("x", x)
        i.Invoke(func(r Result) {
-               if !r.OK || i.Context().Value(util.CtxKey("x")).(int) != x {
+               if !r.OK || i.Context().Value(util.CtxKey("x")).(int) != x*x {
                        t.Fatalf("TestChain_Next")
                }
+               i.WithContext("x", x)
        })
-       if i.Context().Value(util.CtxKey("x")).(int) != x*x {
+       if i.Context().Value(util.CtxKey("x")).(int) != x {
                t.Fatalf("TestChain_Next")
        }
 
diff --git a/pkg/chain/invocation.go b/pkg/chain/invocation.go
index 9396e5d..86d1300 100644
--- a/pkg/chain/invocation.go
+++ b/pkg/chain/invocation.go
@@ -32,9 +32,12 @@ type InvocationOp struct {
        Async bool
 }
 
+// WithFunc append a func to the begin of invocation callback list
 func WithFunc(f func(r Result)) InvocationOption {
        return func(op InvocationOp) InvocationOp { op.Func = f; return op }
 }
+
+// WithAsyncFunc called concurrently after all WithFunc finish
 func WithAsyncFunc(f func(r Result)) InvocationOption {
        return func(op InvocationOp) InvocationOp { op.Func = f; op.Async = 
true; return op }
 }
@@ -61,10 +64,10 @@ func (i *Invocation) WithContext(key util.CtxKey, val 
interface{}) *Invocation {
 
 // Next is the method to go next step in handler chain
 // WithFunc and WithAsyncFunc options can add customize callbacks in chain
-// and the callbacks seq like below
-// i.Success/Fail() -> CB1 ---> CB3 ----------> END           goroutine 0
-//                          \-> CB2(async) \                  goroutine 1
-//                                          \-> CB4(async)    goroutine 1 or 2
+// and the callbacks seq like below:
+// when 
i.Next(WithFunc(CB1)).Next(WithAsyncFunc(CB2)).Next(WithFunc(CB3)).Invoke(CB0)
+// then i.Success/Fail() -> CB3 -> CB1 -> CB0(invoke)             goroutine 0
+//                                              \-> CB2(async)    goroutine 1
 func (i *Invocation) Next(opts ...InvocationOption) {
        var op InvocationOp
        for _, opt := range opts {
@@ -87,17 +90,25 @@ func (i *Invocation) setCallback(f CallbackFunc, async 
bool) {
        }
        cb := i.Func
        i.Func = func(r Result) {
-               cb(r)
-               callback(f, async, r)
+               callback(cb, f, async, r)
        }
 }
 
-func callback(f CallbackFunc, async bool, r Result) {
-       c := Callback{Func: f, Async: async}
+func callback(prev, next CallbackFunc, async bool, r Result) {
+       // we make sure the all sync funcs called before the async funcs
+       if async {
+               prev(r)
+       }
+
+       c := Callback{Func: next, Async: async}
        c.Invoke(r)
+
+       if !async {
+               prev(r)
+       }
 }
 
-func (i *Invocation) Invoke(f CallbackFunc) {
+func (i *Invocation) Invoke(last CallbackFunc) {
        defer func() {
                itf := recover()
                if itf == nil {
@@ -105,9 +116,12 @@ func (i *Invocation) Invoke(f CallbackFunc) {
                }
                log.Panic(itf)
 
-               i.Fail(errorsEx.RaiseError(itf))
+               // this recover only catch the exceptions raised in sync 
invocations.
+               // The async invocations will be catch by gopool pkg then it 
never
+               // change the callback results.
+               i.Fail(errorsEx.Internal(itf))
        }()
-       i.Func = f
+       i.Func = last
        i.chain.Next(i)
 }
 
diff --git a/pkg/dump/dump.go b/pkg/dump/dump.go
index 20e462e..5acf129 100644
--- a/pkg/dump/dump.go
+++ b/pkg/dump/dump.go
@@ -217,7 +217,7 @@ type Request struct {
 }
 
 type Response struct {
-       Response  *discovery.Response    `json:"response,omitempty"`
+       Response  *discovery.Response    `json:"-"`
        Info      *version.Set           `json:"info,omitempty"`
        AppConfig map[string]interface{} `json:"appConf,omitempty"`
        Cache     *Cache                 `json:"cache,omitempty"`
diff --git a/pkg/dump/types.go b/pkg/dump/types.go
index 9d2620e..2af4ba9 100644
--- a/pkg/dump/types.go
+++ b/pkg/dump/types.go
@@ -25,7 +25,7 @@ type AlarmListRequest struct {
 }
 
 type AlarmListResponse struct {
-       Response *discovery.Response `json:"response,omitempty"`
+       Response *discovery.Response `json:"-"`
        Alarms   []*model.AlarmEvent `json:"alarms,omitempty"`
 }
 
@@ -33,7 +33,7 @@ type ClustersRequest struct {
 }
 
 type ClustersResponse struct {
-       Response *discovery.Response `json:"response,omitempty"`
+       Response *discovery.Response `json:"-"`
        Clusters cluster.Clusters    `json:"clusters,omitempty"`
 }
 
@@ -41,5 +41,5 @@ type ClearAlarmRequest struct {
 }
 
 type ClearAlarmResponse struct {
-       Response *discovery.Response `json:"response,omitempty"`
+       Response *discovery.Response `json:"-"`
 }
diff --git a/pkg/errors/error.go b/pkg/errors/error.go
index 818b8bb..ca52d5f 100644
--- a/pkg/errors/error.go
+++ b/pkg/errors/error.go
@@ -27,7 +27,7 @@ func (e InternalError) Error() string {
        return string(e)
 }
 
-func RaiseError(itf interface{}) InternalError {
+func Internal(itf interface{}) InternalError {
        if itf == nil {
                return InternalError("panic: unknown")
        }
diff --git a/pkg/rest/common.go b/pkg/rest/common.go
index 1b96d6e..f99651f 100644
--- a/pkg/rest/common.go
+++ b/pkg/rest/common.go
@@ -34,12 +34,12 @@ const (
        CtxMatchPattern   util.CtxKey = "_server_match_pattern"
        CtxMatchFunc      util.CtxKey = "_server_match_func"
        CtxStartTimestamp util.CtxKey = "x-start-timestamp"
+       CtxResponseStatus util.CtxKey = "_server_response_status"
        CtxResponseObject util.CtxKey = "_server_response_object"
+       CtxRouteHandler   util.CtxKey = "_server_route_handler"
 
        ServerChainName = "_server_chain"
 
-       HeaderResponseStatus = "X-Response-Status"
-
        HeaderAllow           = "Allow"
        HeaderHost            = "Host"
        HeaderServer          = "Server"
diff --git a/pkg/rest/router.go b/pkg/rest/router.go
index 890b145..932fadb 100644
--- a/pkg/rest/router.go
+++ b/pkg/rest/router.go
@@ -24,11 +24,12 @@ import (
        "strings"
 
        "github.com/apache/servicecomb-service-center/pkg/chain"
-       errorsEx "github.com/apache/servicecomb-service-center/pkg/errors"
        "github.com/apache/servicecomb-service-center/pkg/log"
        "github.com/apache/servicecomb-service-center/pkg/util"
 )
 
+var doNothingFunc = func(_ chain.Result) {}
+
 // Router is a HTTP request multiplexer
 // Attention:
 //   1. not thread-safe, must be initialized completely before serve http 
request
@@ -116,29 +117,8 @@ func (router *Router) serve(ph *urlPatternHandler, w 
http.ResponseWriter, r *htt
                WithContext(CtxRequest, r).
                WithContext(CtxMatchPattern, ph.Path).
                WithContext(CtxMatchFunc, ph.Name).
-               Invoke(
-                       func(ret chain.Result) {
-                               defer func() {
-                                       err := ret.Err
-                                       itf := recover()
-                                       if itf != nil {
-                                               log.Panic(itf)
-
-                                               err = errorsEx.RaiseError(itf)
-                                       }
-                                       if _, ok := 
err.(errorsEx.InternalError); ok {
-                                               http.Error(w, err.Error(), 
http.StatusInternalServerError)
-                                               return
-                                       }
-                                       if err != nil {
-                                               http.Error(w, err.Error(), 
http.StatusBadRequest)
-                                               return
-                                       }
-                               }()
-                               if ret.OK {
-                                       ph.ServeHTTP(w, r)
-                               }
-                       })
+               WithContext(CtxRouteHandler, ph).
+               Invoke(doNothingFunc)
 }
 
 // NewRouter news an Router
diff --git a/server/bootstrap/bootstrap.go b/server/bootstrap/bootstrap.go
index 270d707..b34fea6 100644
--- a/server/bootstrap/bootstrap.go
+++ b/server/bootstrap/bootstrap.go
@@ -27,9 +27,8 @@ import (
        _ 
"github.com/apache/servicecomb-service-center/server/rest/controller/v3"
 
        //rest v4 api
-       _ 
"github.com/apache/servicecomb-service-center/server/rest/controller/v4"
-
        _ "github.com/apache/servicecomb-service-center/server/resource"
+       _ 
"github.com/apache/servicecomb-service-center/server/rest/controller/v4"
 
        //cipher
        _ 
"github.com/apache/servicecomb-service-center/server/plugin/security/cipher/buildin"
@@ -42,7 +41,6 @@ import (
 
        //uuid
        _ 
"github.com/apache/servicecomb-service-center/server/plugin/uuid/buildin"
-
        _ 
"github.com/apache/servicecomb-service-center/server/plugin/uuid/context"
 
        //tracing
@@ -70,10 +68,11 @@ import (
        "github.com/apache/servicecomb-service-center/server/broker"
        "github.com/apache/servicecomb-service-center/server/handler/accesslog"
        "github.com/apache/servicecomb-service-center/server/handler/auth"
-       "github.com/apache/servicecomb-service-center/server/handler/cache"
        "github.com/apache/servicecomb-service-center/server/handler/context"
+       "github.com/apache/servicecomb-service-center/server/handler/exception"
        "github.com/apache/servicecomb-service-center/server/handler/maxbody"
        "github.com/apache/servicecomb-service-center/server/handler/metrics"
+       "github.com/apache/servicecomb-service-center/server/handler/route"
        "github.com/apache/servicecomb-service-center/server/handler/tracing"
        "github.com/apache/servicecomb-service-center/server/interceptor"
        "github.com/apache/servicecomb-service-center/server/interceptor/access"
@@ -88,13 +87,14 @@ func init() {
        interceptor.RegisterInterceptFunc(cors.Intercept)
 
        // handle requests after routing.
+       exception.RegisterHandlers()
        accesslog.RegisterHandlers()
+       auth.RegisterHandlers()
        maxbody.RegisterHandlers()
        metrics.RegisterHandlers()
        tracing.RegisterHandlers()
-       auth.RegisterHandlers()
        context.RegisterHandlers()
-       cache.RegisterHandlers()
+       route.RegisterHandlers()
 
        // init broker
        broker.Init()
diff --git a/server/broker/brokerpb/broker.go b/server/broker/brokerpb/broker.go
index 3079ef4..f86a5de 100644
--- a/server/broker/brokerpb/broker.go
+++ b/server/broker/brokerpb/broker.go
@@ -221,7 +221,7 @@ func (m *PublishPactRequest) GetPact() []byte {
 }
 
 type PublishPactResponse struct {
-       Response *discovery.Response `protobuf:"bytes,1,opt,name=response" 
json:"response,omitempty"`
+       Response *discovery.Response `protobuf:"bytes,1,opt,name=response" 
json:"-"`
 }
 
 func (m *PublishPactResponse) Reset() { *m = PublishPactResponse{} }
@@ -280,7 +280,7 @@ func (m *Links) GetPacts() []*ConsumerInfo {
 }
 
 type GetAllProviderPactsResponse struct {
-       Response *discovery.Response `protobuf:"bytes,1,opt,name=response" 
json:"response,omitempty"`
+       Response *discovery.Response `protobuf:"bytes,1,opt,name=response" 
json:"-"`
        XLinks   *Links              
`protobuf:"bytes,2,opt,name=_links,json=Links" json:"_links,omitempty"`
 }
 
@@ -324,7 +324,7 @@ func (m *GetProviderConsumerVersionPactRequest) 
GetBaseUrl() *BaseBrokerRequest
 }
 
 type GetProviderConsumerVersionPactResponse struct {
-       Response *discovery.Response `protobuf:"bytes,1,opt,name=response" 
json:"response,omitempty"`
+       Response *discovery.Response `protobuf:"bytes,1,opt,name=response" 
json:"-"`
        Pact     []byte              `protobuf:"bytes,2,opt,name=pact,proto3" 
json:"pact,omitempty"`
 }
 
@@ -552,7 +552,7 @@ func (m *PublishVerificationRequest) 
GetProviderApplicationVersion() string {
 }
 
 type PublishVerificationResponse struct {
-       Response     *discovery.Response `protobuf:"bytes,1,opt,name=response" 
json:"response,omitempty"`
+       Response     *discovery.Response `protobuf:"bytes,1,opt,name=response" 
json:"-"`
        Confirmation *VerificationDetail 
`protobuf:"bytes,2,opt,name=confirmation" json:"confirmation,omitempty"`
 }
 
@@ -585,7 +585,7 @@ func (m *RetrieveVerificationRequest) GetConsumerVersion() 
string {
 }
 
 type RetrieveVerificationResponse struct {
-       Response *discovery.Response `protobuf:"bytes,1,opt,name=response" 
json:"response,omitempty"`
+       Response *discovery.Response `protobuf:"bytes,1,opt,name=response" 
json:"-"`
        Result   *VerificationResult `protobuf:"bytes,2,opt,name=result" 
json:"result,omitempty"`
 }
 
@@ -655,7 +655,7 @@ func (m *BrokerAPIInfoEntry) GetTemplated() bool {
 }
 
 type BrokerHomeResponse struct {
-       Response *discovery.Response            
`protobuf:"bytes,1,opt,name=response" json:"response,omitempty"`
+       Response *discovery.Response            
`protobuf:"bytes,1,opt,name=response" json:"-"`
        XLinks   map[string]*BrokerAPIInfoEntry 
`protobuf:"bytes,2,rep,name=_links,json=Links" json:"_links,omitempty" 
protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
        Curies   []*BrokerAPIInfoEntry          
`protobuf:"bytes,3,rep,name=curies" json:"curies,omitempty"`
 }
diff --git a/server/broker/controller.go b/server/broker/controller.go
index 15378fa..7f48226 100644
--- a/server/broker/controller.go
+++ b/server/broker/controller.go
@@ -69,10 +69,7 @@ func (brokerService *Controller) GetHome(w 
http.ResponseWriter, r *http.Request)
                Scheme:      getScheme(r),
        }
        resp, _ := ServiceAPI.GetBrokerHome(r.Context(), request)
-
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (*Controller) PublishPact(w http.ResponseWriter, r *http.Request) {
@@ -97,9 +94,7 @@ func (*Controller) PublishPact(w http.ResponseWriter, r 
*http.Request) {
                controller.WriteError(w, pb.ErrInternal, "can not push pact")
                return
        }
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (*Controller) GetAllProviderPacts(w http.ResponseWriter, r *http.Request) 
{
@@ -123,9 +118,7 @@ func (*Controller) GetAllProviderPacts(w 
http.ResponseWriter, r *http.Request) {
                return
        }
        PactLogger.Infof("Pact info: %s\n", string(linksObj))
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (*Controller) GetPactsOfProvider(w http.ResponseWriter, r *http.Request) {
@@ -141,10 +134,7 @@ func (*Controller) GetPactsOfProvider(w 
http.ResponseWriter, r *http.Request) {
        }
 
        resp, _ := ServiceAPI.GetPactsOfProvider(r.Context(), request)
-       respInternal := resp.Response
-       resp.Response = nil
-       //controller.WriteResponse(w, respInternal, resp.Pact)
-       controller.WriteJSONIfSuccess(w, respInternal, resp.Pact)
+       controller.WriteResponse(w, r, resp.Response, resp.Pact)
 }
 
 func (*Controller) DeletePacts(w http.ResponseWriter, r *http.Request) {
@@ -189,9 +179,7 @@ func (*Controller) PublishVerificationResults(w 
http.ResponseWriter, r *http.Req
                controller.WriteError(w, pb.ErrInternal, "publish failed")
                return
        }
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (*Controller) RetrieveVerificationResults(w http.ResponseWriter, r 
*http.Request) {
@@ -202,9 +190,7 @@ func (*Controller) RetrieveVerificationResults(w 
http.ResponseWriter, r *http.Re
        PactLogger.Infof("Retrieve verification results for: %s, %s\n",
                request.ConsumerId, request.ConsumerVersion)
        resp, _ := ServiceAPI.RetrieveVerificationResults(r.Context(), request)
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func getScheme(r *http.Request) string {
diff --git a/server/handler/accesslog/handler.go 
b/server/handler/accesslog/handler.go
index ba52c9e..a15e84c 100644
--- a/server/handler/accesslog/handler.go
+++ b/server/handler/accesslog/handler.go
@@ -67,16 +67,15 @@ func (h *Handler) Handle(i *chain.Invocation) {
                startTimeStr = start.Format("2006-01-02T15:04:05.000Z07:00")
        }
        r := i.Context().Value(rest.CtxRequest).(*http.Request)
-       w := i.Context().Value(rest.CtxResponse).(http.ResponseWriter)
        i.Next(chain.WithAsyncFunc(func(_ chain.Result) {
                delayByMillisecond := "unknown"
                if ok {
                        delayByMillisecond = fmt.Sprintf("%d", 
time.Since(start)/time.Millisecond)
                }
-               statusCode := w.Header().Get(rest.HeaderResponseStatus)
+               statusCode := i.Context().Value(rest.CtxResponseStatus).(int)
                // format:  remoteIp requestReceiveTime "method requestUri 
proto" statusCode requestBodySize delay(ms)
                // example: 127.0.0.1 2006-01-02T15:04:05.000Z07:00 "GET 
/v4/default/registry/microservices HTTP/1.1" 200 0 0
-               h.logger.Infof("%s %s \"%s %s %s\" %s %d %s",
+               h.logger.Infof("%s %s \"%s %s %s\" %d %d %s",
                        util.GetIPFromContext(i.Context()),
                        startTimeStr,
                        r.Method,
diff --git a/server/handler/auth/auth.go b/server/handler/auth/auth.go
index 5ae5e82..bfbabce 100644
--- a/server/handler/auth/auth.go
+++ b/server/handler/auth/auth.go
@@ -24,7 +24,6 @@ import (
        "github.com/apache/servicecomb-service-center/pkg/log"
        "github.com/apache/servicecomb-service-center/pkg/rest"
        "github.com/apache/servicecomb-service-center/server/plugin/auth"
-       "github.com/apache/servicecomb-service-center/server/rest/controller"
        "github.com/go-chassis/cari/discovery"
 )
 
@@ -33,18 +32,28 @@ type Handler struct {
 
 func (h *Handler) Handle(i *chain.Invocation) {
        r := i.Context().Value(rest.CtxRequest).(*http.Request)
-       err := auth.Identify(r)
-       if err == nil {
-               i.Next()
+
+       if err := auth.Identify(r); err != nil {
+               log.Errorf(err, "authenticate request failed, %s %s", r.Method, 
r.RequestURI)
+               i.Fail(discovery.NewError(discovery.ErrUnauthorized, 
err.Error()))
                return
        }
 
-       log.Errorf(err, "authenticate request failed, %s %s", r.Method, 
r.RequestURI)
-
-       w := i.Context().Value(rest.CtxResponse).(http.ResponseWriter)
-       controller.WriteError(w, discovery.ErrUnauthorized, err.Error())
-
-       i.Fail(nil)
+       i.Next(chain.WithFunc(func(ret chain.Result) {
+               if !ret.OK {
+                       return
+               }
+
+               obj := i.Context().Value(rest.CtxResponseObject)
+               if obj == nil {
+                       return
+               }
+
+               // TODO filter and rewrite here!
+               // data, _ := json.Marshal(obj)
+               // w := 
i.Context().Value(rest.CtxResponse).(http.ResponseWriter)
+               // w.Write(data)
+       }))
 }
 
 func RegisterHandlers() {
diff --git a/server/handler/cache/cache.go b/server/handler/cache/cache.go
deleted file mode 100644
index 881f585..0000000
--- a/server/handler/cache/cache.go
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package cache
-
-import (
-       "net/http"
-
-       "github.com/apache/servicecomb-service-center/pkg/chain"
-       "github.com/apache/servicecomb-service-center/pkg/rest"
-       "github.com/apache/servicecomb-service-center/pkg/util"
-)
-
-const (
-       queryGlobal    = "global"
-       queryNoCache   = "noCache"
-       queryCacheOnly = "cacheOnly"
-)
-
-type Handler struct {
-}
-
-func (l *Handler) Handle(i *chain.Invocation) {
-       defer i.Next()
-
-       r := i.Context().Value(rest.CtxRequest).(*http.Request)
-       query := r.URL.Query()
-
-       global := util.StringTRUE(query.Get(queryGlobal))
-       if global && r.Method == http.MethodGet {
-               i.WithContext(util.CtxGlobal, "1")
-       }
-
-       noCache := util.StringTRUE(query.Get(queryNoCache))
-       if noCache {
-               i.WithContext(util.CtxNocache, "1")
-               return
-       }
-
-       cacheOnly := util.StringTRUE(query.Get(queryCacheOnly))
-       if cacheOnly {
-               i.WithContext(util.CtxCacheOnly, "1")
-               return
-       }
-
-       rev := query.Get("rev")
-       if len(rev) > 0 {
-               i.WithContext(util.CtxRequestRevision, rev)
-               return
-       }
-}
-
-func RegisterHandlers() {
-       chain.RegisterHandler(rest.ServerChainName, &Handler{})
-}
diff --git a/server/handler/context/context.go 
b/server/handler/context/context.go
index d837431..f388ffc 100644
--- a/server/handler/context/context.go
+++ b/server/handler/context/context.go
@@ -21,40 +21,68 @@ import (
        "net/http"
 
        "github.com/apache/servicecomb-service-center/pkg/chain"
-       roa "github.com/apache/servicecomb-service-center/pkg/rest"
+       "github.com/apache/servicecomb-service-center/pkg/rest"
        "github.com/apache/servicecomb-service-center/pkg/util"
 )
 
+const (
+       queryGlobal    = "global"
+       queryNoCache   = "noCache"
+       queryCacheOnly = "cacheOnly"
+)
+
 type Handler struct {
 }
 
 func (c *Handler) Handle(i *chain.Invocation) {
        var (
-               err     error
-               v3      v3Context
-               v4      v4Context
-               r       = i.Context().Value(roa.CtxRequest).(*http.Request)
-               pattern = i.Context().Value(roa.CtxMatchPattern).(string)
+               v3 v3Context
+               v4 v4Context
+               r  = i.Context().Value(rest.CtxRequest).(*http.Request)
        )
 
        switch {
-       case util.IsVersionOrHealthPattern(pattern):
        case v3.IsMatch(r):
-               err = v3.Do(r)
+               v3.Write(r)
        case v4.IsMatch(r):
-               err = v4.Do(r)
+               v4.Write(r)
        }
 
-       if err != nil {
-               i.Fail(err)
+       c.commonQueryToContext(i)
+
+       i.Next()
+}
+
+func (c *Handler) commonQueryToContext(i *chain.Invocation) {
+       r := i.Context().Value(rest.CtxRequest).(*http.Request)
+       query := r.URL.Query()
+
+       i.WithContext(util.CtxRemoteIP, util.GetRealIP(r))
+
+       global := util.StringTRUE(query.Get(queryGlobal))
+       if global && r.Method == http.MethodGet {
+               i.WithContext(util.CtxGlobal, "1")
+       }
+
+       noCache := util.StringTRUE(query.Get(queryNoCache))
+       if noCache {
+               i.WithContext(util.CtxNocache, "1")
                return
        }
 
-       i.WithContext(util.CtxRemoteIP, util.GetRealIP(r))
+       cacheOnly := util.StringTRUE(query.Get(queryCacheOnly))
+       if cacheOnly {
+               i.WithContext(util.CtxCacheOnly, "1")
+               return
+       }
 
-       i.Next()
+       rev := query.Get("rev")
+       if len(rev) > 0 {
+               i.WithContext(util.CtxRequestRevision, rev)
+               return
+       }
 }
 
 func RegisterHandlers() {
-       chain.RegisterHandler(roa.ServerChainName, &Handler{})
+       chain.RegisterHandler(rest.ServerChainName, &Handler{})
 }
diff --git a/server/handler/context/v3.go b/server/handler/context/v3.go
index 453e1e1..14f9bae 100644
--- a/server/handler/context/v3.go
+++ b/server/handler/context/v3.go
@@ -32,7 +32,7 @@ func (v *v3Context) IsMatch(r *http.Request) bool {
        return strings.Index(r.RequestURI, "/registry/v3/") == 0
 }
 
-func (v *v3Context) Do(r *http.Request) error {
+func (v *v3Context) Write(r *http.Request) {
        ctx := r.Context()
 
        domain, project := util.ParseDomain(ctx), util.ParseProject(ctx)
@@ -52,6 +52,4 @@ func (v *v3Context) Do(r *http.Request) error {
        if len(project) == 0 {
                util.SetRequestContext(r, util.CtxProject, core.RegistryProject)
        }
-
-       return nil
 }
diff --git a/server/handler/context/v4.go b/server/handler/context/v4.go
index b4723a9..8ffca5f 100644
--- a/server/handler/context/v4.go
+++ b/server/handler/context/v4.go
@@ -32,7 +32,7 @@ func (v *v4Context) IsMatch(r *http.Request) bool {
        return strings.Index(r.RequestURI, "/v4/") == 0
 }
 
-func (v *v4Context) Do(r *http.Request) error {
+func (v *v4Context) Write(r *http.Request) {
        ctx := r.Context()
 
        domain, project := util.ParseDomain(ctx), util.ParseProject(ctx)
@@ -52,6 +52,4 @@ func (v *v4Context) Do(r *http.Request) error {
                }
                util.SetRequestContext(r, util.CtxProject, project)
        }
-
-       return nil
 }
diff --git a/server/handler/exception/exception.go 
b/server/handler/exception/exception.go
new file mode 100644
index 0000000..b711dd4
--- /dev/null
+++ b/server/handler/exception/exception.go
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package exception
+
+import (
+       "github.com/apache/servicecomb-service-center/pkg/chain"
+       "github.com/apache/servicecomb-service-center/pkg/errors"
+       "github.com/apache/servicecomb-service-center/pkg/log"
+       "github.com/apache/servicecomb-service-center/pkg/rest"
+       "github.com/apache/servicecomb-service-center/pkg/util"
+       "github.com/go-chassis/cari/discovery"
+       "net/http"
+)
+
+// Handler provide a common response writer to handle exceptions
+type Handler struct {
+}
+
+func (l *Handler) Handle(i *chain.Invocation) {
+       w, r := i.Context().Value(rest.CtxResponse).(http.ResponseWriter),
+               i.Context().Value(rest.CtxRequest).(*http.Request)
+
+       asyncWriter := NewWriter(w)
+       util.SetRequestContext(r, rest.CtxResponse, asyncWriter)
+
+       i.Next(chain.WithFunc(func(ret chain.Result) {
+               if !ret.OK {
+                       i.WithContext(rest.CtxResponseStatus, 
l.responseError(w, ret.Err))
+                       return
+               }
+
+               i.WithContext(rest.CtxResponseStatus, asyncWriter.StatusCode)
+               if err := asyncWriter.Flush(); err != nil {
+                       log.Error("response writer flush failed", err)
+               }
+       }))
+}
+
+func (l *Handler) responseError(w http.ResponseWriter, e error) (statusCode 
int) {
+       statusCode = http.StatusBadRequest
+       contentType := rest.ContentTypeText
+       body := []byte("Unknown error")
+       defer func() {
+               w.Header().Set(rest.HeaderContentType, contentType)
+               w.WriteHeader(statusCode)
+               if _, writeErr := w.Write(body); writeErr != nil {
+                       log.Error("write response failed", writeErr)
+               }
+       }()
+
+       if e == nil {
+               log.Warn("callback result is failure but no error")
+               return
+       }
+
+       body = util.StringToBytesWithNoCopy(e.Error())
+       switch err := e.(type) {
+       case errors.InternalError:
+               statusCode = http.StatusInternalServerError
+       case *discovery.Error:
+               statusCode = err.StatusCode()
+               contentType = rest.ContentTypeJSON
+               body = err.Marshal()
+       }
+       return
+}
+
+func RegisterHandlers() {
+       chain.RegisterHandler(rest.ServerChainName, &Handler{})
+}
diff --git a/server/handler/exception/writer.go 
b/server/handler/exception/writer.go
new file mode 100644
index 0000000..05c13a0
--- /dev/null
+++ b/server/handler/exception/writer.go
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package exception
+
+import (
+       "fmt"
+       "net/http"
+
+       "github.com/apache/servicecomb-service-center/pkg/log"
+       "github.com/apache/servicecomb-service-center/pkg/rest"
+       "github.com/apache/servicecomb-service-center/pkg/util"
+)
+
+// Writer is the async response writer, it is not thread safe!
+type Writer struct {
+       StatusCode int
+       Body       []byte
+       w          http.ResponseWriter
+       flushed    bool
+}
+
+func (aw *Writer) Header() http.Header {
+       return aw.w.Header()
+}
+
+func (aw *Writer) Write(body []byte) (int, error) {
+       aw.Body = body
+       return len(body), nil
+}
+
+func (aw *Writer) WriteHeader(statusCode int) {
+       aw.StatusCode = statusCode
+}
+
+func (aw *Writer) Flush() error {
+       if aw.flushed {
+               log.Warn("writer already flushed")
+               return nil
+       }
+       aw.flushed = true
+
+       if aw.StatusCode == 0 {
+               err := fmt.Errorf("unknown status code %d", aw.StatusCode)
+               aw.w.Header().Set(rest.HeaderContentType, rest.ContentTypeText)
+               aw.w.WriteHeader(http.StatusInternalServerError)
+               if _, writeErr := 
aw.w.Write(util.StringToBytesWithNoCopy(err.Error())); writeErr != nil {
+                       log.Error("write response failed", writeErr)
+               }
+               return err
+       }
+       aw.w.WriteHeader(aw.StatusCode)
+       if len(aw.Body) == 0 {
+               return nil
+       }
+
+       _, err := aw.w.Write(aw.Body)
+       return err
+}
+
+func NewWriter(w http.ResponseWriter) *Writer {
+       return &Writer{w: w}
+}
diff --git a/server/handler/exception/writer_test.go 
b/server/handler/exception/writer_test.go
new file mode 100644
index 0000000..3ff49c3
--- /dev/null
+++ b/server/handler/exception/writer_test.go
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package exception_test
+
+import (
+       "bytes"
+       "github.com/apache/servicecomb-service-center/server/handler/exception"
+       "github.com/stretchr/testify/assert"
+       "io/ioutil"
+       "net/http"
+       "net/http/httptest"
+       "testing"
+)
+
+type handler struct {
+       Writer *exception.Writer
+}
+
+func (receiver *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+       aw := exception.NewWriter(w)
+       aw.Write([]byte("Bye"))
+       aw.Header().Set("a", "b")
+       aw.WriteHeader(http.StatusCreated)
+       aw.Flush()
+}
+
+func TestAsyncWriter_Flush(t *testing.T) {
+       h := &handler{}
+       c := &http.Client{}
+       s := httptest.NewServer(h)
+       defer s.Close()
+
+       t.Run("should recv the response when new writer", func(t *testing.T) {
+               request, err := http.NewRequest(http.MethodPost, s.URL, 
bytes.NewReader([]byte("Hi")))
+               assert.NoError(t, err)
+               resp, err := c.Do(request)
+               assert.NoError(t, err)
+               assert.Equal(t, http.StatusCreated, resp.StatusCode)
+               assert.Equal(t, "b", resp.Header.Get("a"))
+               body, err := ioutil.ReadAll(resp.Body)
+               assert.NoError(t, err)
+               assert.Equal(t, "Bye", string(body))
+       })
+}
diff --git a/server/handler/response/response.go b/server/handler/route/route.go
similarity index 64%
rename from server/handler/response/response.go
rename to server/handler/route/route.go
index 3b83aee..72839fb 100644
--- a/server/handler/response/response.go
+++ b/server/handler/route/route.go
@@ -15,13 +15,29 @@
  * limitations under the License.
  */
 
-package response
+package route
 
-import "github.com/apache/servicecomb-service-center/pkg/chain"
+import (
+       "net/http"
 
+       "github.com/apache/servicecomb-service-center/pkg/chain"
+       "github.com/apache/servicecomb-service-center/pkg/rest"
+)
+
+// Handler call the func of matched route
 type Handler struct {
 }
 
 func (l *Handler) Handle(i *chain.Invocation) {
+       w, r := i.Context().Value(rest.CtxResponse).(http.ResponseWriter),
+               i.Context().Value(rest.CtxRequest).(*http.Request)
+       ph := i.Context().Value(rest.CtxRouteHandler).(http.Handler)
+
+       ph.ServeHTTP(w, r)
+
+       i.Next()
+}
 
+func RegisterHandlers() {
+       chain.RegisterHandler(rest.ServerChainName, &Handler{})
 }
diff --git a/server/handler/tracing/tracing.go 
b/server/handler/tracing/tracing.go
index 6bbfb36..149bc3f 100644
--- a/server/handler/tracing/tracing.go
+++ b/server/handler/tracing/tracing.go
@@ -30,19 +30,14 @@ type Handler struct {
 }
 
 func (h *Handler) Handle(i *chain.Invocation) {
-       w, r, op := i.Context().Value(rest.CtxResponse).(http.ResponseWriter),
-               i.Context().Value(rest.CtxRequest).(*http.Request),
+       r, op := i.Context().Value(rest.CtxRequest).(*http.Request),
                i.Context().Value(rest.CtxMatchFunc).(string)
 
        span := tracing.ServerBegin(op, r)
 
        i.Next(chain.WithAsyncFunc(func(ret chain.Result) {
-               statusCode := w.Header().Get(rest.HeaderResponseStatus)
-               code, _ := strconv.ParseInt(statusCode, 10, 64)
-               if code == 0 {
-                       code = 200
-               }
-               tracing.ServerEnd(span, int(code), statusCode)
+               statusCode := i.Context().Value(rest.CtxResponseStatus).(int)
+               tracing.ServerEnd(span, statusCode, strconv.Itoa(statusCode))
        }))
 }
 
diff --git a/server/interceptor/interceptors.go 
b/server/interceptor/interceptors.go
index 4e7dad2..dffcfce 100644
--- a/server/interceptor/interceptors.go
+++ b/server/interceptor/interceptors.go
@@ -59,7 +59,7 @@ func InvokeInterceptors(w http.ResponseWriter, req 
*http.Request) (err error) {
                if itf := recover(); itf != nil {
                        log.Panic(itf)
 
-                       err = errorsEx.RaiseError(itf)
+                       err = errorsEx.Internal(itf)
 
                        http.Error(w, err.Error(), 
http.StatusInternalServerError)
                }
diff --git a/server/metrics/http.go b/server/metrics/http.go
index 687f1cd..db9c71e 100644
--- a/server/metrics/http.go
+++ b/server/metrics/http.go
@@ -77,7 +77,7 @@ func ReportRequestCompleted(w http.ResponseWriter, r 
*http.Request, start time.T
                reqDurations.WithLabelValues(r.Method, instance, route, 
domain).Observe(elapsed)
        }
 
-       success, code := codeOf(w.Header())
+       success, code := codeOf(r.Context().Value(rest.CtxResponseStatus).(int))
 
        incomingRequests.WithLabelValues(r.Method, code, instance, route, 
domain).Inc()
 
@@ -86,15 +86,10 @@ func ReportRequestCompleted(w http.ResponseWriter, r 
*http.Request, start time.T
        }
 }
 
-func codeOf(h http.Header) (bool, string) {
-       statusCode := h.Get(rest.HeaderResponseStatus)
-       if statusCode == "" {
-               return true, "200"
+func codeOf(code int) (bool, string) {
+       sz := strconv.Itoa(code)
+       if code >= http.StatusOK && code <= http.StatusAccepted {
+               return true, sz
        }
-
-       if code, _ := strconv.Atoi(statusCode); code >= http.StatusOK && code 
<= http.StatusAccepted {
-               return true, statusCode
-       }
-
-       return false, statusCode
+       return false, sz
 }
diff --git a/server/resource/v1/gov_resource.go 
b/server/resource/v1/gov_resource.go
index 706e841..ea8fbf8 100644
--- a/server/resource/v1/gov_resource.go
+++ b/server/resource/v1/gov_resource.go
@@ -18,7 +18,6 @@
 package v1
 
 import (
-       "encoding/json"
        "io/ioutil"
        "net/http"
 
@@ -44,10 +43,11 @@ const (
 )
 
 //Create gov config
-func (t *Governance) Create(w http.ResponseWriter, req *http.Request) {
-       kind := req.URL.Query().Get(KindKey)
-       project := req.URL.Query().Get(ProjectKey)
-       body, err := ioutil.ReadAll(req.Body)
+func (t *Governance) Create(w http.ResponseWriter, r *http.Request) {
+       query := r.URL.Query()
+       kind := query.Get(KindKey)
+       project := query.Get(ProjectKey)
+       body, err := ioutil.ReadAll(r.Body)
        if err != nil {
                log.Error("read body err", err)
                controller.WriteError(w, discovery.ErrInternal, err.Error())
@@ -64,24 +64,18 @@ func (t *Governance) Create(w http.ResponseWriter, req 
*http.Request) {
                return
        }
 
-       policy := &model.Policy{GovernancePolicy: &model.GovernancePolicy{ID: 
string(id)}}
-       b, err := json.Marshal(policy)
-       if err != nil {
-               processError(w, err, "marshal policy id response failed")
-               return
-       }
-       controller.WriteJSON(w, b)
+       controller.WriteResponse(w, r, nil, &model.Policy{GovernancePolicy: 
&model.GovernancePolicy{ID: string(id)}})
 }
 
 //Put gov config
-func (t *Governance) Put(w http.ResponseWriter, req *http.Request) {
-       kind := req.URL.Query().Get(KindKey)
-       id := req.URL.Query().Get(IDKey)
-       project := req.URL.Query().Get(ProjectKey)
-       body, err := ioutil.ReadAll(req.Body)
+func (t *Governance) Put(w http.ResponseWriter, r *http.Request) {
+       query := r.URL.Query()
+       kind := query.Get(KindKey)
+       id := query.Get(IDKey)
+       project := query.Get(ProjectKey)
+       body, err := ioutil.ReadAll(r.Body)
        if err != nil {
-               log.Error("read body err", err)
-               controller.WriteError(w, discovery.ErrInternal, err.Error())
+               processError(w, err, "read body err")
                return
        }
        err = gov.Update(kind, id, project, body)
@@ -94,15 +88,16 @@ func (t *Governance) Put(w http.ResponseWriter, req 
*http.Request) {
                processError(w, err, "put gov err")
                return
        }
-       w.WriteHeader(http.StatusOK)
+       controller.WriteResponse(w, r, nil, nil)
 }
 
 //ListOrDisPlay return all gov config
-func (t *Governance) ListOrDisPlay(w http.ResponseWriter, req *http.Request) {
-       kind := req.URL.Query().Get(KindKey)
-       project := req.URL.Query().Get(ProjectKey)
-       app := req.URL.Query().Get(AppKey)
-       environment := req.URL.Query().Get(EnvironmentKey)
+func (t *Governance) ListOrDisPlay(w http.ResponseWriter, r *http.Request) {
+       query := r.URL.Query()
+       kind := query.Get(KindKey)
+       project := query.Get(ProjectKey)
+       app := query.Get(AppKey)
+       environment := query.Get(EnvironmentKey)
        var body []byte
        var err error
        if kind == DisplayKey {
@@ -114,51 +109,38 @@ func (t *Governance) ListOrDisPlay(w http.ResponseWriter, 
req *http.Request) {
                processError(w, err, "list gov err")
                return
        }
-       _, err = w.Write(body)
-       if err != nil {
-               processError(w, err, "")
-               return
-       }
-       w.WriteHeader(http.StatusOK)
-       w.Header().Set(rest.HeaderContentType, rest.ContentTypeJSON)
+       controller.WriteResponse(w, r, nil, body)
 }
 
 //Get gov config
-func (t *Governance) Get(w http.ResponseWriter, req *http.Request) {
-       kind := req.URL.Query().Get(KindKey)
-       id := req.URL.Query().Get(IDKey)
-       project := req.URL.Query().Get(ProjectKey)
+func (t *Governance) Get(w http.ResponseWriter, r *http.Request) {
+       query := r.URL.Query()
+       kind := query.Get(KindKey)
+       id := query.Get(IDKey)
+       project := query.Get(ProjectKey)
        body, err := gov.Get(kind, id, project)
        if err != nil {
-               w.WriteHeader(http.StatusBadRequest)
-               log.Error("get gov err", err)
-               controller.WriteError(w, discovery.ErrInternal, err.Error())
-               return
-       }
-       _, err = w.Write(body)
-       if err != nil {
-               processError(w, err, "")
+               processError(w, err, "get gov err")
                return
        }
-       w.WriteHeader(http.StatusOK)
-       w.Header().Set(rest.HeaderContentType, rest.ContentTypeJSON)
+       controller.WriteResponse(w, r, nil, body)
 }
 
 //Delete delete gov config
-func (t *Governance) Delete(w http.ResponseWriter, req *http.Request) {
-       kind := req.URL.Query().Get(KindKey)
-       id := req.URL.Query().Get(IDKey)
-       project := req.URL.Query().Get(ProjectKey)
+func (t *Governance) Delete(w http.ResponseWriter, r *http.Request) {
+       query := r.URL.Query()
+       kind := query.Get(KindKey)
+       id := query.Get(IDKey)
+       project := query.Get(ProjectKey)
        err := gov.Delete(kind, id, project)
        if err != nil {
                processError(w, err, "delete gov err")
                return
        }
-       w.WriteHeader(http.StatusOK)
+       controller.WriteResponse(w, r, nil, nil)
 }
 
 func processError(w http.ResponseWriter, err error, msg string) {
-       w.WriteHeader(http.StatusBadRequest)
        log.Error(msg, err)
        controller.WriteError(w, discovery.ErrInternal, err.Error())
 }
diff --git a/server/resource/v4/auth_resource.go 
b/server/resource/v4/auth_resource.go
index c1d3b41..c754b10 100644
--- a/server/resource/v4/auth_resource.go
+++ b/server/resource/v4/auth_resource.go
@@ -42,18 +42,18 @@ type AuthResource struct {
 }
 
 //URLPatterns define htp pattern
-func (r *AuthResource) URLPatterns() []rest.Route {
+func (ar *AuthResource) URLPatterns() []rest.Route {
        return []rest.Route{
-               {Method: http.MethodPost, Path: "/v4/token", Func: r.Login},
-               {Method: http.MethodPost, Path: "/v4/accounts", Func: 
r.CreateAccount},
-               {Method: http.MethodGet, Path: "/v4/accounts", Func: 
r.ListAccount},
-               {Method: http.MethodGet, Path: "/v4/accounts/:name", Func: 
r.GetAccount},
-               {Method: http.MethodDelete, Path: "/v4/accounts/:name", Func: 
r.DeleteAccount},
-               {Method: http.MethodPut, Path: "/v4/accounts/:name", Func: 
r.UpdateAccount},
-               {Method: http.MethodPost, Path: "/v4/accounts/:name/password", 
Func: r.ChangePassword},
+               {Method: http.MethodPost, Path: "/v4/token", Func: ar.Login},
+               {Method: http.MethodPost, Path: "/v4/accounts", Func: 
ar.CreateAccount},
+               {Method: http.MethodGet, Path: "/v4/accounts", Func: 
ar.ListAccount},
+               {Method: http.MethodGet, Path: "/v4/accounts/:name", Func: 
ar.GetAccount},
+               {Method: http.MethodDelete, Path: "/v4/accounts/:name", Func: 
ar.DeleteAccount},
+               {Method: http.MethodPut, Path: "/v4/accounts/:name", Func: 
ar.UpdateAccount},
+               {Method: http.MethodPost, Path: "/v4/accounts/:name/password", 
Func: ar.ChangePassword},
        }
 }
-func (r *AuthResource) CreateAccount(w http.ResponseWriter, req *http.Request) 
{
+func (ar *AuthResource) CreateAccount(w http.ResponseWriter, req 
*http.Request) {
        body, err := ioutil.ReadAll(req.Body)
        if err != nil {
                log.Error("read body err", err)
@@ -86,7 +86,7 @@ func (r *AuthResource) CreateAccount(w http.ResponseWriter, 
req *http.Request) {
        }
        controller.WriteSuccess(w, req)
 }
-func (r *AuthResource) DeleteAccount(w http.ResponseWriter, req *http.Request) 
{
+func (ar *AuthResource) DeleteAccount(w http.ResponseWriter, req 
*http.Request) {
        _, err := dao.DeleteAccount(context.TODO(), 
req.URL.Query().Get(":name"))
        if err != nil {
                log.Error(errorsEx.MsgOperateAccountFailed, err)
@@ -95,7 +95,7 @@ func (r *AuthResource) DeleteAccount(w http.ResponseWriter, 
req *http.Request) {
        }
        controller.WriteSuccess(w, req)
 }
-func (r *AuthResource) UpdateAccount(w http.ResponseWriter, req *http.Request) 
{
+func (ar *AuthResource) UpdateAccount(w http.ResponseWriter, req 
*http.Request) {
        body, err := ioutil.ReadAll(req.Body)
        if err != nil {
                log.Error("read body err", err)
@@ -117,7 +117,7 @@ func (r *AuthResource) UpdateAccount(w http.ResponseWriter, 
req *http.Request) {
        }
        controller.WriteSuccess(w, req)
 }
-func (r *AuthResource) ListAccount(w http.ResponseWriter, req *http.Request) {
+func (ar *AuthResource) ListAccount(w http.ResponseWriter, r *http.Request) {
        as, n, err := dao.ListAccount(context.TODO())
        if err != nil {
                log.Error(errorsEx.MsgGetAccountFailed, err)
@@ -128,32 +128,20 @@ func (r *AuthResource) ListAccount(w http.ResponseWriter, 
req *http.Request) {
                Total:    n,
                Accounts: as,
        }
-       b, err := json.Marshal(resp)
-       if err != nil {
-               log.Error(errorsEx.MsgJSON, err)
-               controller.WriteError(w, discovery.ErrInternal, 
errorsEx.MsgJSON)
-               return
-       }
-       controller.WriteJSON(w, b)
+       controller.WriteResponse(w, r, nil, resp)
 }
-func (r *AuthResource) GetAccount(w http.ResponseWriter, req *http.Request) {
-       a, err := dao.GetAccount(context.TODO(), req.URL.Query().Get(":name"))
+func (ar *AuthResource) GetAccount(w http.ResponseWriter, r *http.Request) {
+       a, err := dao.GetAccount(context.TODO(), r.URL.Query().Get(":name"))
        if err != nil {
                log.Error(errorsEx.MsgGetAccountFailed, err)
                controller.WriteError(w, discovery.ErrInternal, 
errorsEx.MsgGetAccountFailed)
                return
        }
        a.Password = ""
-       b, err := json.Marshal(a)
-       if err != nil {
-               log.Error(errorsEx.MsgJSON, err)
-               controller.WriteError(w, discovery.ErrInternal, 
errorsEx.MsgJSON)
-               return
-       }
-       controller.WriteJSON(w, b)
+       controller.WriteResponse(w, r, nil, a)
 }
 
-func (r *AuthResource) ChangePassword(w http.ResponseWriter, req 
*http.Request) {
+func (ar *AuthResource) ChangePassword(w http.ResponseWriter, req 
*http.Request) {
        ip := util.GetRealIP(req)
        if rbacsvc.IsBanned(ip) {
                log.Warn("ip is banned:" + ip)
@@ -202,14 +190,14 @@ func (r *AuthResource) ChangePassword(w 
http.ResponseWriter, req *http.Request)
        }
        controller.WriteSuccess(w, req)
 }
-func (r *AuthResource) Login(w http.ResponseWriter, req *http.Request) {
-       ip := util.GetRealIP(req)
+func (ar *AuthResource) Login(w http.ResponseWriter, r *http.Request) {
+       ip := util.GetRealIP(r)
        if rbacsvc.IsBanned(ip) {
                log.Warn("ip is banned:" + ip)
                controller.WriteError(w, discovery.ErrForbidden, "")
                return
        }
-       body, err := ioutil.ReadAll(req.Body)
+       body, err := ioutil.ReadAll(r.Body)
        if err != nil {
                log.Error("read body err", err)
                controller.WriteError(w, discovery.ErrInternal, err.Error())
@@ -242,12 +230,5 @@ func (r *AuthResource) Login(w http.ResponseWriter, req 
*http.Request) {
                controller.WriteError(w, discovery.ErrInternal, err.Error())
                return
        }
-       to := &rbac.Token{TokenStr: t}
-       b, err := json.Marshal(to)
-       if err != nil {
-               log.Error("json err", err)
-               controller.WriteError(w, discovery.ErrInvalidParams, 
err.Error())
-               return
-       }
-       controller.WriteJSON(w, b)
+       controller.WriteResponse(w, r, nil, &rbac.Token{TokenStr: t})
 }
diff --git a/server/resource/v4/role_resource.go 
b/server/resource/v4/role_resource.go
index fd73c27..b61470a 100644
--- a/server/resource/v4/role_resource.go
+++ b/server/resource/v4/role_resource.go
@@ -40,18 +40,18 @@ type RoleResource struct {
 }
 
 //URLPatterns define http pattern
-func (r *RoleResource) URLPatterns() []rest.Route {
+func (rr *RoleResource) URLPatterns() []rest.Route {
        return []rest.Route{
-               {Method: http.MethodGet, Path: "/v4/roles", Func: 
r.GetRolePermission},
-               {Method: http.MethodPost, Path: "/v4/roles", Func: 
r.CreateRolePermission},
-               {Method: http.MethodPut, Path: "/v4/roles/:roleName", Func: 
r.UpdateRolePermission},
-               {Method: http.MethodGet, Path: "/v4/roles/:roleName", Func: 
r.GetRole},
-               {Method: http.MethodDelete, Path: "/v4/roles/:roleName", Func: 
r.DeleteRole},
+               {Method: http.MethodGet, Path: "/v4/roles", Func: 
rr.GetRolePermission},
+               {Method: http.MethodPost, Path: "/v4/roles", Func: 
rr.CreateRolePermission},
+               {Method: http.MethodPut, Path: "/v4/roles/:roleName", Func: 
rr.UpdateRolePermission},
+               {Method: http.MethodGet, Path: "/v4/roles/:roleName", Func: 
rr.GetRole},
+               {Method: http.MethodDelete, Path: "/v4/roles/:roleName", Func: 
rr.DeleteRole},
        }
 }
 
 //GetRolePermission list all roles and there's permissions
-func (r *RoleResource) GetRolePermission(w http.ResponseWriter, req 
*http.Request) {
+func (rr *RoleResource) GetRolePermission(w http.ResponseWriter, req 
*http.Request) {
        rs, _, err := dao.ListRole(context.TODO())
        if err != nil {
                log.Error(errorsEx.MsgGetRoleFailed, err)
@@ -61,17 +61,11 @@ func (r *RoleResource) GetRolePermission(w 
http.ResponseWriter, req *http.Reques
        resp := &rbac.RoleResponse{
                Roles: rs,
        }
-       b, err := json.Marshal(resp)
-       if err != nil {
-               log.Error(errorsEx.MsgJSON, err)
-               controller.WriteError(w, discovery.ErrInternal, 
errorsEx.MsgJSON)
-               return
-       }
-       controller.WriteJSON(w, b)
+       controller.WriteResponse(w, req, nil, resp)
 }
 
 //roleParse parse the role info from the request body
-func (r *RoleResource) roleParse(body []byte) (*rbac.Role, error) {
+func (rr *RoleResource) roleParse(body []byte) (*rbac.Role, error) {
        role := &rbac.Role{}
        err := json.Unmarshal(body, role)
        if err != nil {
@@ -83,14 +77,14 @@ func (r *RoleResource) roleParse(body []byte) (*rbac.Role, 
error) {
 }
 
 //CreateRolePermission create new role and assign permissions
-func (r *RoleResource) CreateRolePermission(w http.ResponseWriter, req 
*http.Request) {
+func (rr *RoleResource) CreateRolePermission(w http.ResponseWriter, req 
*http.Request) {
        body, err := ioutil.ReadAll(req.Body)
        if err != nil {
                log.Error("read body err", err)
                controller.WriteError(w, discovery.ErrInternal, err.Error())
                return
        }
-       role, err := r.roleParse(body)
+       role, err := rr.roleParse(body)
        if err != nil {
                controller.WriteError(w, discovery.ErrInvalidParams, 
errorsEx.MsgJSON)
                return
@@ -109,14 +103,14 @@ func (r *RoleResource) CreateRolePermission(w 
http.ResponseWriter, req *http.Req
 }
 
 //UpdateRolePermission update role permissions
-func (r *RoleResource) UpdateRolePermission(w http.ResponseWriter, req 
*http.Request) {
+func (rr *RoleResource) UpdateRolePermission(w http.ResponseWriter, req 
*http.Request) {
        body, err := ioutil.ReadAll(req.Body)
        if err != nil {
                log.Error("read body err", err)
                controller.WriteError(w, discovery.ErrInternal, err.Error())
                return
        }
-       role, err := r.roleParse(body)
+       role, err := rr.roleParse(body)
        if err != nil {
                controller.WriteError(w, discovery.ErrInvalidParams, 
errorsEx.MsgJSON)
                return
@@ -132,23 +126,17 @@ func (r *RoleResource) UpdateRolePermission(w 
http.ResponseWriter, req *http.Req
 }
 
 //GetRole get the role info according to role name
-func (r *RoleResource) GetRole(w http.ResponseWriter, req *http.Request) {
-       role, err := dao.GetRole(context.TODO(), 
req.URL.Query().Get(":roleName"))
+func (rr *RoleResource) GetRole(w http.ResponseWriter, r *http.Request) {
+       role, err := dao.GetRole(context.TODO(), r.URL.Query().Get(":roleName"))
        if err != nil {
                log.Error(errorsEx.MsgGetRoleFailed, err)
                controller.WriteError(w, discovery.ErrInternal, 
errorsEx.MsgGetRoleFailed)
        }
-       v, err := json.Marshal(role)
-       if err != nil {
-               log.Error(errorsEx.MsgJSON, err)
-               controller.WriteError(w, discovery.ErrInternal, 
errorsEx.MsgJSON)
-               return
-       }
-       controller.WriteJSON(w, v)
+       controller.WriteResponse(w, r, nil, role)
 }
 
 //DeleteRole delete the role info by role name
-func (r *RoleResource) DeleteRole(w http.ResponseWriter, req *http.Request) {
+func (rr *RoleResource) DeleteRole(w http.ResponseWriter, req *http.Request) {
        _, err := dao.DeleteRole(context.TODO(), 
req.URL.Query().Get(":roleName"))
        if err != nil {
                log.Error(errorsEx.MsgJSON, err)
diff --git a/server/rest/admin/controller_v4.go 
b/server/rest/admin/controller_v4.go
index 71b5189..b2c5744 100644
--- a/server/rest/admin/controller_v4.go
+++ b/server/rest/admin/controller_v4.go
@@ -53,30 +53,21 @@ func (ctrl *ControllerV4) Dump(w http.ResponseWriter, r 
*http.Request) {
        }
        ctx := r.Context()
        resp, _ := AdminServiceAPI.Dump(ctx, request)
-
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (ctrl *ControllerV4) Clusters(w http.ResponseWriter, r *http.Request) {
        request := &dump.ClustersRequest{}
        ctx := r.Context()
        resp, _ := AdminServiceAPI.Clusters(ctx, request)
-
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (ctrl *ControllerV4) AlarmList(w http.ResponseWriter, r *http.Request) {
        request := &dump.AlarmListRequest{}
        ctx := r.Context()
        resp, _ := AdminServiceAPI.AlarmList(ctx, request)
-
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (ctrl *ControllerV4) ClearAlarm(w http.ResponseWriter, r *http.Request) {
diff --git a/server/rest/controller/rest_util.go 
b/server/rest/controller/rest_util.go
index 2c77835..5fdad2a 100644
--- a/server/rest/controller/rest_util.go
+++ b/server/rest/controller/rest_util.go
@@ -19,9 +19,7 @@ package controller
 
 import (
        "encoding/json"
-       "fmt"
        "net/http"
-       "strconv"
 
        "github.com/apache/servicecomb-service-center/pkg/log"
        "github.com/apache/servicecomb-service-center/pkg/rest"
@@ -32,10 +30,9 @@ import (
 
 func WriteError(w http.ResponseWriter, code int32, detail string) {
        err := discovery.NewError(code, detail)
-       w.Header().Set(rest.HeaderResponseStatus, 
strconv.Itoa(err.StatusCode()))
        w.Header().Set(rest.HeaderContentType, rest.ContentTypeJSON)
        w.WriteHeader(err.StatusCode())
-       fmt.Fprintln(w, util.BytesToStringWithNoCopy(err.Marshal()))
+       _, _ = w.Write(err.Marshal())
 
        if err.InternalError() {
                err := alarm.Raise(alarm.IDInternalError, 
alarm.AdditionalContext(detail))
@@ -57,42 +54,32 @@ func WriteResponse(w http.ResponseWriter, r *http.Request, 
resp *discovery.Respo
        }
 
        if obj == nil {
-               w.Header().Set(rest.HeaderResponseStatus, 
strconv.Itoa(http.StatusOK))
                w.Header().Set(rest.HeaderContentType, rest.ContentTypeText)
                w.WriteHeader(http.StatusOK)
                return
        }
 
-       // async handler maybe need this obj
        util.SetRequestContext(r, rest.CtxResponseObject, obj)
 
-       b, err := json.Marshal(obj)
-       if err != nil {
-               WriteError(w, discovery.ErrInternal, err.Error())
-               return
-       }
-       w.Header().Set(rest.HeaderResponseStatus, strconv.Itoa(http.StatusOK))
-       w.Header().Set(rest.HeaderContentType, rest.ContentTypeJSON)
-       w.WriteHeader(http.StatusOK)
-       fmt.Fprintln(w, util.BytesToStringWithNoCopy(b))
-}
-
-func WriteJSONIfSuccess(w http.ResponseWriter, resp *discovery.Response, json 
[]byte) {
-       if resp.GetCode() == discovery.ResponseSuccess {
-               WriteJSON(w, json)
-               return
+       var (
+               data []byte
+               err  error
+       )
+       switch body := obj.(type) {
+       case []byte:
+               data = body
+       default:
+               data, err = json.Marshal(body)
+               if err != nil {
+                       WriteError(w, discovery.ErrInternal, err.Error())
+                       return
+               }
        }
-       WriteError(w, resp.GetCode(), resp.GetMessage())
-}
-
-//WriteJSON simply write json
-func WriteJSON(w http.ResponseWriter, json []byte) {
-       w.Header().Set(rest.HeaderResponseStatus, strconv.Itoa(http.StatusOK))
        w.Header().Set(rest.HeaderContentType, rest.ContentTypeJSON)
        w.WriteHeader(http.StatusOK)
-       _, err := w.Write(json)
+       _, err = w.Write(data)
        if err != nil {
-               log.Error("", err)
+               log.Error("write response failed", err)
        }
 }
 
diff --git a/server/rest/controller/v3/main_controller.go 
b/server/rest/controller/v3/main_controller.go
index ecd0da5..7735496 100644
--- a/server/rest/controller/v3/main_controller.go
+++ b/server/rest/controller/v3/main_controller.go
@@ -55,5 +55,5 @@ func (s *MainService) URLPatterns() []rest.Route {
 }
 
 func (s *MainService) GetVersion(w http.ResponseWriter, r *http.Request) {
-       controller.WriteJSONIfSuccess(w, versionResp, versionJsonCache)
+       controller.WriteResponse(w, r, versionResp, versionJsonCache)
 }
diff --git a/server/rest/controller/v4/dependency_controller.go 
b/server/rest/controller/v4/dependency_controller.go
index 8ca5ebe..169dd16 100644
--- a/server/rest/controller/v4/dependency_controller.go
+++ b/server/rest/controller/v4/dependency_controller.go
@@ -98,9 +98,7 @@ func (s *DependencyService) GetConProDependencies(w 
http.ResponseWriter, r *http
                NoSelf:     query.Get("noSelf") == "1",
        }
        resp, _ := core.ServiceAPI.GetConsumerDependencies(r.Context(), request)
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (s *DependencyService) GetProConDependencies(w http.ResponseWriter, r 
*http.Request) {
@@ -111,7 +109,5 @@ func (s *DependencyService) GetProConDependencies(w 
http.ResponseWriter, r *http
                NoSelf:     query.Get("noSelf") == "1",
        }
        resp, _ := core.ServiceAPI.GetProviderDependencies(r.Context(), request)
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
diff --git a/server/rest/controller/v4/instance_controller.go 
b/server/rest/controller/v4/instance_controller.go
index b711a71..92b4b2b 100644
--- a/server/rest/controller/v4/instance_controller.go
+++ b/server/rest/controller/v4/instance_controller.go
@@ -75,9 +75,7 @@ func (s *MicroServiceInstanceService) RegisterInstance(w 
http.ResponseWriter, r
                controller.WriteError(w, pb.ErrInternal, "register instance 
failed")
                return
        }
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 //TODO 什么样的服务允许更新服务心跳,只能是本服务才可以更新自己,如何屏蔽其他服务伪造的心跳更新?
@@ -107,14 +105,7 @@ func (s *MicroServiceInstanceService) HeartbeatSet(w 
http.ResponseWriter, r *htt
                return
        }
        resp, _ := core.InstanceAPI.HeartbeatSet(r.Context(), request)
-
-       if resp.Response.GetCode() == pb.ResponseSuccess {
-               controller.WriteResponse(w, r, nil, nil)
-               return
-       }
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, nil)
 }
 
 func (s *MicroServiceInstanceService) UnregisterInstance(w 
http.ResponseWriter, r *http.Request) {
@@ -156,7 +147,6 @@ func (s *MicroServiceInstanceService) FindInstances(w 
http.ResponseWriter, r *ht
                w.WriteHeader(http.StatusNotModified)
                return
        }
-
        controller.WriteResponse(w, r, respInternal, resp)
 }
 
@@ -181,9 +171,7 @@ func (s *MicroServiceInstanceService) InstancesAction(w 
http.ResponseWriter, r *
                request.ConsumerServiceId = r.Header.Get("X-ConsumerId")
                ctx := util.SetTargetDomainProject(r.Context(), 
r.Header.Get("X-Domain-Name"), r.URL.Query().Get(":project"))
                resp, _ := core.InstanceAPI.BatchFind(ctx, request)
-               respInternal := resp.Response
-               resp.Response = nil
-               controller.WriteResponse(w, r, respInternal, resp)
+               controller.WriteResponse(w, r, resp.Response, resp)
        default:
                err = fmt.Errorf("Invalid action: %s", action)
                log.Errorf(err, "invalid request")
diff --git a/server/rest/controller/v4/main_controller.go 
b/server/rest/controller/v4/main_controller.go
index 01e6d9b..4070578 100644
--- a/server/rest/controller/v4/main_controller.go
+++ b/server/rest/controller/v4/main_controller.go
@@ -55,9 +55,7 @@ func (s *MainService) URLPatterns() []rest.Route {
 
 func (s *MainService) ClusterHealth(w http.ResponseWriter, r *http.Request) {
        resp, _ := core.InstanceAPI.ClusterHealth(r.Context())
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (s *MainService) GetVersion(w http.ResponseWriter, r *http.Request) {
@@ -69,5 +67,5 @@ func (s *MainService) GetVersion(w http.ResponseWriter, r 
*http.Request) {
                versionJSONCache, _ = json.Marshal(result)
                versionResp = pb.CreateResponse(pb.ResponseSuccess, "get 
version successfully")
        })
-       controller.WriteJSONIfSuccess(w, versionResp, versionJSONCache)
+       controller.WriteResponse(w, r, versionResp, versionJSONCache)
 }
diff --git a/server/rest/controller/v4/microservice_controller.go 
b/server/rest/controller/v4/microservice_controller.go
index 43254c9..6c7797d 100644
--- a/server/rest/controller/v4/microservice_controller.go
+++ b/server/rest/controller/v4/microservice_controller.go
@@ -68,9 +68,7 @@ func (s *MicroServiceService) Register(w http.ResponseWriter, 
r *http.Request) {
                controller.WriteError(w, pb.ErrInternal, err.Error())
                return
        }
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (s *MicroServiceService) Update(w http.ResponseWriter, r *http.Request) {
@@ -130,9 +128,7 @@ func (s *MicroServiceService) GetServices(w 
http.ResponseWriter, r *http.Request
                controller.WriteError(w, pb.ErrInternal, err.Error())
                return
        }
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (s *MicroServiceService) GetExistence(w http.ResponseWriter, r 
*http.Request) {
@@ -153,10 +149,8 @@ func (s *MicroServiceService) GetExistence(w 
http.ResponseWriter, r *http.Reques
                return
        }
        w.Header().Add("X-Schema-Summary", resp.Summary)
-       respInternal := resp.Response
-       resp.Response = nil
        resp.Summary = ""
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (s *MicroServiceService) GetServiceOne(w http.ResponseWriter, r 
*http.Request) {
@@ -169,9 +163,7 @@ func (s *MicroServiceService) GetServiceOne(w 
http.ResponseWriter, r *http.Reque
                controller.WriteError(w, pb.ErrInternal, "get service failed")
                return
        }
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (s *MicroServiceService) UnregisterServices(w http.ResponseWriter, r 
*http.Request) {
@@ -197,7 +189,5 @@ func (s *MicroServiceService) UnregisterServices(w 
http.ResponseWriter, r *http.
                controller.WriteError(w, pb.ErrInternal, "delete services 
failed")
                return
        }
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
diff --git a/server/rest/controller/v4/query_rule_controller.go 
b/server/rest/controller/v4/query_rule_controller.go
index cfce2d0..c35908c 100644
--- a/server/rest/controller/v4/query_rule_controller.go
+++ b/server/rest/controller/v4/query_rule_controller.go
@@ -67,9 +67,7 @@ func (s *RuleService) AddRule(w http.ResponseWriter, r 
*http.Request) {
                controller.WriteError(w, pb.ErrInternal, "add rule failed")
                return
        }
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (s *RuleService) DeleteRule(w http.ResponseWriter, r *http.Request) {
@@ -117,7 +115,5 @@ func (s *RuleService) GetRules(w http.ResponseWriter, r 
*http.Request) {
        resp, _ := core.ServiceAPI.GetRule(r.Context(), 
&pb.GetServiceRulesRequest{
                ServiceId: r.URL.Query().Get(":serviceId"),
        })
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
diff --git a/server/rest/controller/v4/schema_controller.go 
b/server/rest/controller/v4/schema_controller.go
index cdb642f..802ed84 100644
--- a/server/rest/controller/v4/schema_controller.go
+++ b/server/rest/controller/v4/schema_controller.go
@@ -19,6 +19,7 @@ package v4
 
 import (
        "encoding/json"
+       "errors"
        "io/ioutil"
        "net/http"
        "strings"
@@ -32,6 +33,8 @@ import (
        pb "github.com/go-chassis/cari/discovery"
 )
 
+var errModifySchemaDisabled = errors.New("schema modify is disabled")
+
 type SchemaService struct {
        //
 }
@@ -54,8 +57,7 @@ func (s *SchemaService) URLPatterns() []rest.Route {
 }
 
 func (s *SchemaService) DisableSchema(w http.ResponseWriter, r *http.Request) {
-       w.WriteHeader(http.StatusForbidden)
-       _, _ = w.Write([]byte("schema modify is disabled"))
+       controller.WriteError(w, pb.ErrForbidden, 
errModifySchemaDisabled.Error())
 }
 
 func (s *SchemaService) GetSchemas(w http.ResponseWriter, r *http.Request) {
@@ -67,9 +69,7 @@ func (s *SchemaService) GetSchemas(w http.ResponseWriter, r 
*http.Request) {
        resp, _ := core.ServiceAPI.GetSchemaInfo(r.Context(), request)
        w.Header().Add("X-Schema-Summary", resp.SchemaSummary)
        resp.SchemaSummary = ""
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (s *SchemaService) ModifySchema(w http.ResponseWriter, r *http.Request) {
@@ -147,7 +147,5 @@ func (s *SchemaService) GetAllSchemas(w 
http.ResponseWriter, r *http.Request) {
                WithSchema: withSchema == "1",
        }
        resp, _ := core.ServiceAPI.GetAllSchemaInfo(r.Context(), request)
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
diff --git a/server/rest/controller/v4/tag_controller.go 
b/server/rest/controller/v4/tag_controller.go
index 6576491..bcf1644 100644
--- a/server/rest/controller/v4/tag_controller.go
+++ b/server/rest/controller/v4/tag_controller.go
@@ -85,9 +85,7 @@ func (s *TagService) GetTags(w http.ResponseWriter, r 
*http.Request) {
        resp, _ := core.ServiceAPI.GetTags(r.Context(), 
&pb.GetServiceTagsRequest{
                ServiceId: r.URL.Query().Get(":serviceId"),
        })
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (s *TagService) DeleteTags(w http.ResponseWriter, r *http.Request) {
diff --git a/server/rest/govern/controller_v4.go 
b/server/rest/govern/controller_v4.go
index 6707a7f..c28673f 100644
--- a/server/rest/govern/controller_v4.go
+++ b/server/rest/govern/controller_v4.go
@@ -133,10 +133,7 @@ func (governService *ResourceV4) GetServiceDetail(w 
http.ResponseWriter, r *http
        }
        ctx := r.Context()
        resp, _ := ServiceAPI.GetServiceDetail(ctx, request)
-
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (governService *ResourceV4) GetAllServicesInfo(w http.ResponseWriter, r 
*http.Request) {
@@ -158,19 +155,14 @@ func (governService *ResourceV4) GetAllServicesInfo(w 
http.ResponseWriter, r *ht
                request.CountOnly = true
        }
        resp, _ := ServiceAPI.GetServicesInfo(ctx, request)
-
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (governService *ResourceV4) GetAllServicesStatistics(w 
http.ResponseWriter, r *http.Request) {
        request := &pb.GetServicesRequest{}
        ctx := r.Context()
        resp, _ := ServiceAPI.GetServicesStatistics(ctx, request)
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
 
 func (governService *ResourceV4) GetAllApplications(w http.ResponseWriter, r 
*http.Request) {
@@ -180,8 +172,5 @@ func (governService *ResourceV4) GetAllApplications(w 
http.ResponseWriter, r *ht
        request.Environment = query.Get("env")
        request.WithShared = util.StringTRUE(query.Get("withShared"))
        resp, _ := ServiceAPI.GetApplications(ctx, request)
-
-       respInternal := resp.Response
-       resp.Response = nil
-       controller.WriteResponse(w, r, respInternal, resp)
+       controller.WriteResponse(w, r, resp.Response, resp)
 }
diff --git a/server/service/gov/kie/kie_distributor.go 
b/server/service/gov/kie/kie_distributor.go
index 0d52473..4533a1d 100644
--- a/server/service/gov/kie/kie_distributor.go
+++ b/server/service/gov/kie/kie_distributor.go
@@ -58,7 +58,9 @@ var PolicyNames = []string{"retry", "rateLimiting", 
"circuitBreaker", "bulkhead"
 var rule = Validator{}
 
 func (d *Distributor) Create(kind, project string, spec []byte) ([]byte, 
error) {
-       p := &gov.Policy{}
+       p := &gov.Policy{
+               GovernancePolicy: &gov.GovernancePolicy{Selector: 
&gov.Selector{}},
+       }
        err := json.Unmarshal(spec, p)
        if err != nil {
                return nil, err

Reply via email to