Params

package revel

// Params provides a unified view of the request params.
// Includes:
// - URL query string
// - Form values
// - File uploads
//
// Warning: param maps other than Values may be nil if there were none.
type Params struct {
    url.Values // A unified view of all the individual param maps below.

    // Set by the router
    Fixed url.Values // Fixed parameters from the route, e.g. App.Action("fixed param")
    Route url.Values // Parameters extracted from the route,  e.g. /customers/{id}

    // Set by the ParamsFilter
    Query url.Values // Parameters from the query string, e.g. /index?limit=10
    Form  url.Values // Parameters from the request body.

    Files map[string][]*multipart.FileHeader // Files uploaded in a multipart form

}

func ParseParams(params *Params, req *Request) {
    params.Query = req.URL.Query()

    // Parse the body depending on the content type.
    switch req.ContentType {
    case "application/x-www-form-urlencoded":
        // Typical form.
        if err := req.ParseForm(); err != nil {
            WARN.Println("Error parsing request body:", err)
        } else {
            params.Form = req.Form
        }

    case "multipart/form-data":
        // Multipart form.
        // TODO: Extract the multipart form param so app can set it.
        if err := req.ParseMultipartForm(32 << 20 /* 32 MB */); err != nil {
            WARN.Println("Error parsing request body:", err)
        } else {
            params.Form = req.MultipartForm.Value
            params.Files = req.MultipartForm.File
        }
    }

    params.Values = params.calcValues()
}

// Bind looks for the named parameter, converts it to the requested type, and
// writes it into "dest", which must be settable.  If the value can not be
// parsed, "dest" is set to the zero value.
func (p *Params) Bind(dest interface{}, name string) {
    value := reflect.ValueOf(dest)
    if value.Kind() != reflect.Ptr {
        panic("revel/params: non-pointer passed to Bind: " + name)
    }
    value = value.Elem()
    if !value.CanSet() {
        panic("revel/params: non-settable variable passed to Bind: " + name)
    }
    value.Set(Bind(p, name, value.Type()))
}

func ParamsFilter(c *Controller, fc []Filter) {
    ParseParams(c.Params, c.Request)

    // Clean up from the request.
    defer func() {
        // Delete temp files.
        if c.Request.MultipartForm != nil {
            err := c.Request.MultipartForm.RemoveAll()
            if err != nil {
                WARN.Println("Error removing temporary files:", err)
            }
        }

        for _, tmpFile := range c.Params.tmpFiles {
            err := os.Remove(tmpFile.Name())
            if err != nil {
                WARN.Println("Could not remove upload temp file:", err)
            }
        }
    }()

    fc[0](c, fc[1:])
}