11package graphql .spring .web .servlet .components ;
22
3-
43import com .fasterxml .jackson .databind .ObjectMapper ;
54import graphql .ExecutionResult ;
65import graphql .Internal ;
76import graphql .spring .web .servlet .ExecutionResultHandler ;
87import graphql .spring .web .servlet .GraphQLInvocation ;
98import graphql .spring .web .servlet .GraphQLInvocationData ;
109import org .springframework .beans .factory .annotation .Autowired ;
10+ import org .springframework .http .HttpHeaders ;
11+ import org .springframework .http .HttpStatus ;
1112import org .springframework .http .MediaType ;
1213import org .springframework .web .bind .annotation .RequestBody ;
14+ import org .springframework .web .bind .annotation .RequestHeader ;
1315import org .springframework .web .bind .annotation .RequestMapping ;
1416import org .springframework .web .bind .annotation .RequestMethod ;
1517import org .springframework .web .bind .annotation .RequestParam ;
1618import org .springframework .web .bind .annotation .RestController ;
1719import org .springframework .web .context .request .WebRequest ;
20+ import org .springframework .web .server .ResponseStatusException ;
1821
1922import java .io .IOException ;
2023import java .util .Collections ;
@@ -36,28 +39,86 @@ public class GraphQLController {
3639
3740 @ RequestMapping (value = "${graphql.url:graphql}" ,
3841 method = RequestMethod .POST ,
39- consumes = MediaType .APPLICATION_JSON_VALUE ,
40- produces = MediaType .APPLICATION_JSON_UTF8_VALUE )
41- public Object graphqlPOST (@ RequestBody GraphQLRequestBody body ,
42- WebRequest webRequest ) {
43- String query = body .getQuery ();
44- if (query == null ) {
45- query = "" ;
42+ produces = MediaType .APPLICATION_JSON_VALUE )
43+ public Object graphqlPOST (
44+ @ RequestHeader (value = HttpHeaders .CONTENT_TYPE , required = false ) String contentType ,
45+ @ RequestParam (value = "query" , required = false ) String query ,
46+ @ RequestParam (value = "operationName" , required = false ) String operationName ,
47+ @ RequestParam (value = "variables" , required = false ) String variablesJson ,
48+ @ RequestBody (required = false ) String body ,
49+ WebRequest webRequest ) throws IOException {
50+
51+ if (body == null ) {
52+ body = "" ;
4653 }
47- CompletableFuture <ExecutionResult > executionResult = graphQLInvocation .invoke (new GraphQLInvocationData (query , body .getOperationName (), body .getVariables ()), webRequest );
48- return executionResultHandler .handleExecutionResult (executionResult );
54+
55+ // https://graphql.org/learn/serving-over-http/#post-request
56+ //
57+ // A standard GraphQL POST request should use the application/json content type,
58+ // and include a JSON-encoded body of the following form:
59+ //
60+ // {
61+ // "query": "...",
62+ // "operationName": "...",
63+ // "variables": { "myVariable": "someValue", ... }
64+ // }
65+
66+ if (MediaType .APPLICATION_JSON_VALUE .equals (contentType )) {
67+ GraphQLRequestBody request = objectMapper .readValue (body , GraphQLRequestBody .class );
68+ if (request .getQuery () == null ) {
69+ request .setQuery ("" );
70+ }
71+ return executeRequest (request .getQuery (), request .getOperationName (), request .getVariables (), webRequest );
72+ }
73+
74+ // In addition to the above, we recommend supporting two additional cases:
75+ //
76+ // * If the "query" query string parameter is present (as in the GET example above),
77+ // it should be parsed and handled in the same way as the HTTP GET case.
78+
79+ if (query != null ) {
80+ return executeRequest (query , operationName , convertVariablesJson (variablesJson ), webRequest );
81+ }
82+
83+ // * If the "application/graphql" Content-Type header is present,
84+ // treat the HTTP POST body contents as the GraphQL query string.
85+
86+ if ("application/graphql" .equals (contentType )) {
87+ return executeRequest (body , null , null , webRequest );
88+ }
89+
90+ throw new ResponseStatusException (HttpStatus .UNPROCESSABLE_ENTITY , "Could not process GraphQL request" );
4991 }
5092
5193 @ RequestMapping (value = "${graphql.url:graphql}" ,
5294 method = RequestMethod .GET ,
53- produces = MediaType .APPLICATION_JSON_UTF8_VALUE )
95+ produces = MediaType .APPLICATION_JSON_VALUE )
5496 public Object graphqlGET (
5597 @ RequestParam ("query" ) String query ,
5698 @ RequestParam (value = "operationName" , required = false ) String operationName ,
5799 @ RequestParam (value = "variables" , required = false ) String variablesJson ,
58100 WebRequest webRequest ) {
59- CompletableFuture <ExecutionResult > executionResult = graphQLInvocation .invoke (new GraphQLInvocationData (query , operationName , convertVariablesJson (variablesJson )), webRequest );
60- return executionResultHandler .handleExecutionResult (executionResult );
101+
102+ // https://graphql.org/learn/serving-over-http/#get-request
103+ //
104+ // When receiving an HTTP GET request, the GraphQL query should be specified in the "query" query string.
105+ // For example, if we wanted to execute the following GraphQL query:
106+ //
107+ // {
108+ // me {
109+ // name
110+ // }
111+ // }
112+ //
113+ // This request could be sent via an HTTP GET like so:
114+ //
115+ // http://myapi/graphql?query={me{name}}
116+ //
117+ // Query variables can be sent as a JSON-encoded string in an additional query parameter called "variables".
118+ // If the query contains several named operations,
119+ // an "operationName" query parameter can be used to control which one should be executed.
120+
121+ return executeRequest (query , operationName , convertVariablesJson (variablesJson ), webRequest );
61122 }
62123
63124 private Map <String , Object > convertVariablesJson (String jsonMap ) {
@@ -70,5 +131,14 @@ private Map<String, Object> convertVariablesJson(String jsonMap) {
70131
71132 }
72133
134+ private Object executeRequest (
135+ String query ,
136+ String operationName ,
137+ Map <String , Object > variables ,
138+ WebRequest webRequest ) {
139+ GraphQLInvocationData invocationData = new GraphQLInvocationData (query , operationName , variables );
140+ CompletableFuture <ExecutionResult > executionResult = graphQLInvocation .invoke (invocationData , webRequest );
141+ return executionResultHandler .handleExecutionResult (executionResult );
142+ }
73143
74144}
0 commit comments