Skip to content

Commit

Permalink
Added detailed method parameters documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
eyalpost authored May 17, 2017
1 parent 754ea79 commit cbc3fc5
Showing 1 changed file with 111 additions and 0 deletions.
111 changes: 111 additions & 0 deletions en-US/mvc/controller/params.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,117 @@ Notes:
* While calling the method ParseForm of the Controller, the parameter passed in must be a pointer to a struct, otherwise the assignment will fail and it will return a `xx must be a struct pointer` error.
* If you want to ignore some fields, there are two ways: the first is using lowercase for that field; the second is to use `-` as the value of the tag.

## Automatic Parameter Routing

Automatic parameter routing removes the need for boilerplate code like this.GetString(..), this.GetInt(..) etc. and instead, injects https parameters directly as method parameters and renders the method return values as http responses. This works in conjunction with annotations to create a seamless integration.

### How does it work?

Start by defining a regular controller method with a `@router` annotation and add parameters to the method signature
```go
// @router /tasks
func (c *TaskController) MyMethod(id int) {
...
}
```

When an http request comes in that matches the routing you defined, Beego will scan the parameters in the method signature and will try to find matching http request paramters, convert them to the correct parameter type and pass them to your method. By default, Beego will look for parameters in the quey string (when using `GET`) or form data (when using `POST`). If your routing definition contains parameters, then Beego will automatically search for them in the path:
```go
// @router /task/:id
func (c *TaskController) MyMethod(id int) {
...
}
```

You can also use annotations to indicate a parameter is passed in a header or in the request body and bego will search for it accordingly.
If a parameter is not found in the http request, it will be passed to your controller method as a zero value (i.e. 0 for int, false for bool etc.). If you define a default value for that parameter in annotations, beego will pass that default value if it is missing. If you want to differentiate between missing parameters and default values, you can define the parameter as a pointer, e.g.:
```go
// @router /tasks
func (c *TaskController) MyMethod(id *int) {
...
}
```
In the above case, if the paramter was missing, `id` will be null but if it exists and equals to zero, `id` will be 0. If you are using annotations to create swagger documentation, you can mark a parameter as `required`. In this case, if the parameter is missing in the request, a `400 Bad Request` error will be returned to the client:
```go
// @Param id query int true "task id"
// @router /tasks
func (c *TaskController) MyMethod(id *int) {
...
}
```

Also, If a Beego can not convert the parameter to the requested type (i.e. if you pass a string that can not be parsed as an integer), an error will be returned to the client.

The following table shows which types are supported and how they are parsed:

Data Type | Location | Example | Comment
----------|----------|---------|----------
int, int64, uint etc. | anywhere | "1","-100" | Uses `strconv.Atoi(value)`
float32,float64 | anywhere | "1.5", "-3.5" | Uses `strconv.ParseFloat()`
bool | anywhere | "1", "T", "false" | Uses `strconv.ParseBool()`
time.Time | anywhere | "2017-01-01" "2017-01-01T00:00:00Z" | Uses RFC3339 or short date format (`"2006-01-02"`) when parsing
[]string, []int etc. | query | "A,B,C" "1,2,3" | Any type is supported as a slice. When it is located in the query string, it is parsed as a command separated list
[]string, []int etc. | body | \["A","B","C"] [1,2,3] | When slices are located in the request body they are parsed as JSON arrays
[]byte | anywhere | "ABC" | byte[] is not treated as an array but as a string
\*int, \*string, \*float etc. | anywhere | Pointers will receive null if the parameter is missing from the request otherwise, it will behave the same as defined in the other rows
structs, all others | anywhere | {"X":"Y"} | structs and other types (e.g. maps) are always parsed as JSON using `json.Unmarshal()`

### How are method return values handled?
The same way parameters are handled automatically, a method return value is also handled automatically. A method can have one or more return values and Beego will render all of them to the response. The best practice is to define one result as a 'regular' type (i.e. a map, a struct or any other data type) and another error data type (Same as the best practice for regular go methods):

```go
// @Param id query int true "task id"
// @router /tasks
func (c *TaskController) MyMethod(id *int) (*MyModel, error) {
...
}
```

In the code above, the method can return three different results:
- `MyModel` with a nil `error`
- nil `MyModel` with a non-nil `error`
- `MyModel` and a non-nil `error`

When a regular type is returned, it is rendered to the response directly as JSON. When an error is returned it is rendered as an http status code. Altough the first two options are more common, Beego will handle all cases correctrly and supports returning both response body and http error if both values are non-nil.
A few helper types allow you toy return common http status codes easily. For example, you can return `404 Not Found`, `302 Redirect` or other http status codes like in the following example:
```go
func (c *TaskController) MyMethod(id *int) (*MyModel, error) {
if /* not found */ {
return nil, httpResponse.NotFound
} else if /* some error */ {
return nil, httpResponse.StatusCode(401)
} else /* redirect */ {
return nil, httpResponse.Redirect("/login")
}
}
```
### How annotations work in conjuction with method parameters?

Automatic Parameter Routing works best together with `@Param` annotations. The following features are supported with annotations:
- If a parameter is marked as required, Beego will return an error if the parameter is not present in the http request:
`// @Param brand_id query int true "brand id"`
(the `true` option in the annotation above indicates that brand_id is a required parameter)
- If a parameter has a default value and it does not exist in the http request, Beego will pass that default value to the method:
`// @Param brand_id query int false 5 "brand id"`
(the `5` in the annotation above indicates that this is the default value for that parameter)
- The location parameter in the annotation indicates where beego will search for that parameter in the request (i.e. query, header, body etc.)
```go
// @Param brand_id path int true "brand id"
// @Param category query string false "category"
// @Param token header string false "auth token"
// @Param task body {models.Task} false "the task object"
```
- If a parameter name in the http request is different from the method parameter name, you can "redirect" the parameter using the `=>` notation. This is useful for example, if a header name is `X-Token` and the method parameter is named x_token:
`// @Param X-Token=>x_token header string false "auth token"`
- A parameterswagger data type can be inferred from the method to make maintainance easier. Just use the `auto` data type to let bee generate the correct swagger documentation:
```go
// @Param id query auto true "task id"
// @router /tasks
func (c *TaskController) MyMethod(id int) (*MyModel, error) {
...
}
```

## Retrieving data from request body

In API application development, we always use `JSON` or `XML` as the data type. So how can we retrieve the data from the request body?
Expand Down

0 comments on commit cbc3fc5

Please sign in to comment.