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