You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ti...@apache.org on 2020/04/07 12:30:02 UTC

[servicecomb-kie] branch master updated: Support parameter validation (#124)

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-kie.git


The following commit(s) were added to refs/heads/master by this push:
     new fb30630  Support parameter validation (#124)
fb30630 is described below

commit fb306300e32e1ecfa661655f5e4bd0659ebfd635
Author: humingcheng <hu...@163.com>
AuthorDate: Tue Apr 7 20:29:52 2020 +0800

    Support parameter validation (#124)
    
    * Support parameter validation
    
    * No need content type when write error; replace rule "key" with "commonName"
---
 cmd/kieserver/main.go                  |   5 ++
 go.mod                                 |   3 +
 go.sum                                 |  38 +++---------
 pkg/model/db_schema.go                 |  16 ++---
 pkg/validate/instance.go               |  36 +++++++++++
 pkg/validate/instance_test.go          | 110 +++++++++++++++++++++++++++++++++
 pkg/validate/rule.go                   |  90 +++++++++++++++++++++++++++
 pkg/validate/rule_test.go              |  39 ++++++++++++
 pkg/validate/validator.go              |  90 +++++++++++++++++++++++++++
 pkg/validate/validator_test.go         |  36 +++++++++++
 server/resource/v1/common.go           |  21 ++-----
 server/resource/v1/history_resource.go |  16 ++---
 server/resource/v1/kv_resource.go      |  47 +++++++-------
 server/resource/v1/kv_resource_test.go |   4 ++
 server/resource/v1/label_resouce.go    |  10 +--
 15 files changed, 470 insertions(+), 91 deletions(-)

diff --git a/cmd/kieserver/main.go b/cmd/kieserver/main.go
index a0323f9..e51b2b5 100644
--- a/cmd/kieserver/main.go
+++ b/cmd/kieserver/main.go
@@ -20,10 +20,12 @@ package main
 import (
 	"os"
 
+	"github.com/apache/servicecomb-kie/pkg/validate"
 	"github.com/apache/servicecomb-kie/server/config"
 	"github.com/apache/servicecomb-kie/server/pubsub"
 	v1 "github.com/apache/servicecomb-kie/server/resource/v1"
 	"github.com/apache/servicecomb-kie/server/service"
+
 	"github.com/go-chassis/go-chassis"
 	"github.com/go-chassis/go-chassis/core/common"
 	"github.com/go-mesh/openlogging"
@@ -94,6 +96,9 @@ func main() {
 	if err := service.DBInit(); err != nil {
 		openlogging.Fatal(err.Error())
 	}
+	if err := validate.Init(); err != nil {
+		openlogging.Fatal("validate init failed: " + err.Error())
+	}
 	pubsub.Init()
 	pubsub.Start()
 	if err := chassis.Run(); err != nil {
diff --git a/go.mod b/go.mod
index 5ab7fa6..51f0f74 100644
--- a/go.mod
+++ b/go.mod
@@ -6,8 +6,11 @@ require (
 	github.com/go-chassis/go-chassis v0.0.0-20200322084331-36849ab2cb10
 	github.com/go-chassis/paas-lager v1.1.1
 	github.com/go-mesh/openlogging v1.0.1
+	github.com/go-playground/universal-translator v0.17.0
+	github.com/go-playground/validator v9.31.0+incompatible
 	github.com/hashicorp/mdns v1.0.1 // indirect
 	github.com/hashicorp/serf v0.8.5
+	github.com/leodido/go-urn v1.2.0 // indirect
 	github.com/satori/go.uuid v1.2.0
 	github.com/stretchr/testify v1.4.0
 	github.com/urfave/cli v1.20.0
diff --git a/go.sum b/go.sum
index 44b4b38..f9816e3 100644
--- a/go.sum
+++ b/go.sum
@@ -53,15 +53,8 @@ github.com/go-chassis/foundation v0.1.1-0.20191113114104-2b05871e9ec4 h1:wx8JXvg
 github.com/go-chassis/foundation v0.1.1-0.20191113114104-2b05871e9ec4/go.mod h1:21/ajGtgJlWTCeM0TxGJdRhO8bJkKirWyV8Stlh6g6c=
 github.com/go-chassis/go-archaius v1.2.1-0.20200309104817-8c3d4e87d33c h1:pimEM4Oy/Uf4xG4G7TrRUQbIRFAfHiarxDQQS2gmKaM=
 github.com/go-chassis/go-archaius v1.2.1-0.20200309104817-8c3d4e87d33c/go.mod h1:gVP52u/jCU0fgUjXdUW1VLp5YLLJ+Yl2zoOPrLM/WOM=
-github.com/go-chassis/go-chassis v0.0.0-20200318142212-4ca7f5fb7745 h1:BDHDeJBW+0/iC/1otn4XQ7ndfqzeGrFFi6SLSBuhRvs=
-github.com/go-chassis/go-chassis v0.0.0-20200318142212-4ca7f5fb7745/go.mod h1:UsROzOZPD9YgTpVUE/jFclVxDwHWESLNdwwbfKQ8aMs=
-github.com/go-chassis/go-chassis v0.0.0-20200320074343-30f669c0c47b h1:ZH88vLcGL4bNc+Vbm1g3jrhtTxBSnGvyZCpbbkVa5a0=
-github.com/go-chassis/go-chassis v0.0.0-20200320074343-30f669c0c47b/go.mod h1:UsROzOZPD9YgTpVUE/jFclVxDwHWESLNdwwbfKQ8aMs=
 github.com/go-chassis/go-chassis v0.0.0-20200322084331-36849ab2cb10 h1:l3bUZf0Hjh1+7b2Ed5Fqb/cCOjMGBe8WmPd/ucyoGgQ=
 github.com/go-chassis/go-chassis v0.0.0-20200322084331-36849ab2cb10/go.mod h1:UsROzOZPD9YgTpVUE/jFclVxDwHWESLNdwwbfKQ8aMs=
-github.com/go-chassis/go-chassis v1.8.2-0.20200310060113-4b383ba3d3f0 h1:YD9MtuYIpQb+EKxIzV/swdXUhnV5PtXtSW696JiwW1c=
-github.com/go-chassis/go-chassis v1.8.2-0.20200310060113-4b383ba3d3f0/go.mod h1:sFnVxSvprpy6umPFK4uSdfCDdfqdgbp3FdW/CG0VNnE=
-github.com/go-chassis/go-chassis v1.8.2 h1:lX+XJbIGSkC4c+1h8IcoBQFEX9vaZ0+KNugE24Q/I/Y=
 github.com/go-chassis/go-restful-swagger20 v1.0.3-0.20200310030431-17d80f34264f h1:5QmmNpVcGqIc6tuKNe5EAI4PA8Yn2EL9Oee7YdcJ4PE=
 github.com/go-chassis/go-restful-swagger20 v1.0.3-0.20200310030431-17d80f34264f/go.mod h1:eW62fYuzlNFDvIacB6AV8bhUDCTy4myfTCv0bT9Gbb0=
 github.com/go-chassis/paas-lager v1.1.1 h1:/6wqawUGjPCpd57A/tzJzgC4MnEhNuigbayQS+2VWPQ=
@@ -75,6 +68,12 @@ github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+
 github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
 github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
 github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA=
+github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig=
 github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
@@ -102,7 +101,6 @@ github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGt
 github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
 github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
 github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -119,7 +117,6 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
 github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -129,7 +126,6 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@@ -147,18 +143,15 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX
 github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
 github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
 github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
 github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
 github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
-github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw=
 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
 github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs=
 github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
 github.com/hashicorp/mdns v1.0.1 h1:XFSOubp8KWB+Jd2PDyaX5xUd5bhSP/+pTDZVDMzZJM8=
 github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
@@ -166,7 +159,6 @@ github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG67
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
 github.com/hashicorp/serf v0.8.5 h1:ZynDUIQiA8usmRgPdGPHFdPnb1wgGI9tK3mO9hcAJjc=
 github.com/hashicorp/serf v0.8.5/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k=
-github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -177,7 +169,6 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
 github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
 github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
@@ -189,17 +180,16 @@ github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
 github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
 github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
 github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -224,20 +214,16 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
 github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
 github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg=
 github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -266,9 +252,7 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
 github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
 github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@@ -279,14 +263,12 @@ github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzu
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
 github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
@@ -335,7 +317,6 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
@@ -389,17 +370,14 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
 gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/pkg/model/db_schema.go b/pkg/model/db_schema.go
index ede75ee..da6b496 100644
--- a/pkg/model/db_schema.go
+++ b/pkg/model/db_schema.go
@@ -31,19 +31,19 @@ type LabelDoc struct {
 type KVDoc struct {
 	ID             string `json:"id,omitempty" bson:"id,omitempty" yaml:"id,omitempty" swag:"string"`
 	LabelID        string `json:"label_id,omitempty" bson:"label_id,omitempty" yaml:"label_id,omitempty"`
-	Key            string `json:"key" yaml:"key"`
-	Value          string `json:"value,omitempty" yaml:"value,omitempty"`
-	ValueType      string `json:"value_type,omitempty" bson:"value_type,omitempty" yaml:"value_type,omitempty"` //ini,json,text,yaml,properties
-	Checker        string `json:"check,omitempty" yaml:"check,omitempty"`                                       //python script
+	Key            string `json:"key" yaml:"key" validate:"commonName"`
+	Value          string `json:"value,omitempty" yaml:"value,omitempty" validate:"ascii,min=1,max=2097152"`
+	ValueType      string `json:"value_type,omitempty" bson:"value_type,omitempty" yaml:"value_type,omitempty" validate:"valueType"` //ini,json,text,yaml,properties
+	Checker        string `json:"check,omitempty" yaml:"check,omitempty"`                                                            //python script
 	CreateRevision int64  `json:"create_revision,omitempty" bson:"create_revision," yaml:"create_revision,omitempty"`
 	UpdateRevision int64  `json:"update_revision,omitempty" bson:"update_revision," yaml:"update_revision,omitempty"`
-	Project        string `json:"project,omitempty" yaml:"project,omitempty"`
-	Status         string `json:"status,omitempty" yaml:"status,omitempty"`
+	Project        string `json:"project,omitempty" yaml:"project,omitempty" validate:"commonName"`
+	Status         string `json:"status,omitempty" yaml:"status,omitempty" validate:"kvStatus"`
 	CreateTime     int64  `json:"create_time,omitempty" bson:"create_time," yaml:"create_time,omitempty"`
 	UpdateTime     int64  `json:"update_time,omitempty" bson:"update_time," yaml:"update_time,omitempty"`
 
-	Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` //redundant
-	Domain string            `json:"domain,omitempty" yaml:"domain,omitempty"` //redundant
+	Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty" validate:"max=64,dive,keys,commonName,endkeys,commonName"` //redundant
+	Domain string            `json:"domain,omitempty" yaml:"domain,omitempty" validate:"commonName"`                                     //redundant
 
 }
 
diff --git a/pkg/validate/instance.go b/pkg/validate/instance.go
new file mode 100644
index 0000000..706fb13
--- /dev/null
+++ b/pkg/validate/instance.go
@@ -0,0 +1,36 @@
+package validate
+
+var defaultValidator = NewValidator()
+
+// custom validate rules
+// please use different tag names from third party tags
+var customRules = []*RegexValidateRule{
+	NewRule("commonName", `^[a-zA-Z0-9]*$|^[a-zA-Z0-9][a-zA-Z0-9_\-.]*[a-zA-Z0-9]$`, &Option{Min: 1, Max: 256}),
+	NewRule("valueType", `^(ini|json|text|yaml|properties){0,1}$`, nil),
+	NewRule("kvStatus", `^(enabled|disabled){0,1}$`, nil),
+}
+
+// tags of third party validate rules we used, for error translation
+var thirdPartyTags = []string{
+	"min", "max", "length",
+}
+
+// Init initializes validate
+func Init() error {
+	for _, r := range customRules {
+		if err := defaultValidator.RegisterRule(r); err != nil {
+			return err
+		}
+	}
+	for _, t := range thirdPartyTags {
+		if err := defaultValidator.AddErrorTranslation4Tag(t); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Validate validates data
+func Validate(v interface{}) error {
+	return defaultValidator.Validate(v)
+}
diff --git a/pkg/validate/instance_test.go b/pkg/validate/instance_test.go
new file mode 100644
index 0000000..5848001
--- /dev/null
+++ b/pkg/validate/instance_test.go
@@ -0,0 +1,110 @@
+package validate_test
+
+import (
+	"testing"
+
+	"github.com/apache/servicecomb-kie/pkg/model"
+	"github.com/apache/servicecomb-kie/pkg/validate"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestValidate(t *testing.T) {
+	s := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" //64
+	testStr := s + s + s + s                                                //256
+	err := validate.Init()
+	assert.NoError(t, err)
+
+	kvDoc := &model.KVDoc{Project: "a", Domain: "a",
+		Key:   "a",
+		Value: "a",
+	}
+	assert.NoError(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:   "",
+		Value: "a",
+	}
+	assert.Error(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:   "a#",
+		Value: "a",
+	}
+	assert.Error(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:   testStr + "a",
+		Value: "a",
+	}
+	assert.Error(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:   "a",
+		Value: "",
+	}
+	assert.Error(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:       "a",
+		Value:     "a",
+		ValueType: "",
+	}
+	assert.NoError(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:       "a",
+		Value:     "a",
+		ValueType: "text",
+	}
+	assert.NoError(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:       "a",
+		Value:     "a",
+		ValueType: "a",
+	}
+	assert.Error(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:    "a",
+		Value:  "a",
+		Status: "",
+	}
+	assert.NoError(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:    "a",
+		Value:  "a",
+		Status: "enabled",
+	}
+	assert.NoError(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:    "a",
+		Value:  "a",
+		Status: "a",
+	}
+	assert.Error(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:    "a",
+		Value:  "a",
+		Labels: nil,
+	}
+	assert.NoError(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:    "a",
+		Value:  "a",
+		Labels: map[string]string{"a": "a"},
+	}
+	assert.NoError(t, validate.Validate(kvDoc))
+
+	kvDoc = &model.KVDoc{Project: "a", Domain: "a",
+		Key:    "a",
+		Value:  "a",
+		Labels: map[string]string{testStr + "a": "a"},
+	}
+	assert.Error(t, validate.Validate(kvDoc))
+}
diff --git a/pkg/validate/rule.go b/pkg/validate/rule.go
new file mode 100644
index 0000000..3fb9560
--- /dev/null
+++ b/pkg/validate/rule.go
@@ -0,0 +1,90 @@
+package validate
+
+import (
+	"fmt"
+	"regexp"
+	"unicode/utf8"
+
+	"github.com/go-playground/validator"
+)
+
+// RegexValidateRule contains an validate tag's info
+type RegexValidateRule struct {
+	tag           string
+	min           int64
+	max           int64
+	regex         *regexp.Regexp
+	validateFuncs []func(string) bool
+}
+
+// Option is RegexValidateRule option
+type Option struct {
+	Min int64
+	Max int64
+}
+
+// Validate validates string
+func (r *RegexValidateRule) Validate(s string) bool {
+	for _, f := range r.validateFuncs {
+		if ok := f(s); !ok {
+			return false
+		}
+	}
+	return true
+}
+
+func (r *RegexValidateRule) validateFL(fl validator.FieldLevel) bool {
+	return r.Validate(fl.Field().String())
+}
+
+// Tag returns the validate rule's tag
+func (r *RegexValidateRule) Tag() string {
+	return r.tag
+}
+
+// Explain explains the rule
+func (r *RegexValidateRule) Explain() string {
+	explain := r.regex.String()
+	if r.max > 0 {
+		explain = fmt.Sprintf("%s , max = %d", explain, r.max)
+	}
+	if r.min > 0 {
+		explain = fmt.Sprintf("%s , min = %d", explain, r.min)
+	}
+	return explain
+}
+
+func (r *RegexValidateRule) matchRegex(s string) bool {
+	return r.regex.MatchString(s)
+}
+func (r *RegexValidateRule) matchMin(s string) bool {
+	return int64(utf8.RuneCountInString(s)) >= r.min
+}
+func (r *RegexValidateRule) matchMax(s string) bool {
+	return int64(utf8.RuneCountInString(s)) <= r.max
+}
+
+// NewRule news a rule
+func NewRule(tag, regexStr string, opt *Option) *RegexValidateRule {
+	r := &RegexValidateRule{
+		tag:           tag,
+		regex:         regexp.MustCompile(regexStr),
+		validateFuncs: make([]func(string) bool, 0),
+	}
+
+	if opt == nil {
+		r.validateFuncs = append(r.validateFuncs, r.matchRegex)
+		return r
+	}
+
+	if opt.Max > 0 {
+		r.max = opt.Max
+		r.validateFuncs = append(r.validateFuncs, r.matchMax)
+	}
+	if opt.Min > 0 {
+		r.min = opt.Min
+		r.validateFuncs = append(r.validateFuncs, r.matchMin)
+	}
+	r.validateFuncs = append(r.validateFuncs, r.matchRegex)
+	return r
+}
diff --git a/pkg/validate/rule_test.go b/pkg/validate/rule_test.go
new file mode 100644
index 0000000..d2336a9
--- /dev/null
+++ b/pkg/validate/rule_test.go
@@ -0,0 +1,39 @@
+package validate_test
+
+import (
+	"testing"
+
+	"github.com/apache/servicecomb-kie/pkg/validate"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewRule(t *testing.T) {
+	rule := validate.NewRule("t", `^[a-zA-Z0-9]*$`, &validate.Option{Min: 2, Max: 4})
+	assert.Equal(t, "t", rule.Tag())
+	rule.Explain()
+	assert.True(t, rule.Validate("ab"))
+	assert.False(t, rule.Validate("a"))
+	assert.False(t, rule.Validate("abcde"))
+	assert.False(t, rule.Validate("ab-"))
+
+	rule = validate.NewRule("t", `^[a-zA-Z0-9]*$`, &validate.Option{Min: 2})
+	rule.Explain()
+	assert.True(t, rule.Validate("ab"))
+	assert.False(t, rule.Validate("a"))
+	assert.True(t, rule.Validate("abcde"))
+	assert.False(t, rule.Validate("ab-"))
+
+	rule = validate.NewRule("t", `^[a-zA-Z0-9]*$`, &validate.Option{Max: 4})
+	rule.Explain()
+	assert.True(t, rule.Validate("ab"))
+	assert.True(t, rule.Validate("a"))
+	assert.False(t, rule.Validate("abcde"))
+	assert.False(t, rule.Validate("ab-"))
+
+	rule = validate.NewRule("t", `^[a-zA-Z0-9]*$`, nil)
+	rule.Explain()
+	assert.True(t, rule.Validate("ab"))
+	assert.True(t, rule.Validate("a"))
+	assert.True(t, rule.Validate("abcdefg12345678"))
+	assert.False(t, rule.Validate("ab-"))
+}
diff --git a/pkg/validate/validator.go b/pkg/validate/validator.go
new file mode 100644
index 0000000..aa2b6b1
--- /dev/null
+++ b/pkg/validate/validator.go
@@ -0,0 +1,90 @@
+package validate
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	ut "github.com/go-playground/universal-translator"
+	valid "github.com/go-playground/validator"
+)
+
+var errorTranslator ut.Translator // no use but as an index
+
+func registerErrorTranslator(_ ut.Translator) error { return nil }
+
+// Validator validates data
+// not safe, use it after initialized
+type Validator struct {
+	rules map[string]*RegexValidateRule
+	valid *valid.Validate
+}
+
+// Validate validates the input data
+func (v *Validator) Validate(i interface{}) error {
+	err := v.valid.Struct(i)
+	if err != nil {
+		return v.wrapError(err)
+	}
+	return nil
+}
+
+// converts the raw error into an easy-to-understand error
+func (v *Validator) wrapError(err error) error {
+	validErr, ok := err.(valid.ValidationErrors)
+	if !ok {
+		return err
+	}
+	msgs := make([]string, len(validErr))
+	for i, ve := range validErr {
+		fe := ve.(valid.FieldError)
+		msgs[i] = fe.Translate(errorTranslator)
+	}
+	return errors.New("validate failed, " + strings.Join(msgs, " | "))
+}
+
+// RegisterRule registers a custom validate rule
+func (v *Validator) RegisterRule(r *RegexValidateRule) error {
+	if r == nil {
+		return errors.New("empty regex validate rule")
+	}
+	v.rules[r.tag] = r
+	if err := v.valid.RegisterValidation(r.tag, r.validateFL); err != nil {
+		return err
+	}
+	if err := v.AddErrorTranslation4Tag(r.tag); err != nil {
+		return err
+	}
+	return nil
+}
+
+// translates raw errors to easy-to-understand messages
+func (v *Validator) translateError(_ ut.Translator, fe valid.FieldError) string {
+	var rule string
+	if r, ok := v.rules[fe.Tag()]; ok {
+		rule = r.Explain()
+	} else {
+		rule = fe.Tag()
+		if len(fe.Param()) > 0 {
+			rule = rule + " = " + fe.Param()
+		}
+	}
+	return fmt.Sprintf("field: %s, rule: %s", fe.Namespace(), rule)
+}
+
+// AddErrorTranslation4Tag adds translation for the errors of some tag,
+// to make the error easier to understand
+func (v *Validator) AddErrorTranslation4Tag(tag string) error {
+	return v.valid.RegisterTranslation(tag,
+		errorTranslator,
+		registerErrorTranslator,
+		v.translateError)
+}
+
+// NewValidator news a validator
+func NewValidator() *Validator {
+	return &Validator{
+		valid: valid.New(),
+		rules: make(map[string]*RegexValidateRule),
+	}
+}
diff --git a/pkg/validate/validator_test.go b/pkg/validate/validator_test.go
new file mode 100644
index 0000000..3bcde81
--- /dev/null
+++ b/pkg/validate/validator_test.go
@@ -0,0 +1,36 @@
+package validate_test
+
+import (
+	"testing"
+
+	"github.com/apache/servicecomb-kie/pkg/validate"
+	"github.com/stretchr/testify/assert"
+)
+
+type student struct {
+	Name    string `validate:"kieTest"`
+	Address string `validate:"alpha,min=2,max=4"`
+}
+
+func TestNewValidator(t *testing.T) {
+	r := validate.NewRule("kieTest", `^[a-zA-Z0-9]*$`, nil)
+	valid := validate.NewValidator()
+	err := valid.RegisterRule(r)
+	assert.Nil(t, err)
+	assert.Nil(t, valid.AddErrorTranslation4Tag("min"))
+	assert.Nil(t, valid.AddErrorTranslation4Tag("max"))
+
+	s := &student{Name: "a1", Address: "abc"}
+	err = valid.Validate(s)
+	assert.Nil(t, err)
+
+	s = &student{Name: "a1-", Address: "abc"}
+	err = valid.Validate(s)
+	assert.NotNil(t, err)
+	t.Log(err)
+
+	s = &student{Name: "a1", Address: "abcde"}
+	err = valid.Validate(s)
+	assert.NotNil(t, err)
+	t.Log(err)
+}
diff --git a/server/resource/v1/common.go b/server/resource/v1/common.go
index 3e1a7ed..0770109 100644
--- a/server/resource/v1/common.go
+++ b/server/resource/v1/common.go
@@ -96,12 +96,11 @@ func ReadLabelCombinations(req *goRestful.Request) ([]map[string]string, error)
 }
 
 //WriteErrResponse write error message to client
-func WriteErrResponse(context *restful.Context, status int, msg, contentType string) {
+func WriteErrResponse(context *restful.Context, status int, msg string) {
+	context.Resp.Header().Set(goRestful.HEADER_ContentType, goRestful.MIME_JSON)
 	context.WriteHeader(status)
 	b, _ := json.MarshalIndent(&ErrorMsg{Msg: msg}, "", " ")
-	context.ReadRestfulResponse().AddHeader(goRestful.HEADER_ContentType, contentType)
 	context.Write(b)
-
 }
 
 func readRequest(ctx *restful.Context, v interface{}) error {
@@ -210,18 +209,6 @@ func checkPagination(offsetStr, limitStr string) (int64, int64, error) {
 	return offset, limit, err
 }
 
-func validatePost(kv *model.KVDoc) error {
-	err := checkDomainAndProject(kv.Domain, kv.Project)
-	if err != nil {
-		return err
-	}
-	if kv.Key == "" {
-		return session.ErrKeyIsNil
-	}
-	_, err = checkStatus(kv.Status)
-	return err
-}
-
 func validatePut(kv *model.KVDoc) error {
 	err := validateGet(kv.Domain, kv.Project, kv.ID)
 	if err != nil {
@@ -281,13 +268,13 @@ func queryAndResponse(rctx *restful.Context, doc *model.KVDoc, offset, limit int
 	}
 	rev, err := service.RevisionService.GetRevision(rctx.Ctx, doc.Domain)
 	if err != nil {
-		WriteErrResponse(rctx, http.StatusInternalServerError, err.Error(), common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusInternalServerError, err.Error())
 		return
 	}
 	kv, err := service.KVService.List(rctx.Ctx, doc.Domain, doc.Project, opts...)
 	if err != nil {
 		openlogging.Error("common: " + err.Error())
-		WriteErrResponse(rctx, http.StatusInternalServerError, common.MsgDBError, common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusInternalServerError, common.MsgDBError)
 		return
 	}
 	rctx.ReadResponseWriter().Header().Set(common.HeaderRevision, strconv.FormatInt(rev, 10))
diff --git a/server/resource/v1/history_resource.go b/server/resource/v1/history_resource.go
index 5c27ee0..b06606f 100644
--- a/server/resource/v1/history_resource.go
+++ b/server/resource/v1/history_resource.go
@@ -45,12 +45,12 @@ func (r *HistoryResource) GetRevisions(context *restful.Context) {
 	limitStr := context.ReadQueryParameter(common.QueryParamLimit)
 	offset, limit, err := checkPagination(offsetStr, limitStr)
 	if err != nil {
-		WriteErrResponse(context, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		WriteErrResponse(context, http.StatusBadRequest, err.Error())
 		return
 	}
 	if kvID == "" {
 		openlogging.Error("kv id is nil")
-		WriteErrResponse(context, http.StatusForbidden, "kv_id must not be empty", common.ContentTypeText)
+		WriteErrResponse(context, http.StatusForbidden, "kv_id must not be empty")
 		return
 	}
 	revisions, err := service.HistoryService.GetHistory(context.Ctx, kvID,
@@ -58,10 +58,10 @@ func (r *HistoryResource) GetRevisions(context *restful.Context) {
 		service.WithLimit(limit))
 	if err != nil {
 		if err == service.ErrRevisionNotExist {
-			WriteErrResponse(context, http.StatusNotFound, err.Error(), common.ContentTypeText)
+			WriteErrResponse(context, http.StatusNotFound, err.Error())
 			return
 		}
-		WriteErrResponse(context, http.StatusInternalServerError, err.Error(), common.ContentTypeText)
+		WriteErrResponse(context, http.StatusInternalServerError, err.Error())
 		return
 	}
 	err = writeResponse(context, revisions)
@@ -91,17 +91,17 @@ func (r *HistoryResource) GetPollingData(context *restful.Context) {
 	}
 	domain := ReadDomain(context)
 	if domain == nil {
-		WriteErrResponse(context, http.StatusInternalServerError, common.MsgDomainMustNotBeEmpty, common.ContentTypeText)
+		WriteErrResponse(context, http.StatusInternalServerError, common.MsgDomainMustNotBeEmpty)
 		return
 	}
 	query.Domain = domain.(string)
 	records, err := track.Get(context.Ctx, query)
 	if err != nil {
 		if err == service.ErrRecordNotExists {
-			WriteErrResponse(context, http.StatusNotFound, err.Error(), common.ContentTypeText)
+			WriteErrResponse(context, http.StatusNotFound, err.Error())
 			return
 		}
-		WriteErrResponse(context, http.StatusInternalServerError, err.Error(), common.ContentTypeText)
+		WriteErrResponse(context, http.StatusInternalServerError, err.Error())
 		return
 	}
 	resp := &model.PollingDataResponse{}
@@ -119,7 +119,7 @@ func (r *HistoryResource) HealthCheck(context *restful.Context) {
 	resp := &model.DocHealthCheck{}
 	latest, err := service.RevisionService.GetRevision(context.Ctx, domain.(string))
 	if err != nil {
-		WriteErrResponse(context, http.StatusInternalServerError, err.Error(), common.ContentTypeText)
+		WriteErrResponse(context, http.StatusInternalServerError, err.Error())
 		return
 	}
 	resp.Revision = strconv.FormatInt(latest, 10)
diff --git a/server/resource/v1/kv_resource.go b/server/resource/v1/kv_resource.go
index df93981..ad6628c 100644
--- a/server/resource/v1/kv_resource.go
+++ b/server/resource/v1/kv_resource.go
@@ -25,6 +25,7 @@ import (
 
 	"github.com/apache/servicecomb-kie/pkg/common"
 	"github.com/apache/servicecomb-kie/pkg/model"
+	"github.com/apache/servicecomb-kie/pkg/validate"
 	"github.com/apache/servicecomb-kie/server/pubsub"
 	"github.com/apache/servicecomb-kie/server/service"
 	"github.com/apache/servicecomb-kie/server/service/mongo/session"
@@ -43,25 +44,25 @@ func (r *KVResource) Post(rctx *restful.Context) {
 	project := rctx.ReadPathParameter(common.PathParameterProject)
 	kv := new(model.KVDoc)
 	if err = readRequest(rctx, kv); err != nil {
-		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 		return
 	}
 	domain := ReadDomain(rctx)
 	kv.Domain = domain.(string)
 	kv.Project = project
-	err = validatePost(kv)
+	err = validate.Validate(kv)
 	if err != nil {
-		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 		return
 	}
 	kv, err = service.KVService.Create(rctx.Ctx, kv)
 	if err != nil {
 		openlogging.Error(fmt.Sprintf("post err:%s", err.Error()))
 		if err == session.ErrKVAlreadyExists {
-			WriteErrResponse(rctx, http.StatusConflict, err.Error(), common.ContentTypeText)
+			WriteErrResponse(rctx, http.StatusConflict, err.Error())
 			return
 		}
-		WriteErrResponse(rctx, http.StatusInternalServerError, "create kv failed", common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusInternalServerError, "create kv failed")
 		return
 	}
 	err = pubsub.Publish(&pubsub.KVChangeEvent{
@@ -90,7 +91,7 @@ func (r *KVResource) Put(rctx *restful.Context) {
 	project := rctx.ReadPathParameter(common.PathParameterProject)
 	kv := new(model.KVDoc)
 	if err = readRequest(rctx, kv); err != nil {
-		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 		return
 	}
 	domain := ReadDomain(rctx)
@@ -99,13 +100,13 @@ func (r *KVResource) Put(rctx *restful.Context) {
 	kv.Project = project
 	err = validatePut(kv)
 	if err != nil {
-		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 		return
 	}
 	kv, err = service.KVService.Update(rctx.Ctx, kv)
 	if err != nil {
 		openlogging.Error(fmt.Sprintf("put [%s] err:%s", kvID, err.Error()))
-		WriteErrResponse(rctx, http.StatusInternalServerError, "update kv failed", common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusInternalServerError, "update kv failed")
 		return
 	}
 	err = pubsub.Publish(&pubsub.KVChangeEvent{
@@ -134,17 +135,17 @@ func (r *KVResource) Get(rctx *restful.Context) {
 	kvID := rctx.ReadPathParameter(common.PathParamKVID)
 	err := validateGet(domain, project, kvID)
 	if err != nil {
-		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 		return
 	}
 	kv, err := service.KVService.Get(rctx.Ctx, domain, project, kvID)
 	if err != nil {
 		openlogging.Error("kv_resource: " + err.Error())
 		if err == service.ErrKeyNotExists {
-			WriteErrResponse(rctx, http.StatusNotFound, err.Error(), common.ContentTypeText)
+			WriteErrResponse(rctx, http.StatusNotFound, err.Error())
 			return
 		}
-		WriteErrResponse(rctx, http.StatusInternalServerError, "get kv failed", common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusInternalServerError, "get kv failed")
 		return
 	}
 	kv.Domain = ""
@@ -164,12 +165,12 @@ func (r *KVResource) List(rctx *restful.Context) {
 	domain := ReadDomain(rctx).(string)
 	err = validateList(domain, project)
 	if err != nil {
-		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 		return
 	}
 	labels, err := getLabels(rctx)
 	if err != nil {
-		WriteErrResponse(rctx, http.StatusBadRequest, common.MsgIllegalLabels, common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusBadRequest, common.MsgIllegalLabels)
 		return
 	}
 
@@ -177,14 +178,14 @@ func (r *KVResource) List(rctx *restful.Context) {
 	limitStr := rctx.ReadQueryParameter(common.QueryParamLimit)
 	offset, limit, err := checkPagination(offsetStr, limitStr)
 	if err != nil {
-		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 		return
 	}
 	sessionID := rctx.ReadHeader(HeaderSessionID)
 	statusStr := rctx.ReadQueryParameter(common.QueryParamStatus)
 	status, err := checkStatus(statusStr)
 	if err != nil {
-		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 		return
 	}
 	returnData(rctx, &model.KVDoc{
@@ -211,7 +212,7 @@ func returnData(rctx *restful.Context, doc *model.KVDoc, offset, limit int64, se
 			DomainID:  doc.Domain,
 		})
 		if err != nil {
-			WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+			WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 			return
 		}
 		if changed {
@@ -223,10 +224,10 @@ func returnData(rctx *restful.Context, doc *model.KVDoc, offset, limit int64, se
 		revised, err := isRevised(rctx.Ctx, revStr, doc.Domain)
 		if err != nil {
 			if err == ErrInvalidRev {
-				WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+				WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 				return
 			}
-			WriteErrResponse(rctx, http.StatusInternalServerError, err.Error(), common.ContentTypeText)
+			WriteErrResponse(rctx, http.StatusInternalServerError, err.Error())
 			return
 		}
 		if revised {
@@ -240,7 +241,7 @@ func returnData(rctx *restful.Context, doc *model.KVDoc, offset, limit int64, se
 				DomainID:  doc.Domain,
 			})
 			if err != nil {
-				WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+				WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 				return
 			}
 			if changed {
@@ -262,17 +263,17 @@ func (r *KVResource) Delete(rctx *restful.Context) {
 	kvID := rctx.ReadPathParameter(common.PathParamKVID)
 	err := validateDelete(domain, project, kvID)
 	if err != nil {
-		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusBadRequest, err.Error())
 		return
 	}
 	kv, err := service.KVService.Get(rctx.Ctx, domain, project, kvID)
 	if err != nil {
 		openlogging.Error("kv_resource: " + err.Error())
 		if err == service.ErrKeyNotExists {
-			WriteErrResponse(rctx, http.StatusNotFound, err.Error(), common.ContentTypeText)
+			WriteErrResponse(rctx, http.StatusNotFound, err.Error())
 			return
 		}
-		WriteErrResponse(rctx, http.StatusInternalServerError, common.MsgDeleteKVFailed, common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusInternalServerError, common.MsgDeleteKVFailed)
 		return
 	}
 	err = service.KVService.Delete(rctx.Ctx, kvID, domain, project)
@@ -281,7 +282,7 @@ func (r *KVResource) Delete(rctx *restful.Context) {
 			"kvID":  kvID,
 			"error": err.Error(),
 		}))
-		WriteErrResponse(rctx, http.StatusInternalServerError, common.MsgDeleteKVFailed, common.ContentTypeText)
+		WriteErrResponse(rctx, http.StatusInternalServerError, common.MsgDeleteKVFailed)
 		return
 	}
 	err = pubsub.Publish(&pubsub.KVChangeEvent{
diff --git a/server/resource/v1/kv_resource_test.go b/server/resource/v1/kv_resource_test.go
index 6b8f27e..d91443a 100644
--- a/server/resource/v1/kv_resource_test.go
+++ b/server/resource/v1/kv_resource_test.go
@@ -22,6 +22,7 @@ import (
 	"encoding/json"
 	common2 "github.com/apache/servicecomb-kie/pkg/common"
 	"github.com/apache/servicecomb-kie/pkg/model"
+	"github.com/apache/servicecomb-kie/pkg/validate"
 	"github.com/apache/servicecomb-kie/server/config"
 	handler2 "github.com/apache/servicecomb-kie/server/handler"
 	"github.com/apache/servicecomb-kie/server/pubsub"
@@ -44,6 +45,9 @@ import (
 )
 
 func init() {
+	if err := validate.Init(); err != nil {
+		panic(err)
+	}
 	log.Init(log.Config{
 		Writers:       []string{"stdout"},
 		LoggerLevel:   "DEBUG",
diff --git a/server/resource/v1/label_resouce.go b/server/resource/v1/label_resouce.go
index 49e42f7..d7d688d 100644
--- a/server/resource/v1/label_resouce.go
+++ b/server/resource/v1/label_resouce.go
@@ -20,27 +20,27 @@ func (r *LabelResource) PutLabel(context *restful.Context) {
 	var err error
 	entity := new(model.LabelDoc)
 	if err = readRequest(context, entity); err != nil {
-		WriteErrResponse(context, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		WriteErrResponse(context, http.StatusBadRequest, err.Error())
 		return
 	}
 	entity.Project = context.ReadPathParameter("project")
 	domain := ReadDomain(context)
 	if domain == nil {
-		WriteErrResponse(context, http.StatusInternalServerError, common.MsgDomainMustNotBeEmpty, common.ContentTypeText)
+		WriteErrResponse(context, http.StatusInternalServerError, common.MsgDomainMustNotBeEmpty)
 		return
 	}
 	entity.Domain = domain.(string)
 	res, err := service.LabelService.CreateOrUpdate(context.Ctx, entity)
 	if err != nil {
 		if err == service.ErrRevisionNotExist {
-			WriteErrResponse(context, http.StatusNotFound, err.Error(), common.ContentTypeText)
+			WriteErrResponse(context, http.StatusNotFound, err.Error())
 			return
 		}
-		WriteErrResponse(context, http.StatusInternalServerError, err.Error(), common.ContentTypeText)
+		WriteErrResponse(context, http.StatusInternalServerError, err.Error())
 		return
 	}
 	if res == nil {
-		WriteErrResponse(context, http.StatusNotFound, "put alias fail", common.ContentTypeText)
+		WriteErrResponse(context, http.StatusNotFound, "put alias fail")
 		return
 	}
 	if entity.ID == "" {