In this chapter, we’ll implement a standardized response format for our API endpoints. All responses, whether successful or not, will be returned in a consistent JSON format.

Response Structure

Let’s define our standard response structure:

  1. type Response struct {
  2. Message string `json:"message" dc:"Response message"`
  3. Data interface{} `json:"data" dc:"Response payload"`
  4. }

We’ve added json tags to specify how each field should be serialized in the JSON response.

API Contracts

  1. type HelloReq struct {
  2. g.Meta `path:"/" method:"get"`
  3. Name string `v:"required" json:"name" dc:"User's name"`
  4. Age int `v:"required" json:"age" dc:"User's age"`
  5. }
  6. type HelloRes struct {
  7. Content string `json:"content" dc:"Response content"`
  8. }
  • The HelloRes struct defines the shape of our successful response data
  • All fields include json tags for proper serialization

Handler Implementation

  1. type Hello struct{}
  2. func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) {
  3. res = &HelloRes{
  4. Content: fmt.Sprintf(
  5. "Hello %s! Your Age is %d",
  6. req.Name,
  7. req.Age,
  8. ),
  9. }
  10. return
  11. }

Instead of directly writing to the response as in previous examples, we now return a structured response using HelloRes.

Response Middleware

  1. func Middleware(r *ghttp.Request) {
  2. r.Middleware.Next()
  3. var (
  4. msg string
  5. res = r.GetHandlerResponse()
  6. err = r.GetError()
  7. )
  8. if err != nil {
  9. msg = err.Error()
  10. } else {
  11. msg = "OK"
  12. }
  13. r.Response.WriteJson(Response{
  14. Message: msg,
  15. Data: res,
  16. })
  17. }

This middleware:

  • Gets the handler’s response using r.GetHandlerResponse() (this is the *HelloRes returned by our handler)
  • Checks for errors using r.GetError() (the error value returned by our handler)
  • Wraps everything in our standard response structure and sends it as JSON

Complete Example

main.go

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/gogf/gf/v2/frame/g"
  6. "github.com/gogf/gf/v2/net/ghttp"
  7. )
  8. type Response struct {
  9. Message string `json:"message" dc:"Response message"`
  10. Data interface{} `json:"data" dc:"Response payload"`
  11. }
  12. type HelloReq struct {
  13. g.Meta `path:"/" method:"get"`
  14. Name string `v:"required" json:"name" dc:"User's name"`
  15. Age int `v:"required" json:"age" dc:"User's age"`
  16. }
  17. type HelloRes struct {
  18. Content string `json:"content" dc:"Response content"`
  19. }
  20. type Hello struct{}
  21. func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) {
  22. res = &HelloRes{
  23. Content: fmt.Sprintf(
  24. "Hello %s! Your Age is %d",
  25. req.Name,
  26. req.Age,
  27. ),
  28. }
  29. return
  30. }
  31. func Middleware(r *ghttp.Request) {
  32. r.Middleware.Next()
  33. var (
  34. msg string
  35. res = r.GetHandlerResponse()
  36. err = r.GetError()
  37. )
  38. if err != nil {
  39. msg = err.Error()
  40. } else {
  41. msg = "OK"
  42. }
  43. r.Response.WriteJson(Response{
  44. Message: msg,
  45. Data: res,
  46. })
  47. }
  48. func main() {
  49. s := g.Server()
  50. s.Group("/", func(group *ghttp.RouterGroup) {
  51. group.Middleware(Middleware)
  52. group.Bind(
  53. new(Hello),
  54. )
  55. })
  56. s.SetPort(8000)
  57. s.Run()
  58. }

Testing the API

Let’s test with valid parameters at http://127.0.0.1:8000/?name=john&age=18:

img_3.png

And with missing parameters at http://127.0.0.1:8000/:

img_5.png

Looking Ahead

We’ve successfully implemented a standardized JSON response format, which is crucial for maintaining consistency across large APIs.

Notice how we’ve structured everything - from input parameters to response formats - with clear types, descriptions, and validation rules. This structured approach opens up interesting possibilities: could we automatically generate API documentation from these definitions? Indeed we can, and that’s exactly what we’ll explore in the next chapter.