1
1
package graphql .spring .web .reactive .components ;
2
2
3
-
4
3
import com .fasterxml .jackson .databind .ObjectMapper ;
5
4
import graphql .ExecutionResult ;
6
5
import graphql .Internal ;
7
6
import graphql .spring .web .reactive .ExecutionResultHandler ;
8
7
import graphql .spring .web .reactive .GraphQLInvocation ;
9
8
import graphql .spring .web .reactive .GraphQLInvocationData ;
10
9
import org .springframework .beans .factory .annotation .Autowired ;
10
+ import org .springframework .http .HttpHeaders ;
11
+ import org .springframework .http .HttpStatus ;
11
12
import org .springframework .http .MediaType ;
12
13
import org .springframework .web .bind .annotation .RequestBody ;
14
+ import org .springframework .web .bind .annotation .RequestHeader ;
13
15
import org .springframework .web .bind .annotation .RequestMapping ;
14
16
import org .springframework .web .bind .annotation .RequestMethod ;
15
17
import org .springframework .web .bind .annotation .RequestParam ;
16
18
import org .springframework .web .bind .annotation .RestController ;
19
+ import org .springframework .web .server .ResponseStatusException ;
17
20
import org .springframework .web .server .ServerWebExchange ;
18
21
import reactor .core .publisher .Mono ;
19
22
@@ -36,16 +39,55 @@ public class GraphQLController {
36
39
37
40
@ RequestMapping (value = "${graphql.url:graphql}" ,
38
41
method = RequestMethod .POST ,
39
- consumes = MediaType .APPLICATION_JSON_VALUE ,
40
42
produces = MediaType .APPLICATION_JSON_VALUE )
41
- public Object graphqlPOST (@ RequestBody GraphQLRequestBody body ,
42
- ServerWebExchange serverWebExchange ) {
43
- String query = body .getQuery ();
44
- if (query == null ) {
45
- query = "" ;
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
+ ServerWebExchange serverWebExchange ) throws IOException {
50
+
51
+ if (body == null ) {
52
+ body = "" ;
46
53
}
47
- Mono <ExecutionResult > executionResult = graphQLInvocation .invoke (new GraphQLInvocationData (query , body .getOperationName (), body .getVariables ()), serverWebExchange );
48
- return executionResultHandler .handleExecutionResult (executionResult , serverWebExchange .getResponse ());
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 (), serverWebExchange );
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 ), serverWebExchange );
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 , serverWebExchange );
88
+ }
89
+
90
+ throw new ResponseStatusException (HttpStatus .UNPROCESSABLE_ENTITY , "Could not process GraphQL request" );
49
91
}
50
92
51
93
@ RequestMapping (value = "${graphql.url:graphql}" ,
@@ -55,10 +97,28 @@ public Object graphqlGET(
55
97
@ RequestParam ("query" ) String query ,
56
98
@ RequestParam (value = "operationName" , required = false ) String operationName ,
57
99
@ RequestParam (value = "variables" , required = false ) String variablesJson ,
58
- ServerWebExchange serverWebExchange
59
- ) {
60
- Mono <ExecutionResult > executionResult = graphQLInvocation .invoke (new GraphQLInvocationData (query , operationName , convertVariablesJson (variablesJson )), serverWebExchange );
61
- return executionResultHandler .handleExecutionResult (executionResult , serverWebExchange .getResponse ());
100
+ ServerWebExchange serverWebExchange ) {
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 ), serverWebExchange );
62
122
}
63
123
64
124
private Map <String , Object > convertVariablesJson (String jsonMap ) {
@@ -71,5 +131,14 @@ private Map<String, Object> convertVariablesJson(String jsonMap) {
71
131
72
132
}
73
133
134
+ private Object executeRequest (
135
+ String query ,
136
+ String operationName ,
137
+ Map <String , Object > variables ,
138
+ ServerWebExchange serverWebExchange ) {
139
+ GraphQLInvocationData invocationData = new GraphQLInvocationData (query , operationName , variables );
140
+ Mono <ExecutionResult > executionResult = graphQLInvocation .invoke (invocationData , serverWebExchange );
141
+ return executionResultHandler .handleExecutionResult (executionResult , serverWebExchange .getResponse ());
142
+ }
74
143
75
144
}
0 commit comments