wI2L / fizz

:lemon: Gin wrapper with OpenAPI 3 spec generation
https://pkg.go.dev/github.com/wI2L/fizz
MIT License
214 stars 52 forks source link

Params and Body #42

Closed hmajid2301 closed 3 years ago

hmajid2301 commented 3 years ago

Is it possible to have parameters and the body passed to a function?

For example let's say my endpoint is POST /game/{name}/question.

Where the defintion looks like:

fizzApp.Group("/game", "game", "Related to managing the game types.")
// ...

func route(grp *fizz.RouterGroup) {
        / ...
    grp.POST("/:name/question", []fizz.OperationOption{
        fizz.Summary("Add a new question to a game type."),
    }, tonic.Handler(controllers.AddQuestion, http.StatusOK))
}

and the function definition looks something like:

func AddQuestion(_ *gin.Context, params *models.GameParams, question *models.NewQuestion) (struct{}, error)

However I get the following error message:

 incorrect number of input parameters for handler ontrollers.AddQuestion expected 1 or 2, got 3

Is there another way to get the body and the parameters passed to a function ? Thanks

wI2L commented 3 years ago

Hello.

This is not related to this library, but to loopfz/tonic, which define the tonic.Handler type. See https://github.com/loopfz/gadgeto/blob/master/tonic/README for reference.

What you should do is relying on a single type for your input parameter in the handler. You may use embedded structs in this case, to split common field in their own struct type.

edit: btw, tonic will handle the binding of the query params, headers AND body in the same input type.

hmajid2301 commented 3 years ago

I'm pretty new to golang would you mind showing me an example ?

wI2L commented 3 years ago

Sure. So, according yo what you said, your route /game/:name/question should be represented with an input type like the following, to use in the handler's definition. This type defines the parameters of the body, but it also "embed" the game parameters through the type GameParams.

type NewQuestionIn struct {
    GameParams
    Field1 string `json:"field1"`
    Field2 string `json:"field2"`
}

type GameParams struct {
    Name string `path:"name"`
    APIKey string `header:"X-Api-Key"` // for example, if you need to bind headers
}

The handler should then have the following prototype:

func AddQuestion(c *gin.Context, in *NewQuestionIn) ("return type", error)

I imagine that you may have aQuestion model type, like so:

type Question struct {
    ...
}

If so, you may reuse it. Embed it in the input type used in the handler:

type NewQuestionIn struct {
    GameParams
    Question
}

And the handler becomes:

func AddQuestion(c *gin.Context, in *NewQuestionIn) (*Question, error)

Here i assume that the handler should return a Question object since that's the behavior of CREATE methods in traditional CRUD APIs.

wI2L commented 3 years ago

I suggest that you take a look at the example. It doesn't show types composition, but it's a good start.

Closing, since there's no issue with the library itself, but feel free to ask if you need other clarifications.