Add a controller

Before we adding any logic to our application, first we need a controller and its route. Remember the hello controller? You have used it in the previous chapter.

        • hello.go
      • hello.go
        • hello_new.go
        • hello_v1_hello.go
        • hello.go
  • The route is registered in internal/cmd/cmd.go:

    s.Group("/", func(group *ghttp.RouterGroup) {
        group.Middleware(ghttp.MiddlewareHandlerResponse)
    	group.Bind(
    	    hello.NewV1(),
    	)
    })

    This method using here is called Standardized Routing in GoFrame, which is easy to use and could be combined with code generation command gf gen ctrl. The API documentation would also be generated automatically, see Swagger page hello.

    We recommend to use Standardized Routing since it would be better to maintain and extend. You could also find other ways of routing here.

    Create API structure

    To use the code generation command gf gen ctrl, you need to declare the API structure first. GoFrame require certain file structure to generate controller, which is api/{model}/{version}/{definition}.go. And the API structure name is also require to end with Req and Res. Let’s create a new API in RESTful way for our message:

    api/message/v1/store.go
    package v1
    
    import "github.com/gogf/gf/v2/frame/g"
    
    type CreateMessageReq struct {
    	g.Meta  `path:"/" tags:"Message" method:"post" sum:"Create a message"`
    	UserUid string `json:"user_uid" v:"required|size:10" des:"Message sender ID" eg:"0000000000"`
    	Content string `json:"content" v:"required|length:1,100" des:"Message content" eg:"This is my first message."`
    }
    
    type CreateMessageRes struct {
    	g.Meta  `mime:"application/json"`
    	Code    int         `json:"code" v:"required" des:"Status code" eg:"0"`
    	Message string      `json:"message" v:"required" des:"Status message" eg:"Success"`
    	Data    interface{} `json:"data"`
    }

    The g.Meta here is used to add additional attributes to Request or Response. For this request, path is used to define the route, tags is used to set group in OpenAPI documentation, method is used to declare the accept HTTP method and sum is used to add summary to the route. And for the response, mime is just used to set the response content type.

    Tags in structure could not only be used to generate OpenAPI documentation, but also to validate data or transform data. For example, v tag could be used to validate data in request. For our request here, the UserUID is set to be required and the length should be 10, similar for other fields. Documentation for validation here.

    Those tags seems a little confusing, but don’t worry, you will see their function clearly soon after we register the route. Or you could also find more details for gtag here.

    Generate Controller

    Now we could use gf gen ctrl to generate controller with those structures declared before:

    & gf gen ctrl
    generated: ...\api\hello\hello.go
    generated: ...\api\message\message.go
    generated: internal\controller\message\message.go
    generated: internal\controller\message\message_new.go
    generated: internal\controller\message\message_v1_create_message.go
    done!

    And you will see its structure is similar to the hello controller auto-generated before.

    ℹ️
    To avoid running this command every time after changing some code in api directory, you could use some auto-run plugin, like Run on Save in VSCode.

    Open the internal\controller\message\message_v1_create_message.go, you will see the implementation of controller here:

    func (c *ControllerV1) CreateMessage(ctx context.Context, req *v1.CreateMessageReq) (res *v1.CreateMessageRes, err error) {
    	return nil, gerror.NewCode(gcode.CodeNotImplemented)
    }

    The response is not implemented yet, we may add some code later.

    Register route

    After generate controller, we need to register the route. Open the cmd.go in internal\cmd folder, the hello we used before has been registered here:

    internal/cmd/cmd.go
    s.Group("/", func(group *ghttp.RouterGroup) {
    	group.Middleware(ghttp.MiddlewareHandlerResponse)
    	group.Bind(
    		hello.NewV1(),
    	)
    })

    We could add the message similarly:

    internal/cmd/cmd.go
    s.Group("/messages", func(group *ghttp.RouterGroup) {
    	group.Middleware(ghttp.MiddlewareHandlerResponse)
    	group.Bind(
    		message.NewV1(),
    	)
    })

    Since the path we set before is /, we need to add messages to the route group when registering. You could also set the path in api structure to /messages, and you can ignore the messages here.

    route used for controller if api path is set to /messages
    internal/cmd/cmd.go
    s.Group("/", func(group *ghttp.RouterGroup) {
    	group.Middleware(ghttp.MiddlewareHandlerResponse)
    	group.Bind(
    		hello.NewV1(),
    		message.NewV1(),
    	)
    })

    Test your result

    Good job! You have successfully created your first API and registered the route. Check it in Swagger here.

    If you have used tools like Postman, you could test the API with the following request:

    POST http://localhost:8000/messages

    {
      "user_uid": "0000000000",
      "content": "This is my first message."
    }

    Or use curl to test the API:

    curl -X POST -H "Content-Type: application/json" -d '{"user_uid":"0000000000","content":"This is my first message."}' "http://localhost:8000/messages"

    You will see the Not Implemented response like this:

    {
      "code": 58,
      "message": "Not Implemented",
      "data": null
    }