You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2020/10/20 03:09:20 UTC

[apisix-dashboard] branch refactor updated: feat: compatible with HTTP status of `admin api` (#563)

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

juzhiyuan pushed a commit to branch refactor
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/refactor by this push:
     new 2088156  feat: compatible with HTTP status of `admin api` (#563)
2088156 is described below

commit 2088156d6b3589faef83a239120d06dcddc8d31c
Author: nic-chen <33...@users.noreply.github.com>
AuthorDate: Tue Oct 20 11:09:08 2020 +0800

    feat: compatible with HTTP status of `admin api` (#563)
    
    * feat: compatible with HTTP status of `admin api`
    
    * test cases and improve
---
 api/go.mod                                |  9 ++----
 api/go.sum                                | 19 +++++--------
 api/internal/core/store/validate.go       | 10 +++----
 api/internal/core/store/validate_test.go  |  2 +-
 api/internal/handler/consumer/consumer.go | 18 +++++++-----
 api/internal/handler/handler.go           | 19 +++++++++++++
 api/internal/handler/route/route.go       | 38 ++++++++++++++-----------
 api/internal/handler/route/route_test.go  | 26 ++++++++++++++++-
 api/internal/handler/service/service.go   | 47 ++++++++++++++++++++++++-------
 api/internal/handler/ssl/ssl.go           | 27 ++++++++++--------
 api/internal/handler/upstream/upstream.go | 16 +++++------
 11 files changed, 153 insertions(+), 78 deletions(-)

diff --git a/api/go.mod b/api/go.mod
index face2f0..0f53629 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -3,8 +3,8 @@ module github.com/apisix/manager-api
 go 1.13
 
 require (
-	github.com/api7/apitest v1.4.9
 	github.com/api7/go-jsonpatch v0.0.0-20180223123257-a8710867776e
+	github.com/cameront/go-jsonpatch v0.0.0-20180223123257-a8710867776e // indirect
 	github.com/coreos/etcd v3.3.25+incompatible // indirect
 	github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
@@ -16,14 +16,12 @@ require (
 	github.com/gogo/protobuf v1.3.1 // indirect
 	github.com/google/uuid v1.1.2 // indirect
 	github.com/jinzhu/gorm v1.9.12
-	github.com/magiconair/properties v1.8.1
 	github.com/satori/go.uuid v1.2.0
-	github.com/shiningrush/droplet v0.1.2
-	github.com/shiningrush/droplet/wrapper/gin v0.1.0
+	github.com/shiningrush/droplet v0.2.1
+	github.com/shiningrush/droplet/wrapper/gin v0.2.0
 	github.com/sirupsen/logrus v1.6.0
 	github.com/sony/sonyflake v1.0.0
 	github.com/spf13/viper v1.7.1
-	github.com/steinfletcher/apitest v1.4.10 // indirect
 	github.com/stretchr/testify v1.6.1
 	github.com/tidwall/gjson v1.6.0
 	github.com/xeipuuv/gojsonschema v1.2.0
@@ -32,7 +30,6 @@ require (
 	golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect
 	golang.org/x/sys v0.0.0-20200915084602-288bc346aa39 // indirect
 	golang.org/x/text v0.3.3 // indirect
-	gopkg.in/resty.v1 v1.12.0
 	sigs.k8s.io/yaml v1.2.0 // indirect
 )
 
diff --git a/api/go.sum b/api/go.sum
index 6201e29..7646a70 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -16,8 +16,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/api7/apitest v1.4.9 h1:FYTUQJ1hgeB9UvMFif1jjbfiA+XqHPEBfsjhDskytA8=
-github.com/api7/apitest v1.4.9/go.mod h1:YZruZ+jDMFL6rNgMWiuhwCTugNN0mJkLCYCHG3ICYlE=
 github.com/api7/go-jsonpatch v0.0.0-20180223123257-a8710867776e h1:TX/8xM53DHaIIBr4wU+ifYI8IkUzS8+HrX8kzIzaaG0=
 github.com/api7/go-jsonpatch v0.0.0-20180223123257-a8710867776e/go.mod h1:yc49guNPyTy2deyszk3erQN+2vO2NwLKPNicXurj7Hs=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -31,6 +29,8 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm
 github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
 github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
 github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI=
+github.com/cameront/go-jsonpatch v0.0.0-20180223123257-a8710867776e h1:6c3+GQuYUWljNcReOg4gxMUss9Gjll+5Y9vqDM+ILy8=
+github.com/cameront/go-jsonpatch v0.0.0-20180223123257-a8710867776e/go.mod h1:kdPJxKAfR3ZdD+MWYorN1oTdV9+qwJy9jO/0meJmcxU=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
@@ -48,6 +48,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
 github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
 github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
 github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
@@ -277,14 +278,10 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/shiningrush/droplet v0.0.0-20191118073048-00b06fe19ce4 h1:p2mP/ZZegqnshl0Ab9pFrrFWkWxeQNX+/P+vPMHHaBs=
 github.com/shiningrush/droplet v0.0.0-20191118073048-00b06fe19ce4/go.mod h1:E/th13n/wtPi+Cj2f0hAAEFeT3gb5xsS6Ob4WRrdxdM=
-github.com/shiningrush/droplet v0.1.0 h1:Lk/nzfouI8Xqv9VtzNZZISwTVxWeXmEd9IlgIZwU9IA=
-github.com/shiningrush/droplet v0.1.0/go.mod h1:akW2vIeamvMD6zj6wIBfzYn6StGXBxwlW3gA+hcHu5M=
-github.com/shiningrush/droplet v0.1.1 h1:x+69JP60jzq6ROmsDooNhVSX8jhwFEmjBnryVvCHgjc=
-github.com/shiningrush/droplet v0.1.1/go.mod h1:akW2vIeamvMD6zj6wIBfzYn6StGXBxwlW3gA+hcHu5M=
-github.com/shiningrush/droplet v0.1.2 h1:nGE6Ii8Hgra6UQ6lr4jvkvFuSsmg3AASf+KCF8wQgxY=
-github.com/shiningrush/droplet v0.1.2/go.mod h1:akW2vIeamvMD6zj6wIBfzYn6StGXBxwlW3gA+hcHu5M=
-github.com/shiningrush/droplet/wrapper/gin v0.1.0 h1:eKUtuInaz8BH9dwjDnpdnP29iH7bhaB0NIOF9tL7nFM=
-github.com/shiningrush/droplet/wrapper/gin v0.1.0/go.mod h1:ZJu+sCRrVXn5Pg618c1KK3Ob2UiXGuPM1ROx5uMM9YQ=
+github.com/shiningrush/droplet v0.2.1 h1:p2utttTbCfgiL+x0Zrb2jFeWspB7/o+v3e+R94G6nm4=
+github.com/shiningrush/droplet v0.2.1/go.mod h1:akW2vIeamvMD6zj6wIBfzYn6StGXBxwlW3gA+hcHu5M=
+github.com/shiningrush/droplet/wrapper/gin v0.2.0 h1:LHkU+TbSkpePgXrTg3hJoSZlCMS03GeWMl0t+oLkd44=
+github.com/shiningrush/droplet/wrapper/gin v0.2.0/go.mod h1:ZJu+sCRrVXn5Pg618c1KK3Ob2UiXGuPM1ROx5uMM9YQ=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
@@ -307,8 +304,6 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
 github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/steinfletcher/apitest v1.4.10 h1:uQ79AiO3U1hIM829m8p7ke0NsybrBaTLzv23gsHqjzQ=
-github.com/steinfletcher/apitest v1.4.10/go.mod h1:0MT98QwexQVvf5pIn3fqiC/+8Nyd7A4RShxuSjnpOcE=
 github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
diff --git a/api/internal/core/store/validate.go b/api/internal/core/store/validate.go
index a5851c0..4496ee7 100644
--- a/api/internal/core/store/validate.go
+++ b/api/internal/core/store/validate.go
@@ -105,7 +105,7 @@ func getPlugins(reqBody interface{}) map[string]interface{} {
 func (v *APISIXJsonSchemaValidator) Validate(obj interface{}) error {
 	ret, err := v.schema.Validate(gojsonschema.NewGoLoader(obj))
 	if err != nil {
-		return fmt.Errorf("validate failed: %w", err)
+		return fmt.Errorf("scheme validate failed: %w", err)
 	}
 
 	if !ret.Valid() {
@@ -125,17 +125,17 @@ func (v *APISIXJsonSchemaValidator) Validate(obj interface{}) error {
 		for pluginName, pluginConf := range plugins {
 			schemaDef := conf.Schema.Get("plugins." + pluginName).String()
 			if schemaDef == "" {
-				return fmt.Errorf("schema not found")
+				return fmt.Errorf("scheme validate failed: schema not found")
 			}
 
 			s, err := gojsonschema.NewSchema(gojsonschema.NewStringLoader(schemaDef))
 			if err != nil {
-				return fmt.Errorf("new schema failed: %w", err)
+				return fmt.Errorf("scheme validate failed: %w", err)
 			}
 
 			ret, err := s.Validate(gojsonschema.NewGoLoader(pluginConf))
 			if err != nil {
-				return fmt.Errorf("validate failed: %w", err)
+				return fmt.Errorf("scheme validate failed: %w", err)
 			}
 
 			if !ret.Valid() {
@@ -146,7 +146,7 @@ func (v *APISIXJsonSchemaValidator) Validate(obj interface{}) error {
 					}
 					errString.AppendString(vErr.String())
 				}
-				return fmt.Errorf("scheme validate fail: %s", errString.String())
+				return fmt.Errorf("scheme validate failed: %s", errString.String())
 			}
 		}
 	}
diff --git a/api/internal/core/store/validate_test.go b/api/internal/core/store/validate_test.go
index 18597d8..1e26384 100644
--- a/api/internal/core/store/validate_test.go
+++ b/api/internal/core/store/validate_test.go
@@ -135,6 +135,6 @@ func TestAPISIXJsonSchemaValidator_Validate(t *testing.T) {
 
 	err = validator.Validate(consumer3)
 	assert.NotNil(t, err)
-	assert.EqualError(t, err, "scheme validate fail: (root): count is required")
+	assert.EqualError(t, err, "scheme validate failed: (root): count is required")
 
 }
diff --git a/api/internal/handler/consumer/consumer.go b/api/internal/handler/consumer/consumer.go
index f00d1d2..9ceb853 100644
--- a/api/internal/handler/consumer/consumer.go
+++ b/api/internal/handler/consumer/consumer.go
@@ -18,11 +18,13 @@ package consumer
 
 import (
 	"fmt"
+	"net/http"
 	"reflect"
 	"strings"
 
 	"github.com/gin-gonic/gin"
 	"github.com/shiningrush/droplet"
+	"github.com/shiningrush/droplet/data"
 	"github.com/shiningrush/droplet/wrapper"
 	wgin "github.com/shiningrush/droplet/wrapper/gin"
 
@@ -65,7 +67,7 @@ func (h *Handler) Get(c droplet.Context) (interface{}, error) {
 
 	r, err := h.consumerStore.Get(input.Username)
 	if err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 	return r, nil
 }
@@ -98,12 +100,13 @@ func (h *Handler) List(c droplet.Context) (interface{}, error) {
 func (h *Handler) Create(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*entity.Consumer)
 	if input.ID != "" && input.ID != input.Username {
-		return nil, fmt.Errorf("consumer's id and username must be a same value")
+		return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+			fmt.Errorf("consumer's id and username must be a same value")
 	}
 	input.ID = input.Username
 
 	if err := h.consumerStore.Create(c.Context(), input); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -117,7 +120,8 @@ type UpdateInput struct {
 func (h *Handler) Update(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*UpdateInput)
 	if input.ID != "" && input.ID != input.Username {
-		return nil, fmt.Errorf("consumer's id and username must be a same value")
+		return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+			fmt.Errorf("consumer's id and username must be a same value")
 	}
 	if input.Username != "" {
 		input.Consumer.Username = input.Username
@@ -128,10 +132,10 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
 		//if not exists, create
 		if err.Error() == fmt.Sprintf("key: %s is not found", input.Username) {
 			if err := h.consumerStore.Create(c.Context(), &input.Consumer); err != nil {
-				return nil, err
+				return handler.SpecCodeResponse(err), err
 			}
 		} else {
-			return nil, err
+			return handler.SpecCodeResponse(err), err
 		}
 	}
 
@@ -146,7 +150,7 @@ func (h *Handler) BatchDelete(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*BatchDelete)
 
 	if err := h.consumerStore.BatchDelete(c.Context(), strings.Split(input.UserNames, ",")); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
diff --git a/api/internal/handler/handler.go b/api/internal/handler/handler.go
index f1d653d..db2c0a5 100644
--- a/api/internal/handler/handler.go
+++ b/api/internal/handler/handler.go
@@ -17,7 +17,11 @@
 package handler
 
 import (
+	"net/http"
+	"strings"
+
 	"github.com/gin-gonic/gin"
+	"github.com/shiningrush/droplet/data"
 )
 
 type RegisterFactory func() (RouteRegister, error)
@@ -25,3 +29,18 @@ type RegisterFactory func() (RouteRegister, error)
 type RouteRegister interface {
 	ApplyRoute(r *gin.Engine)
 }
+
+func SpecCodeResponse(err error) *data.SpecCodeResponse {
+	errMsg := err.Error()
+	if strings.Contains(errMsg, "required") ||
+		strings.Contains(errMsg, "conflicted") ||
+		strings.Contains(errMsg, "scheme validate fail") {
+		return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}
+	}
+
+	if strings.Contains(errMsg, "not found") {
+		return &data.SpecCodeResponse{StatusCode: http.StatusNotFound}
+	}
+
+	return &data.SpecCodeResponse{StatusCode: http.StatusInternalServerError}
+}
diff --git a/api/internal/handler/route/route.go b/api/internal/handler/route/route.go
index adb1954..7131bf1 100644
--- a/api/internal/handler/route/route.go
+++ b/api/internal/handler/route/route.go
@@ -20,6 +20,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
+	"net/http"
 	"os/exec"
 	"reflect"
 	"strings"
@@ -81,7 +82,7 @@ func (h *Handler) Get(c droplet.Context) (interface{}, error) {
 
 	r, err := h.routeStore.Get(input.ID)
 	if err != nil {
-		return nil, err
+		return &data.SpecCodeResponse{StatusCode: http.StatusNotFound}, err
 	}
 
 	//format respond
@@ -191,18 +192,20 @@ func (h *Handler) Create(c droplet.Context) (interface{}, error) {
 		_, err := h.svcStore.Get(input.ServiceID)
 		if err != nil {
 			if err == data.ErrNotFound {
-				return nil, fmt.Errorf("service id: %s not found", input.ServiceID)
+				return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+					fmt.Errorf("service id: %s not found", input.ServiceID)
 			}
-			return nil, err
+			return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, err
 		}
 	}
 	if input.UpstreamID != "" {
 		_, err := h.upstreamStore.Get(input.UpstreamID)
 		if err != nil {
 			if err == data.ErrNotFound {
-				return nil, fmt.Errorf("upstream id: %s not found", input.UpstreamID)
+				return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+					fmt.Errorf("upstream id: %s not found", input.UpstreamID)
 			}
-			return nil, err
+			return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, err
 		}
 	}
 
@@ -226,7 +229,7 @@ func (h *Handler) Create(c droplet.Context) (interface{}, error) {
 	}
 
 	if err := h.routeStore.Create(c.Context(), input); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -246,18 +249,20 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
 		_, err := h.svcStore.Get(input.ServiceID)
 		if err != nil {
 			if err == data.ErrNotFound {
-				return nil, fmt.Errorf("service id: %s not found", input.ServiceID)
+				return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+					fmt.Errorf("service id: %s not found", input.ServiceID)
 			}
-			return nil, err
+			return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, err
 		}
 	}
 	if input.UpstreamID != "" {
 		_, err := h.upstreamStore.Get(input.UpstreamID)
 		if err != nil {
 			if err == data.ErrNotFound {
-				return nil, fmt.Errorf("upstream id: %s not found", input.UpstreamID)
+				return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+					fmt.Errorf("upstream id: %s not found", input.UpstreamID)
 			}
-			return nil, err
+			return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, err
 		}
 	}
 
@@ -269,23 +274,23 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
 		var err error
 		input.Route.Script, err = generateLuaCode(input.Script.(map[string]interface{}))
 		if err != nil {
-			return nil, err
+			return &data.SpecCodeResponse{StatusCode: http.StatusInternalServerError}, err
 		}
 		//save original conf
 		if err = h.scriptStore.Update(c.Context(), script, true); err != nil {
 			//if not exists, create
 			if err.Error() == fmt.Sprintf("key: %s is not found", script.ID) {
 				if err := h.scriptStore.Create(c.Context(), script); err != nil {
-					return nil, err
+					return handler.SpecCodeResponse(err), err
 				}
 			} else {
-				return nil, err
+				return handler.SpecCodeResponse(err), err
 			}
 		}
 	}
 
 	if err := h.routeStore.Update(c.Context(), &input.Route, true); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -300,7 +305,7 @@ func (h *Handler) BatchDelete(c droplet.Context) (interface{}, error) {
 
 	//delete route
 	if err := h.routeStore.BatchDelete(c.Context(), strings.Split(input.IDs, ",")); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	//delete stored script
@@ -348,7 +353,8 @@ func Exist(c *gin.Context) (interface{}, error) {
 	if len(rows) > 0 {
 		r := rows[0].(*entity.Route)
 		if r.ID != exclude {
-			return nil, consts.InvalidParam("Route name is reduplicate")
+			return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+				consts.InvalidParam("Route name is reduplicate")
 		}
 	}
 
diff --git a/api/internal/handler/route/route_test.go b/api/internal/handler/route/route_test.go
index 438a058..9269d34 100644
--- a/api/internal/handler/route/route_test.go
+++ b/api/internal/handler/route/route_test.go
@@ -19,10 +19,12 @@ package route
 
 import (
 	"encoding/json"
+	"net/http"
 	"testing"
 	"time"
 
 	"github.com/shiningrush/droplet"
+	"github.com/shiningrush/droplet/data"
 	"github.com/stretchr/testify/assert"
 
 	"github.com/apisix/manager-api/internal/core/entity"
@@ -837,7 +839,29 @@ func TestRoute(t *testing.T) {
 	json.Unmarshal([]byte(reqBody), getInput)
 	ctx.SetInput(getInput)
 	ret, err = handler.Get(ctx)
-	assert.Nil(t, ret)
 	assert.EqualError(t, err, "data not found")
+	assert.Equal(t, http.StatusNotFound, ret.(*data.SpecCodeResponse).StatusCode)
+
+	//delete test data
+	reqBody = `{"ids": "not-exists"}`
+	json.Unmarshal([]byte(reqBody), inputDel)
+	ctx.SetInput(inputDel)
+	ret, err = handler.BatchDelete(ctx)
+	assert.NotNil(t, err)
+	assert.Equal(t, http.StatusNotFound, ret.(*data.SpecCodeResponse).StatusCode)
+
+	//create route with not exist upstream id
+	route4 := &entity.Route{}
+	reqBody = `{
+      "id": "2222",
+      "name": "r222",
+      "uris": ["/aa", "/bb"],
+      "upstream_id": "not-exists"
+  }`
+	json.Unmarshal([]byte(reqBody), route4)
+	ctx.SetInput(route4)
+	ret, err = handler.Create(ctx)
+	assert.NotNil(t, err)
+	assert.Equal(t, http.StatusBadRequest, ret.(*data.SpecCodeResponse).StatusCode)
 
 }
diff --git a/api/internal/handler/service/service.go b/api/internal/handler/service/service.go
index deee899..09e9a18 100644
--- a/api/internal/handler/service/service.go
+++ b/api/internal/handler/service/service.go
@@ -17,12 +17,15 @@
 package service
 
 import (
+	"fmt"
+	"net/http"
 	"reflect"
 	"strings"
 
 	"github.com/api7/go-jsonpatch"
 	"github.com/gin-gonic/gin"
 	"github.com/shiningrush/droplet"
+	"github.com/shiningrush/droplet/data"
 	"github.com/shiningrush/droplet/wrapper"
 	wgin "github.com/shiningrush/droplet/wrapper/gin"
 
@@ -32,12 +35,14 @@ import (
 )
 
 type Handler struct {
-	serviceStore store.Interface
+	serviceStore  store.Interface
+	upstreamStore store.Interface
 }
 
 func NewHandler() (handler.RouteRegister, error) {
 	return &Handler{
-		serviceStore: store.GetStore(store.HubKeyService),
+		serviceStore:  store.GetStore(store.HubKeyService),
+		upstreamStore: store.GetStore(store.HubKeyUpstream),
 	}, nil
 }
 
@@ -67,7 +72,7 @@ func (h *Handler) Get(c droplet.Context) (interface{}, error) {
 
 	r, err := h.serviceStore.Get(input.ID)
 	if err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	service := r.(*entity.Service)
@@ -109,8 +114,19 @@ func (h *Handler) List(c droplet.Context) (interface{}, error) {
 func (h *Handler) Create(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*entity.Service)
 
+	if input.UpstreamID != "" {
+		_, err := h.upstreamStore.Get(input.UpstreamID)
+		if err != nil {
+			if err == data.ErrNotFound {
+				return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+					fmt.Errorf("upstream id: %s not found", input.UpstreamID)
+			}
+			return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, err
+		}
+	}
+
 	if err := h.serviceStore.Create(c.Context(), input); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -125,8 +141,19 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*UpdateInput)
 	input.Service.ID = input.ID
 
+	if input.UpstreamID != "" {
+		_, err := h.upstreamStore.Get(input.UpstreamID)
+		if err != nil {
+			if err == data.ErrNotFound {
+				return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+					fmt.Errorf("upstream id: %s not found", input.UpstreamID)
+			}
+			return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, err
+		}
+	}
+
 	if err := h.serviceStore.Update(c.Context(), &input.Service, true); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -140,7 +167,7 @@ func (h *Handler) BatchDelete(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*BatchDelete)
 
 	if err := h.serviceStore.BatchDelete(c.Context(), strings.Split(input.IDs, ",")); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -157,7 +184,7 @@ func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
 
 	stored, err := h.serviceStore.Get(input.ID)
 	if err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	var patch jsonpatch.Patch
@@ -170,16 +197,16 @@ func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
 	} else {
 		patch, err = jsonpatch.MakePatch(stored, input.Service)
 		if err != nil {
-			return nil, err
+			return handler.SpecCodeResponse(err), err
 		}
 	}
 
 	if err := patch.Apply(&stored); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	if err := h.serviceStore.Update(c.Context(), &stored, false); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
diff --git a/api/internal/handler/ssl/ssl.go b/api/internal/handler/ssl/ssl.go
index 3291846..79963fb 100644
--- a/api/internal/handler/ssl/ssl.go
+++ b/api/internal/handler/ssl/ssl.go
@@ -23,12 +23,14 @@ import (
 	"encoding/pem"
 	"errors"
 	"fmt"
+	"net/http"
 	"reflect"
 	"strings"
 
 	"github.com/api7/go-jsonpatch"
 	"github.com/gin-gonic/gin"
 	"github.com/shiningrush/droplet"
+	"github.com/shiningrush/droplet/data"
 	"github.com/shiningrush/droplet/wrapper"
 	wgin "github.com/shiningrush/droplet/wrapper/gin"
 
@@ -78,7 +80,7 @@ func (h *Handler) Get(c droplet.Context) (interface{}, error) {
 
 	ret, err := h.sslStore.Get(input.ID)
 	if err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	//format respond
@@ -141,12 +143,12 @@ func (h *Handler) Create(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*entity.SSL)
 	ssl, err := ParseCert(input.Cert, input.Key)
 	if err != nil {
-		return nil, err
+		return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, err
 	}
 
 	ssl.ID = input.ID
 	if err := h.sslStore.Create(c.Context(), ssl); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -161,12 +163,12 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*UpdateInput)
 	ssl, err := ParseCert(input.Cert, input.Key)
 	if err != nil {
-		return nil, err
+		return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, err
 	}
 
 	ssl.ID = input.ID
 	if err := h.sslStore.Update(c.Context(), ssl, true); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -183,7 +185,7 @@ func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
 
 	stored, err := h.sslStore.Get(input.ID)
 	if err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	var patch jsonpatch.Patch
@@ -196,17 +198,17 @@ func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
 	} else {
 		patch, err = jsonpatch.MakePatch(stored, input.SSL)
 		if err != nil {
-			panic(err)
+			return handler.SpecCodeResponse(err), err
 		}
 	}
 
 	err = patch.Apply(&stored)
 	if err != nil {
-		panic(err)
+		return handler.SpecCodeResponse(err), err
 	}
 
 	if err := h.sslStore.Update(c.Context(), &stored, false); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -220,7 +222,7 @@ func (h *Handler) BatchDelete(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*BatchDelete)
 
 	if err := h.sslStore.BatchDelete(c.Context(), strings.Split(input.Ids, ",")); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -350,7 +352,7 @@ func Exist(c *gin.Context) (interface{}, error) {
 	reqBody, _ := c.GetRawData()
 	var hosts []string
 	if err := json.Unmarshal(reqBody, &hosts); err != nil {
-		return nil, err
+		return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, err
 	}
 
 	routeStore := store.GetStore(store.HubKeySsl)
@@ -367,7 +369,8 @@ func Exist(c *gin.Context) (interface{}, error) {
 	for _, host := range hosts {
 		res := checkSniExists(toRows(ret), host)
 		if !res {
-			return nil, consts.InvalidParam("SSL cert not exists for sni:" + host)
+			return &data.SpecCodeResponse{StatusCode: http.StatusNotFound},
+				consts.InvalidParam("SSL cert not exists for sni:" + host)
 		}
 	}
 
diff --git a/api/internal/handler/upstream/upstream.go b/api/internal/handler/upstream/upstream.go
index d7e3365..d91a62c 100644
--- a/api/internal/handler/upstream/upstream.go
+++ b/api/internal/handler/upstream/upstream.go
@@ -71,7 +71,7 @@ func (h *Handler) Get(c droplet.Context) (interface{}, error) {
 
 	r, err := h.upstreamStore.Get(input.ID)
 	if err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	upstream := r.(*entity.Upstream)
@@ -114,7 +114,7 @@ func (h *Handler) Create(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*entity.Upstream)
 
 	if err := h.upstreamStore.Create(c.Context(), input); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -130,7 +130,7 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
 	input.Upstream.ID = input.ID
 
 	if err := h.upstreamStore.Update(c.Context(), &input.Upstream, true); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -144,7 +144,7 @@ func (h *Handler) BatchDelete(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*BatchDelete)
 
 	if err := h.upstreamStore.BatchDelete(c.Context(), strings.Split(input.IDs, ",")); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil
@@ -161,7 +161,7 @@ func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
 
 	stored, err := h.upstreamStore.Get(input.ID)
 	if err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	var patch jsonpatch.Patch
@@ -174,16 +174,16 @@ func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
 	} else {
 		patch, err = jsonpatch.MakePatch(stored, input.Upstream)
 		if err != nil {
-			return nil, err
+			return handler.SpecCodeResponse(err), err
 		}
 	}
 
 	if err := patch.Apply(&stored); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	if err := h.upstreamStore.Update(c.Context(), &stored, false); err != nil {
-		return nil, err
+		return handler.SpecCodeResponse(err), err
 	}
 
 	return nil, nil