github.com/99designs/gqlgen@v0.17.45/graphql/handler/transport/http_get.go (about) 1 package transport 2 3 import ( 4 "encoding/json" 5 "io" 6 "net/http" 7 "net/url" 8 "strings" 9 10 "github.com/vektah/gqlparser/v2/ast" 11 "github.com/vektah/gqlparser/v2/gqlerror" 12 13 "github.com/99designs/gqlgen/graphql" 14 "github.com/99designs/gqlgen/graphql/errcode" 15 ) 16 17 // GET implements the GET side of the default HTTP transport 18 // defined in https://github.com/APIs-guru/graphql-over-http#get 19 type GET struct { 20 // Map of all headers that are added to graphql response. If not 21 // set, only one header: Content-Type: application/json will be set. 22 ResponseHeaders map[string][]string 23 } 24 25 var _ graphql.Transport = GET{} 26 27 func (h GET) Supports(r *http.Request) bool { 28 if r.Header.Get("Upgrade") != "" { 29 return false 30 } 31 32 return r.Method == "GET" 33 } 34 35 func (h GET) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) { 36 query, err := url.ParseQuery(r.URL.RawQuery) 37 if err != nil { 38 w.WriteHeader(http.StatusBadRequest) 39 writeJsonError(w, err.Error()) 40 return 41 } 42 writeHeaders(w, h.ResponseHeaders) 43 44 raw := &graphql.RawParams{ 45 Query: query.Get("query"), 46 OperationName: query.Get("operationName"), 47 Headers: r.Header, 48 } 49 raw.ReadTime.Start = graphql.Now() 50 51 if variables := query.Get("variables"); variables != "" { 52 if err := jsonDecode(strings.NewReader(variables), &raw.Variables); err != nil { 53 w.WriteHeader(http.StatusBadRequest) 54 writeJsonError(w, "variables could not be decoded") 55 return 56 } 57 } 58 59 if extensions := query.Get("extensions"); extensions != "" { 60 if err := jsonDecode(strings.NewReader(extensions), &raw.Extensions); err != nil { 61 w.WriteHeader(http.StatusBadRequest) 62 writeJsonError(w, "extensions could not be decoded") 63 return 64 } 65 } 66 67 raw.ReadTime.End = graphql.Now() 68 69 rc, gqlError := exec.CreateOperationContext(r.Context(), raw) 70 if gqlError != nil { 71 w.WriteHeader(statusFor(gqlError)) 72 resp := exec.DispatchError(graphql.WithOperationContext(r.Context(), rc), gqlError) 73 writeJson(w, resp) 74 return 75 } 76 op := rc.Doc.Operations.ForName(rc.OperationName) 77 if op.Operation != ast.Query { 78 w.WriteHeader(http.StatusNotAcceptable) 79 writeJsonError(w, "GET requests only allow query operations") 80 return 81 } 82 83 responses, ctx := exec.DispatchOperation(r.Context(), rc) 84 writeJson(w, responses(ctx)) 85 } 86 87 func jsonDecode(r io.Reader, val interface{}) error { 88 dec := json.NewDecoder(r) 89 dec.UseNumber() 90 return dec.Decode(val) 91 } 92 93 func statusFor(errs gqlerror.List) int { 94 switch errcode.GetErrorKind(errs) { 95 case errcode.KindProtocol: 96 return http.StatusUnprocessableEntity 97 default: 98 return http.StatusOK 99 } 100 }