Add API Framework Revel Source Files
[iec.git] / src / foundation / api / revel / params.go
1 // Copyright (c) 2012-2017 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
4
5 package revel
6
7 import (
8         "encoding/json"
9         "errors"
10         "io/ioutil"
11         "mime/multipart"
12         "net/url"
13         "os"
14         "reflect"
15 )
16
17 // Params provides a unified view of the request params.
18 // Includes:
19 // - URL query string
20 // - Form values
21 // - File uploads
22 //
23 // Warning: param maps other than Values may be nil if there were none.
24 type Params struct {
25         url.Values // A unified view of all the individual param maps below.
26
27         // Set by the router
28         Fixed url.Values // Fixed parameters from the route, e.g. App.Action("fixed param")
29         Route url.Values // Parameters extracted from the route,  e.g. /customers/{id}
30
31         // Set by the ParamsFilter
32         Query url.Values // Parameters from the query string, e.g. /index?limit=10
33         Form  url.Values // Parameters from the request body.
34
35         Files    map[string][]*multipart.FileHeader // Files uploaded in a multipart form
36         tmpFiles []*os.File                         // Temp files used during the request.
37         JSON     []byte                             // JSON data from request body
38 }
39
40 var paramsLogger = RevelLog.New("section", "params")
41
42 // ParseParams parses the `http.Request` params into `revel.Controller.Params`
43 func ParseParams(params *Params, req *Request) {
44         params.Query = req.GetQuery()
45
46         // Parse the body depending on the content type.
47         switch req.ContentType {
48         case "application/x-www-form-urlencoded":
49                 // Typical form.
50                 var err error
51                 if params.Form, err = req.GetForm(); err != nil {
52                         paramsLogger.Warn("ParseParams: Error parsing request body", "error", err)
53                 }
54
55         case "multipart/form-data":
56                 // Multipart form.
57                 if mp, err := req.GetMultipartForm(); err != nil {
58                         paramsLogger.Warn("ParseParams: parsing request body:", "error", err)
59                 } else {
60                         params.Form = mp.GetValues()
61                         params.Files = mp.GetFiles()
62                 }
63         case "application/json":
64                 fallthrough
65         case "text/json":
66                 if body := req.GetBody(); body != nil {
67                         if content, err := ioutil.ReadAll(body); err == nil {
68                                 // We wont bind it until we determine what we are binding too
69                                 params.JSON = content
70                         } else {
71                                 paramsLogger.Error("ParseParams: Failed to ready request body bytes", "error", err)
72                         }
73                 } else {
74                         paramsLogger.Info("ParseParams: Json post received with empty body")
75                 }
76         }
77
78         params.Values = params.calcValues()
79 }
80
81 // Bind looks for the named parameter, converts it to the requested type, and
82 // writes it into "dest", which must be settable.  If the value can not be
83 // parsed, "dest" is set to the zero value.
84 func (p *Params) Bind(dest interface{}, name string) {
85         value := reflect.ValueOf(dest)
86         if value.Kind() != reflect.Ptr {
87                 paramsLogger.Panic("Bind: revel/params: non-pointer passed to Bind: " + name)
88         }
89         value = value.Elem()
90         if !value.CanSet() {
91                 paramsLogger.Panic("Bind: revel/params: non-settable variable passed to Bind: " + name)
92         }
93
94         // Remove the json from the Params, this will stop the binder from attempting
95         // to use the json data to populate the destination interface. We do not want
96         // to do this on a named bind directly against the param, it is ok to happen when
97         // the action is invoked.
98         jsonData := p.JSON
99         p.JSON = nil
100         value.Set(Bind(p, name, value.Type()))
101         p.JSON = jsonData
102 }
103
104 // Bind binds the JSON data to the dest.
105 func (p *Params) BindJSON(dest interface{}) error {
106         value := reflect.ValueOf(dest)
107         if value.Kind() != reflect.Ptr {
108                 paramsLogger.Warn("BindJSON: Not a pointer")
109                 return errors.New("BindJSON not a pointer")
110         }
111         if err := json.Unmarshal(p.JSON, dest); err != nil {
112                 paramsLogger.Warn("BindJSON: Unable to unmarshal request:", "error", err)
113                 return err
114         }
115         return nil
116 }
117
118 // calcValues returns a unified view of the component param maps.
119 func (p *Params) calcValues() url.Values {
120         numParams := len(p.Query) + len(p.Fixed) + len(p.Route) + len(p.Form)
121
122         // If there were no params, return an empty map.
123         if numParams == 0 {
124                 return make(url.Values, 0)
125         }
126
127         // If only one of the param sources has anything, return that directly.
128         switch numParams {
129         case len(p.Query):
130                 return p.Query
131         case len(p.Route):
132                 return p.Route
133         case len(p.Fixed):
134                 return p.Fixed
135         case len(p.Form):
136                 return p.Form
137         }
138
139         // Copy everything into a param map,
140         // order of priority is least to most trusted
141         values := make(url.Values, numParams)
142
143         // ?query string parameters are first
144         for k, v := range p.Query {
145                 values[k] = append(values[k], v...)
146         }
147
148         // form parameters append
149         for k, v := range p.Form {
150                 values[k] = append(values[k], v...)
151         }
152
153         // :/path parameters overwrite
154         for k, v := range p.Route {
155                 values[k] = v
156         }
157
158         // fixed route parameters overwrite
159         for k, v := range p.Fixed {
160                 values[k] = v
161         }
162
163         return values
164 }
165
166 func ParamsFilter(c *Controller, fc []Filter) {
167         ParseParams(c.Params, c.Request)
168
169         // Clean up from the request.
170         defer func() {
171                 for _, tmpFile := range c.Params.tmpFiles {
172                         err := os.Remove(tmpFile.Name())
173                         if err != nil {
174                                 paramsLogger.Warn("ParamsFilter: Could not remove upload temp file:", err)
175                         }
176                 }
177         }()
178
179         fc[0](c, fc[1:])
180 }