Skip to main content
Version: 2.8.x(Latest)

I. Overview

In RESTful API design, different status codes represent different response situations, such as 200 for success, 201 for successful creation, 400 for request error, 401 for unauthorized, etc. In GoFrame's OpenAPIv3 API documentation, only the successful status (usually 200) response documentation is generated by default.

However, in practical applications, we often need to provide multiple possible response statuses for an API, such as error handling, authentication failures, etc. To meet this need, GoFrame provides the IEnhanceResponseStatus interface in the goai component, allowing developers to extend response structure information by implementing this interface and adding response documentation for multiple status codes.

II. Interface Definition

The relevant definitions of the IEnhanceResponseStatus interface are as follows:

type EnhancedStatusCode = int

type EnhancedStatusType struct {
Response any
Examples any
}

type IEnhanceResponseStatus interface {
EnhanceResponseStatus() map[EnhancedStatusCode]EnhancedStatusType
}

III. Interface Field Description

  • EnhancedStatusCode: Represents HTTP status codes, such as 200, 201, 400, 401, 403, 404, 500, etc.

  • EnhancedStatusType: Contains two fields:

    • Response: Response structure, can be any type (any). You can add documentation information with g.Meta tags, and if you set the mime tag, this structure will override the content of the common response structure.
    • Examples: Response examples, can be any type (any). You can use error code lists to automatically generate example content and display it in the documentation, ensuring synchronization between documentation content and actual business content.
  • IEnhanceResponseStatus: Interface definition, containing a method EnhanceResponseStatus() that returns a mapping from HTTP status codes to corresponding EnhancedStatusType types.

IV. Usage Method

To use the IEnhanceResponseStatus interface, you need to implement the EnhanceResponseStatus() method in the response structure. This method returns a mapping from HTTP status codes to corresponding response structures and examples.

1. Implementing the Interface

Implement the EnhanceResponseStatus() method in the response structure, returning response structures and examples corresponding to different status codes.

2. Setting Response Status Codes

You can use the status attribute in the g.Meta tag of the response structure to set the default response status code, for example g.Meta status:"201"`.

3. Using Example Data

You can return example data in the EnhanceResponseStatus() method, which will be displayed in the generated OpenAPIv3 documentation.

V. Complete Example

Below is a complete example demonstrating how to use the IEnhanceResponseStatus interface to extend response structure information:

package main

import (
"context"

"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/net/goai"
)

type StoreMessageReq struct {
g.Meta `path:"/messages" method:"post" summary:"Store a message"`
Content string `json:"content"`
}
type StoreMessageRes struct {
g.Meta `status:"201"`
Id string `json:"id"`
}
type EmptyRes struct {
g.Meta `mime:"application/json"`
}

type CommonRes struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}

var StoreMessageErr = map[int]gcode.Code{
500: gcode.New(1, "Server Dead", nil),
}

func (r StoreMessageRes) EnhanceResponseStatus() (resList map[int]goai.EnhancedStatusType) {
examples := []interface{}{}
example500 := CommonRes{
Code: StoreMessageErr[500].Code(),
Message: StoreMessageErr[500].Message(),
Data: nil,
}
examples = append(examples, example500)
return map[int]goai.EnhancedStatusType{
403: {
Response: EmptyRes{},
},
500: {
Response: struct{}{},
Examples: examples,
},
}
}

type Controller struct{}

func (c *Controller) StoreMessage(ctx context.Context, req *StoreMessageReq) (res *StoreMessageRes, err error) {
return nil, gerror.NewCode(gcode.CodeNotImplemented)
}

func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Bind(new(Controller))
})
oai := s.GetOpenApi()
oai.Config.CommonResponse = CommonRes{}
oai.Config.CommonResponseDataField = `Data`
s.SetOpenApiPath("/api.json")
s.SetSwaggerPath("/swagger")
s.SetPort(8199)
s.Run()
}

After execution, visit http://127.0.0.1:8199/swagger to view the swagger ui, and visit http://127.0.0.1:8199/api.json to view the corresponding OpenAPIv3 API documentation. The generated OpenAPIv3 API documentation is as follows:

{
"openapi": "3.0.0",
"components": {
"schemas": {
"main.StoreMessageReq": {
"properties": {
"content": {
"format": "string",
"type": "string"
}
},
"type": "object"
},
"main.StoreMessageRes": {
"properties": {
"id": {
"format": "string",
"type": "string"
}
},
"type": "object"
},
"interface": {
"properties": {},
"type": "object"
},
"main.EmptyRes": {
"properties": {},
"type": "object"
},
"struct": {
"properties": {},
"type": "object"
}
}
},
"info": {
"title": "",
"version": ""
},
"paths": {
"/messages": {
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/main.StoreMessageReq"
}
}
}
},
"responses": {
"201": {
"content": {
"application/json": {
"schema": {
"properties": {
"code": {
"format": "int",
"type": "integer"
},
"message": {
"format": "string",
"type": "string"
},
"data": {
"properties": {
"id": {
"format": "string",
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
}
}
},
"description": ""
},
"403": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/main.EmptyRes"
}
}
},
"description": ""
},
"500": {
"content": {
"application/json": {
"schema": {
"properties": {
"code": {
"format": "int",
"type": "integer"
},
"message": {
"format": "string",
"type": "string"
},
"data": {
"properties": {},
"type": "object"
}
},
"type": "object"
},
"examples": {
"example 1": {
"value": {
"code": 1,
"message": "Server Dead",
"data": null
}
}
}
}
},
"description": ""
}
},
"summary": "Store a message"
}
}
}
}

You can see that the default response status code has been changed to 201, and response examples have also been automatically generated.

VI. Common Status Code Usage Scenarios

In practical applications, different HTTP status codes are used to represent different response situations. Here are some common status codes and their usage scenarios:

  • 200 OK: Request successful, returns the requested data.
  • 201 Created: Resource created successfully, typically used for POST requests.
  • 204 No Content: Request successful, but no content returned, typically used for DELETE requests.
  • 400 Bad Request: Request parameter error or incorrect format.
  • 401 Unauthorized: Unauthorized, requires authentication.
  • 403 Forbidden: Authorized, but does not have permission to access the resource.
  • 404 Not Found: The requested resource does not exist.
  • 500 Internal Server Error: Server internal error.

When implementing the IEnhanceResponseStatus interface, you can provide corresponding response structures and example data for these different status codes based on actual business requirements.

VII. Usage Recommendations

  1. Structure Reuse: For similar response structures, define common structures for reuse, reducing code duplication.

  2. Error Code Management: Centrally manage error codes, for example, using map or constants to define error codes corresponding to different status codes, facilitating maintenance and extension.

  3. Authenticity of Example Data: The provided example data should be as realistic as possible, reflecting actual business scenarios, making it easier for frontend developers to understand and handle.

  4. Documentation Synchronous Updates: When business logic or error codes change, update the API documentation promptly to maintain consistency between documentation and code.

VIII. Using Resource Files to Load Example Data

In practical applications, example data may be large or complex, and writing it directly in code may affect code readability. The goai component of GoFrame provides functionality to load example data from external files, supporting reading example data from the gres resource manager or local file system, with file formats including json/xml/yaml/ini and other formats supported by the gjson component.

1. Using the g.Meta Tag to Specify Example Files

You can use the resEg attribute in the g.Meta tag of the response structure to specify the path of the example file:

type MyResponse struct {
g.Meta `status:"201" resEg:"testdata/examples/201.json"`
// ...other fields
}

Where testdata/examples/201.json is the path of the example file, which can be a relative or absolute path.

2. Example File Format

Example files can be a JSON object or JSON array:

  • JSON Object: Each key will be used as the name of the example, and the value as the content of the example.

    {
    "example1": {
    "code": 0,
    "message": "Success",
    "data": { "id": 1 }
    },
    "example2": {
    "code": 1,
    "message": "Failed",
    "data": null
    }
    }
  • JSON Array: Each element in the array will be used as an example, with example names automatically generated as example 1, example 2, etc.

    [
    {
    "code": 0,
    "message": "Success",
    "data": { "id": 1 }
    },
    {
    "code": 1,
    "message": "Failed",
    "data": null
    }
    ]

3. Using the gres Resource Manager

The goai component of GoFrame will first try to read the specified path file from the gres resource manager, and if not found, will try to read from the local file system. This allows example data to be packaged into the program, facilitating distribution and deployment.

To use the gres resource manager, you need to first package the example files into resource files:

package main

import (
"github.com/gogf/gf/v2/os/gres"
"github.com/gogf/gf/v2/frame/g"
)

func main() {
// Package resource files
gres.Dump()

// Initialize server
s := g.Server()
// ...other configurations
s.Run()
}

4. Complete Example

Below is a complete example of using resource files to load example data:

package main

import (
"context"

"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)

type CreateUserReq struct {
g.Meta `path:"/users" method:"post" summary:"Create User"`
Name string `json:"name" v:"required" dc:"Username"`
Email string `json:"email" v:"required|email" dc:"Email Address"`
}

type CreateUserRes struct {
g.Meta `status:"201" resEg:"testdata/examples/user/create_success.json"`
Id int `json:"id" dc:"User ID"`
}

func (r CreateUserRes) EnhanceResponseStatus() map[int]goai.EnhancedStatusType {
return map[int]goai.EnhancedStatusType{
400: {
Response: struct{}{},
// Load error examples from file
},
500: {
Response: struct{}{},
// Load error examples from file
},
}
}

type Controller struct{}

func (c *Controller) CreateUser(ctx context.Context, req *CreateUserReq) (res *CreateUserRes, err error) {
// Actual business logic
return &CreateUserRes{Id: 1}, nil
}

func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Bind(new(Controller))
})
s.SetOpenApiPath("/api.json")
s.SetSwaggerPath("/swagger")
s.Run()
}

The content of the testdata/examples/user/create_success.json file might be:

{
"Success Example": {
"code": 0,
"message": "Created Successfully",
"data": {
"id": 1
}
}
}

This approach allows example data to be separated from code, making it easier to maintain and manage.

IX. Conclusion

The IEnhanceResponseStatus interface of GoFrame provides a flexible and powerful way to extend response information in OpenAPIv3 API documentation. By implementing this interface, developers can:

  1. Provide documentation for multiple response statuses for APIs, making the documentation more compliant with RESTful API design specifications.
  2. Customize response structures for different status codes, providing more precise interface definitions.
  3. Display response formats for different statuses through example data, making it easier for frontend developers to understand and handle.
  4. Maintain consistency between documentation and code, improving development efficiency and collaboration quality.

By properly using the IEnhanceResponseStatus interface, the quality and completeness of API documentation can be significantly improved, providing a better development experience for frontend and backend developers.