├── go.mod ├── .travis.yml ├── go.sum ├── LICENSE ├── jsend_test.go ├── README.md └── jsend.go /go.mod: -------------------------------------------------------------------------------- 1 | module clevergo.tech/jsend 2 | 3 | go 1.13 4 | 5 | require github.com/stretchr/testify v1.5.1 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | language: go 4 | go: 5 | - 1.13.x 6 | - 1.14.x 7 | - 1.15.x 8 | - master 9 | jobs: 10 | allow_failures: 11 | - go: master 12 | fast_finish: true 13 | before_install: 14 | - go get github.com/mattn/goveralls 15 | script: 16 | - go test -v -covermode=count -coverprofile=coverage.out 17 | - go vet ./... 18 | - test -z "$(gofmt -d -s . | tee /dev/stderr)" 19 | - $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci 20 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 8 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 9 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 10 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 11 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 12 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 CleverGo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /jsend_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 CleverGo. All rights reserved. 2 | // Use of this source code is governed by a MIT style license that can be found 3 | // in the LICENSE file. 4 | 5 | package jsend 6 | 7 | import ( 8 | "encoding/json" 9 | "net/http" 10 | "net/http/httptest" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestWrite(t *testing.T) { 17 | tests := []struct { 18 | status int 19 | body Body 20 | }{ 21 | { 22 | body: Body{Status: StatusError, Message: "error"}, 23 | }, 24 | { 25 | body: Body{Status: StatusError, Message: "error", Code: 10000}, 26 | }, 27 | { 28 | body: Body{Status: StatusError, Message: "error", Code: 10001, Data: "error data"}, 29 | }, 30 | { 31 | status: http.StatusInternalServerError, 32 | body: Body{Status: StatusError, Message: "error"}, 33 | }, 34 | { 35 | body: Body{Status: StatusFail, Data: "fail"}, 36 | }, 37 | { 38 | status: http.StatusForbidden, 39 | body: Body{Status: StatusFail, Data: "fail"}, 40 | }, 41 | { 42 | body: Body{Status: StatusSuccess, Data: "success"}, 43 | }, 44 | } 45 | 46 | contentType := "application/json" 47 | for _, test := range tests { 48 | response := httptest.NewRecorder() 49 | var err error 50 | var statuses []int 51 | if test.status != 0 { 52 | statuses = append(statuses, test.status) 53 | } 54 | if test.body.Status == StatusSuccess { 55 | err = Success(response, test.body.Data, statuses...) 56 | } else if test.body.Status == StatusFail { 57 | err = Fail(response, test.body.Data, statuses...) 58 | } else { 59 | if test.body.Data != nil { 60 | err = ErrorCodeData(response, test.body.Message, test.body.Code, test.body.Data, statuses...) 61 | } else if test.body.Code != 0 { 62 | err = ErrorCode(response, test.body.Message, test.body.Code, statuses...) 63 | } else { 64 | err = Error(response, test.body.Message, statuses...) 65 | } 66 | } 67 | if err != nil { 68 | t.Fatal(err) 69 | } 70 | 71 | actualContentType := response.Header().Get("Content-Type") 72 | assert.Equal(t, contentType, actualContentType) 73 | if test.status != 0 { 74 | assert.Equal(t, test.status, response.Result().StatusCode) 75 | } 76 | var actualBody Body 77 | if err = json.Unmarshal(response.Body.Bytes(), &actualBody); err != nil { 78 | t.Fatal(err) 79 | } 80 | assert.Equal(t, test.body, actualBody) 81 | } 82 | } 83 | 84 | func TestWriteError(t *testing.T) { 85 | w := httptest.NewRecorder() 86 | err := Write(w, NewError("msg", 0, make(chan int))) 87 | assert.NotNil(t, err) 88 | } 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSend's implementation writen in Go(golang) 2 | [![Build Status](https://img.shields.io/travis/clevergo/jsend?style=flat-square)](https://travis-ci.org/clevergo/jsend) 3 | [![Coverage Status](https://img.shields.io/coveralls/github/clevergo/jsend?style=flat-square)](https://coveralls.io/github/clevergo/jsend?branch=master) 4 | [![Go.Dev reference](https://img.shields.io/badge/go.dev-reference-blue?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/clevergo.tech/jsend?tab=doc) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/clevergo/jsend?style=flat-square)](https://goreportcard.com/report/github.com/clevergo/jsend) 6 | [![Release](https://img.shields.io/github/release/clevergo/jsend.svg?style=flat-square)](https://github.com/clevergo/jsend/releases) 7 | [![Downloads](https://img.shields.io/endpoint?url=https://pkg.clevergo.tech/api/badges/downloads/total/clevergo.tech/jsend&style=flat-square)](https://pkg.clevergo.tech/clevergo.tech/jsend) 8 | [![Chat](https://img.shields.io/badge/chat-telegram-blue?style=flat-square)](https://t.me/clevergotech) 9 | [![Community](https://img.shields.io/badge/community-forum-blue?style=flat-square&color=orange)](https://forum.clevergo.tech) 10 | 11 | This package is an implementation of [JSend](https://github.com/omniti-labs/jsend) specification written in Go(golang). 12 | 13 | ## Installation 14 | 15 | ```shell 16 | go get clevergo.tech/jsend 17 | ``` 18 | 19 | ## Usage 20 | 21 | Usage is pretty simple. 22 | 23 | ```go 24 | // success response 25 | jsend.Success(w, data) 26 | // fail response 27 | jsend.Fail(w, data) 28 | // error response 29 | jsend.Error(w, message) 30 | // error response with extra code 31 | jsend.ErrorCode(w, message, code) 32 | // error response with extra code and data 33 | jsend.ErrorCodeData(w, message, code, data) 34 | ``` 35 | 36 | It can also be integrated with web framework, such as Gin, Echo, CleverGo: 37 | 38 | ```go 39 | // success response 40 | ctx.JSON(http.StatusOK, jsend.New(data)) 41 | // fail response 42 | ctx.JSON(http.StatusOK, jsend.NewFail(data)) 43 | // error response 44 | ctx.JSON(http.StatusOK, jsend.NewError(message, code, data)) 45 | ``` 46 | 47 | Checkout [example](https://github.com/clevergo/examples/tree/master/jsend) for details. 48 | 49 | ### Error Handling 50 | 51 | It is application responsibility to handle error. 52 | 53 | ### Status Code 54 | 55 | By default status code `http.StatusOK` was used implicitly, 56 | it can also be specified by the last parameter if necessary. 57 | 58 | ```go 59 | jsend.Success(w, data, http.StatusOK) 60 | jsend.Fail(w, data, http.StatusForbidden) 61 | jsend.Error(w, message, http.StatusInternalServerError) 62 | ``` 63 | -------------------------------------------------------------------------------- /jsend.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 CleverGo. All rights reserved. 2 | // Use of this source code is governed by a MIT style license that can be found 3 | // in the LICENSE file. 4 | 5 | package jsend 6 | 7 | import ( 8 | "encoding/json" 9 | "net/http" 10 | ) 11 | 12 | // Status constants 13 | const ( 14 | StatusError = "error" 15 | StatusFail = "fail" 16 | StatusSuccess = "success" 17 | ) 18 | 19 | // Body contains 20 | type Body struct { 21 | // The status indicates the execution result of request, 22 | // it can be one of "success", "fail" and "error". 23 | Status string `json:"status"` 24 | Data interface{} `json:"data,omitempty"` 25 | Message string `json:"message,omitempty"` 26 | Code int `json:"code,omitempty"` 27 | } 28 | 29 | // New returns a success body with the given data. 30 | func New(data interface{}) Body { 31 | return Body{ 32 | Status: StatusSuccess, 33 | Data: data, 34 | } 35 | } 36 | 37 | // NewFail returns a fail body with the given data. 38 | func NewFail(data interface{}) Body { 39 | return Body{ 40 | Status: StatusFail, 41 | Data: data, 42 | } 43 | } 44 | 45 | // NewError returns a error body with given message. 46 | func NewError(message string, code int, data interface{}) Body { 47 | return Body{ 48 | Status: StatusError, 49 | Message: message, 50 | Code: code, 51 | Data: data, 52 | } 53 | } 54 | 55 | // Error writes error body with the given message. 56 | func Error(w http.ResponseWriter, message string, statuses ...int) error { 57 | return Write(w, NewError(message, 0, nil), statuses...) 58 | } 59 | 60 | // ErrorCode writes error body with the given message and code. 61 | func ErrorCode(w http.ResponseWriter, message string, code int, statuses ...int) error { 62 | return Write(w, NewError(message, code, nil), statuses...) 63 | } 64 | 65 | // ErrorCodeData writes error body with the given message, code and data. 66 | func ErrorCodeData(w http.ResponseWriter, message string, code int, data interface{}, statuses ...int) error { 67 | return Write(w, NewError(message, code, data), statuses...) 68 | } 69 | 70 | // Fail writes failed body with the given data. 71 | func Fail(w http.ResponseWriter, data interface{}, statuses ...int) error { 72 | return Write(w, NewFail(data), statuses...) 73 | } 74 | 75 | // Success writes successful body with the given data. 76 | func Success(w http.ResponseWriter, data interface{}, statuses ...int) error { 77 | return Write(w, New(data), statuses...) 78 | } 79 | 80 | // Write writes the body to http.ResponseWriter. 81 | // 82 | // If necessary, the status code can be specified through the third parameter. 83 | func Write(w http.ResponseWriter, body Body, statuses ...int) error { 84 | w.Header().Set("Content-Type", "application/json") 85 | 86 | if len(statuses) > 0 { 87 | w.WriteHeader(statuses[0]) 88 | } 89 | 90 | b, err := json.Marshal(body) 91 | if err != nil { 92 | return err 93 | } 94 | _, err = w.Write(b) 95 | return err 96 | } 97 | --------------------------------------------------------------------------------