github.com/profzone/eden-framework@v1.0.10/pkg/courier/transport_http/transform/request.go (about) 1 package transform 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "mime/multipart" 9 "net/http" 10 "net/textproto" 11 "net/url" 12 "reflect" 13 "strings" 14 "time" 15 16 "github.com/profzone/eden-framework/pkg/courier" 17 "github.com/profzone/eden-framework/pkg/courier/httpx" 18 "github.com/profzone/eden-framework/pkg/courier/status_error" 19 ) 20 21 func NewRequest(method string, uri string, v interface{}, metadatas ...courier.Metadata) (*http.Request, error) { 22 if v == nil { 23 return NewHttpRequestFromParameterGroup(method, uri, nil, metadatas...) 24 } 25 return NewHttpRequestFromParameterGroup(method, uri, ParameterGroupFromValue(v), metadatas...) 26 } 27 28 func CloneRequestBody(req *http.Request) ([]byte, error) { 29 bodyBytes, err := ioutil.ReadAll(req.Body) 30 if err != nil { 31 return nil, err 32 } 33 req.Body.Close() 34 req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) 35 return bodyBytes, nil 36 } 37 38 func NewHttpRequestFromParameterGroup(method string, uri string, m *ParameterGroup, metadatas ...courier.Metadata) (req *http.Request, err error) { 39 body := &bytes.Buffer{} 40 multipartWriter := (*multipart.Writer)(nil) 41 42 if m != nil { 43 if m.FormData.Len() > 0 { 44 postForm := (*url.Values)(nil) 45 46 for _, parameterMeta := range m.FormData { 47 name := parameterMeta.Name 48 dataList, errForMarshal := parameterMeta.Marshal() 49 50 if errForMarshal != nil { 51 err = errForMarshal 52 return 53 } 54 55 if parameterMeta.IsMultipart() { 56 if multipartWriter == nil { 57 multipartWriter = multipart.NewWriter(body) 58 } 59 60 if fileHeaders, ok := parameterMeta.Value.Interface().([]*multipart.FileHeader); ok { 61 for _, fileHeader := range fileHeaders { 62 if errForAppend := appendFile(multipartWriter, name, fileHeader, nil); errForAppend != nil { 63 err = errForAppend 64 return 65 } 66 } 67 continue 68 } 69 70 if fileHeader, ok := parameterMeta.Value.Interface().(*multipart.FileHeader); ok { 71 if errForAppend := appendFile(multipartWriter, name, fileHeader, nil); errForAppend != nil { 72 err = errForAppend 73 return 74 } 75 continue 76 } 77 78 for _, v := range dataList { 79 err = multipartWriter.WriteField(name, string(v)) 80 if err != nil { 81 return 82 } 83 } 84 } else { 85 if postForm == nil { 86 postForm = &url.Values{} 87 } 88 for _, v := range dataList { 89 postForm.Add(name, string(v)) 90 } 91 } 92 } 93 if postForm != nil { 94 body.WriteString(postForm.Encode()) 95 } 96 } else if m.Body != nil { 97 dataList, errForMarshal := m.Body.Marshal() 98 if errForMarshal != nil { 99 err = errForMarshal 100 return 101 } 102 if len(dataList) > 0 { 103 body.Write(dataList[0]) 104 } 105 } 106 } 107 if multipartWriter != nil { 108 multipartWriter.Close() 109 } 110 111 u, errForParseUrl := url.Parse(uri) 112 if errForParseUrl != nil { 113 err = errForParseUrl 114 return 115 } 116 117 if u.Path == "" { 118 u.Path = "/" 119 } 120 121 req, err = http.NewRequest( 122 method, 123 u.String(), 124 body, 125 ) 126 127 if err != nil { 128 return 129 } 130 131 req.Close = true 132 133 if metadatas != nil { 134 for key, values := range courier.MetadataMerge(metadatas...) { 135 for _, v := range values { 136 req.Header.Add(key, v) 137 } 138 } 139 } 140 141 if m == nil { 142 return 143 } 144 145 for _, parameterMeta := range m.Parameters { 146 name := parameterMeta.Name 147 dataList, errForMarshal := parameterMeta.Marshal() 148 if errForMarshal != nil { 149 err = errForMarshal 150 return 151 } 152 153 if len(dataList) == 0 { 154 continue 155 } 156 157 switch parameterMeta.In { 158 case "query": 159 // todo use explode when service updated 160 query := url.Values{} 161 query.Add(parameterMeta.Name, string(bytes.Join(dataList, []byte(",")))) 162 163 if req.URL.RawQuery == "" { 164 req.URL.RawQuery = query.Encode() 165 } else { 166 req.URL.RawQuery = req.URL.RawQuery + "&" + query.Encode() 167 } 168 case "cookie": 169 for _, v := range dataList { 170 req.AddCookie(&http.Cookie{ 171 Name: name, 172 Value: string(v), 173 }) 174 } 175 case "path": 176 paramKey := ":" + name 177 if !strings.Contains(req.URL.Path, paramKey) { 178 err = fmt.Errorf("uri %s need path parameter %s, but uri has no %s", uri, name, paramKey) 179 return 180 } 181 value := string(bytes.Join(dataList, []byte(","))) 182 req.URL.Path = strings.Replace(req.URL.Path, paramKey, value, -1) 183 case "header": 184 value := string(bytes.Join(dataList, []byte(","))) 185 req.Header.Add(name, value) 186 } 187 } 188 189 if m.FormData.Len() > 0 { 190 if multipartWriter != nil { 191 req.Header.Add(httpx.HeaderContentType, multipartWriter.FormDataContentType()) 192 } else { 193 req.Header.Add(httpx.HeaderContentType, httpx.MIME_POST_URLENCODED+"; param=value") 194 } 195 } 196 197 return 198 } 199 200 func appendFile(multipartWriter *multipart.Writer, field string, fileHeader *multipart.FileHeader, content []byte) (err error) { 201 if fileHeader == nil { 202 return 203 } 204 filePart, errForCreate := multipartWriter.CreateFormFile(field, fileHeader.Filename) 205 if errForCreate != nil { 206 err = errForCreate 207 return 208 } 209 210 if len(content) == 0 { 211 if file, errForOpen := fileHeader.Open(); errForOpen != nil { 212 err = errForOpen 213 return 214 } else if _, errForCopy := io.Copy(filePart, file); errForCopy != nil { 215 err = errForCopy 216 return 217 } 218 return nil 219 } 220 221 if _, errForWrite := filePart.Write(content); errForWrite != nil { 222 err = errForWrite 223 return 224 } 225 return nil 226 } 227 228 func NewFileHeader(fieldName string, filename string, content []byte) (fileHeader *multipart.FileHeader, err error) { 229 buffer := &bytes.Buffer{} 230 multipartWriter := multipart.NewWriter(buffer) 231 appendFile(multipartWriter, fieldName, &multipart.FileHeader{ 232 Filename: filename, 233 }, content) 234 multipartWriter.Close() 235 reader := multipart.NewReader(buffer, multipartWriter.Boundary()) 236 form, errForReader := reader.ReadForm(int64(buffer.Len())) 237 if errForReader != nil { 238 err = errForReader 239 return 240 } 241 fileHeader = form.File[fieldName][0] 242 return 243 } 244 245 type IParameterValuesGetter interface { 246 Initial() error 247 Param(name string) string 248 Query(name string) []string 249 Header(name string) []string 250 Cookie(name string) []string 251 FormValue(name string) []string 252 FormFile(name string) ([]*multipart.FileHeader, error) 253 Body() io.Reader 254 } 255 256 func NewParameterValuesGetter(r *http.Request) *ParameterValuesGetter { 257 return &ParameterValuesGetter{Request: r} 258 } 259 260 type ParameterValuesGetter struct { 261 Request *http.Request 262 query url.Values 263 now time.Time 264 cookies []*http.Cookie 265 } 266 267 func (getter *ParameterValuesGetter) Initial() error { 268 getter.now = time.Now() 269 getter.query = getter.Request.URL.Query() 270 return nil 271 } 272 273 func (getter *ParameterValuesGetter) Param(name string) string { 274 return "" 275 } 276 277 func (getter *ParameterValuesGetter) Query(name string) []string { 278 return getter.query[name] 279 } 280 281 func (getter *ParameterValuesGetter) Header(name string) []string { 282 return getter.Request.Header[textproto.CanonicalMIMEHeaderKey(name)] 283 } 284 285 func (getter *ParameterValuesGetter) Cookie(name string) []string { 286 values := make([]string, 0) 287 if len(getter.cookies) == 0 { 288 getter.cookies = getter.Request.Cookies() 289 } 290 for _, c := range getter.cookies { 291 if c.Name == name { 292 if c.Expires.IsZero() { 293 values = append(values, c.Value) 294 } else if c.Expires.After(getter.now) { 295 values = append(values, c.Value) 296 } 297 } 298 } 299 return values 300 } 301 302 func (getter *ParameterValuesGetter) FormValue(name string) []string { 303 if getter.Request.Form == nil { 304 // just parse form 305 getter.Request.FormValue("") 306 } 307 return getter.Request.Form[name] 308 } 309 310 func (getter *ParameterValuesGetter) FormFile(name string) ([]*multipart.FileHeader, error) { 311 if getter.Request.MultipartForm == nil { 312 // just parse form 313 _, fileHeader, err := getter.Request.FormFile(name) 314 if err != nil || fileHeader == nil { 315 return nil, err 316 } 317 } 318 return getter.Request.MultipartForm.File[name], nil 319 } 320 321 func (getter *ParameterValuesGetter) Body() io.Reader { 322 return getter.Request.Body 323 } 324 325 func MarshalParameters(group *ParameterGroup, getter IParameterValuesGetter) error { 326 parameterErrors := ParameterErrors{} 327 328 if err := getter.Initial(); err != nil { 329 parameterErrors.Merge(err) 330 return parameterErrors.Err() 331 } 332 333 for _, parameterMeta := range group.Parameters.List() { 334 switch parameterMeta.In { 335 case "path": 336 parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(strings.Split(getter.Param(parameterMeta.Name), ",")...)) 337 case "header": 338 parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.Header(parameterMeta.Name)...)) 339 case "query": 340 parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.Query(parameterMeta.Name)...)) 341 case "cookie": 342 parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.Cookie(parameterMeta.Name)...)) 343 } 344 } 345 346 for _, parameterMeta := range group.FormData { 347 if _, ok := parameterMeta.Value.Interface().([]*multipart.FileHeader); ok { 348 fileHeaders, err := getter.FormFile(parameterMeta.Name) 349 if err != nil || len(fileHeaders) == 0 { 350 return status_error.ReadFormFileFailed 351 } 352 parameterMeta.Value.Set(reflect.ValueOf(fileHeaders)) 353 continue 354 } 355 if _, ok := parameterMeta.Value.Interface().(*multipart.FileHeader); ok { 356 fileHeaders, err := getter.FormFile(parameterMeta.Name) 357 if err != nil || len(fileHeaders) == 0 { 358 return status_error.ReadFormFileFailed 359 } 360 if parameterMeta.Value.CanSet() { 361 parameterMeta.Value.Set(reflect.ValueOf(fileHeaders[0])) 362 } 363 continue 364 } 365 parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.FormValue(parameterMeta.Name)...)) 366 } 367 368 if parameterErrors.StatusError == nil { 369 isValid, errFields := group.ValidateNoBodyByHook() 370 if !isValid { 371 parameterErrors.Merge(status_error.InvalidField.StatusError().WithErrorFields(errFields...)) 372 } 373 } 374 375 if group.Body != nil { 376 parameterErrors.Merge(group.Body.UnmarshalFromReader(getter.Body())) 377 } 378 379 return parameterErrors.Err() 380 }