Grpc-gateway: Encoding with application/x-www-form-urlencoded

Created on 19 Apr 2015  路  4Comments  路  Source: grpc-ecosystem/grpc-gateway

Today request/response body is always encoded in JSON.
Should accept application/x-www-form-urlencoded too.

  1. Define a new field in the custom option to define a list of acceptable encoding of request body
  2. Similar field to define the list of possible response body

    • maybe should prefer the same encoding as the request

  3. Make the parameter deserialization/serialization pluggable
enhancement

Most helpful comment

we ended up rewriting application/x-www-form-urlencoded requests as JSON prior to the grpc-gateway receiving them like this:

        formWrapper := func(h http.Handler) http.Handler {
                return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                        log.Printf("Got request: %#v\n", r)
                        if strings.ToLower(strings.Split(r.Header.Get("Content-Type"), ";")[0]) == "application/x-www-form-urlencoded" {
                                log.Println("Rewriting form data as json")
                                if err := r.ParseForm(); err != nil {
                                        http.Error(w, err.Error(), http.StatusBadRequest)
                                        log.Println("Bad form request", err.Error())
                                        return
                                }
                                jsonMap := make(map[string]interface{}, len(r.Form))
                                for k, v := range r.Form {
                                        if len(v) > 0 {
                                                jsonMap[k] = v[0]
                                        }
                                }
                                jsonBody, err := json.Marshal(jsonMap)
                                if err != nil {
                                        http.Error(w, err.Error(), http.StatusBadRequest)
                                }

                                r.Body = ioutil.NopCloser(bytes.NewReader(jsonBody))
                                r.ContentLength = int64(len(jsonBody))
                                r.Header.Set("Content-Type", "application/json")
                        }
                        mux.ServeHTTP(w, r)
                })
        }
        if err := http.ListenAndServe(":8080", formWrapper(mux)); err != nil {
                log.Fatalf("failed to start gateway server on 8080: %v", err)
        }

All 4 comments

Is this supported or still open?

I'm implementing an OAuth2 server and the RFC requires the request Content-Type to be application/x-www-form-urlencoded.

we ended up rewriting application/x-www-form-urlencoded requests as JSON prior to the grpc-gateway receiving them like this:

        formWrapper := func(h http.Handler) http.Handler {
                return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                        log.Printf("Got request: %#v\n", r)
                        if strings.ToLower(strings.Split(r.Header.Get("Content-Type"), ";")[0]) == "application/x-www-form-urlencoded" {
                                log.Println("Rewriting form data as json")
                                if err := r.ParseForm(); err != nil {
                                        http.Error(w, err.Error(), http.StatusBadRequest)
                                        log.Println("Bad form request", err.Error())
                                        return
                                }
                                jsonMap := make(map[string]interface{}, len(r.Form))
                                for k, v := range r.Form {
                                        if len(v) > 0 {
                                                jsonMap[k] = v[0]
                                        }
                                }
                                jsonBody, err := json.Marshal(jsonMap)
                                if err != nil {
                                        http.Error(w, err.Error(), http.StatusBadRequest)
                                }

                                r.Body = ioutil.NopCloser(bytes.NewReader(jsonBody))
                                r.ContentLength = int64(len(jsonBody))
                                r.Header.Set("Content-Type", "application/json")
                        }
                        mux.ServeHTTP(w, r)
                })
        }
        if err := http.ListenAndServe(":8080", formWrapper(mux)); err != nil {
                log.Fatalf("failed to start gateway server on 8080: %v", err)
        }

+1 It would be useful to have it built-in!

After one afternoon reading the f**king source code.

This solution is not good.

  1. It cannot check the input whether a int or string by pb.
  2. ...

The good solution is:

  1. make post && form use get logic.
  2. change the autogen code:
- func request_Example_GetForm_0(ctx context.Context, marshaler runtime.Marshaler, client ExampleClient, req *http.Request, pathPar
|     var protoReq FormRequest
|     var metadata runtime.ServerMetadata
|      
|        ---------------------------from form data to genrate : req.URL.Query() ------------------------------
|     if  err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_Example_GetForm_0); err != nil {
|         return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|     }
|
|     msg, err := client.GetForm(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|     return msg, metadata, err
|
| }
  1. change the form data to url.Values then call:

PopulateQueryParameters(msg proto.Message, values url.Values, filter *utilities.DoubleArray)

Was this page helpful?
0 / 5 - 0 ratings