Как передать параметры / переменные в запрос или мутацию?
Я пытаюсь прочитать некоторые параметры, передаваемые через переменные на моем бэкэнде, давайте посмотрим:
(этот метод находится внутри AuthenticationService
, введенный в моем контроллере graphql, см. ниже)
@GraphQLMutation(name = "getSessionToken")
public AuthReturn getSessionToken(@GraphQLArgument(name = "user") String u, @GraphQLArgument(name = "password") String p) {...}
И вот мой запрос graphQL:
mutation ($user: String!, $password: String!) {
getSessionToken(user: $user, password: $password) {
status
payload
}
}
и мои переменные:
{ "user": "myuser", "password": "mypass"}
но когда я пытаюсь запустить этот пример кода, появляется следующая ошибка:
{"timestamp":"2019-07-29T17:18:32.753+0000","status":400,"error":"Bad Request","message":"JSON parse error: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token\n at [Source: (PushbackInputStream); line: 1, column: 162] (through reference chain: java.util.LinkedHashMap[\"variables\"])","path":"/graphql"}
[Редактировать] И вот мой контроллер:
@RestController
public class GraphQLController {
private final GraphQL graphQL;
public GraphQLController(AgendamentoService agendamentos, ConfiguracaoService config, ProcessoService processos, ParametroService parametros, AuthenticationService autenticacao) {
GraphQLSchema schema = new GraphQLSchemaGenerator()
.withResolverBuilders(
//Resolve by annotations
new AnnotatedResolverBuilder())
.withOperationsFromSingletons(agendamentos, config, processos, parametros, autenticacao)
.withValueMapperFactory(new JacksonValueMapperFactory())
.generate();
graphQL = GraphQL.newGraphQL(schema).build();
}
@CrossOrigin
@PostMapping(value = "/graphql", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public Map<String, Object> graphql(@RequestBody Map<String, String> request, HttpServletRequest raw) {
// em context estamos passando o Request, usamos para fazer as verificacoes de autenticacao com GraphQl
ExecutionResult executionResult = graphQL.execute(ExecutionInput.newExecutionInput()
.query(request.get("query"))
.operationName(request.get("operationName"))
.context(raw)
.build());
return executionResult.toSpecification();
}
}
но если я запускаю эту мутацию без передачи параметров как variables
по запросу все работает правильно. Что я могу сделать, чтобы передать переменные в мои запросы GraphQl? Заранее спасибо.
2 ответа
Вы на самом деле не передаете переменную в graphql-java. Это должно быть сделано через ExecutionInput
, Я бы предложил создать такой класс, как:
@JsonIgnoreProperties(ignoreUnknown = true)
public class GraphQLRequest {
private final String query;
private final String operationName;
private final Map<String, Object> variables;
@JsonCreator
public GraphQLRequest(@JsonProperty("query") String query,
@JsonProperty("operationName") String operationName,
@JsonProperty("variables") Map<String, Object> variables) {
this.query = query;
this.operationName = operationName;
this.variables = variables != null ? variables : Collections.emptyMap();
}
public String getQuery() {
return query;
}
public String getOperationName() {
return operationName;
}
public Map<String, Object> getVariables() {
return variables;
}
}
и используйте это как параметр в методе контроллера:
@CrossOrigin
@PostMapping(value = "/graphql", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public Map<String, Object> graphql(@RequestBody GraphQLRequest graphQLRequest, HttpServletRequest httpRequest) {
// em context estamos passando o Request, usamos para fazer as verificacoes de autenticacao com GraphQl
ExecutionInput.Builder inputBuilder = ExecutionInput.newExecutionInput()
.query(graphQLRequest.getQuery())
.operationName(graphQLRequest.getOperationName())
.variables(graphQLRequest.getVariables()) //this is the line you were missing
.context(httpRequest);
return executionResult.toSpecification();
}
Отсутствующие переменные в ExecutionInput
все же не объясняйте ошибку десериализации, которую вы получили. В нем говорится, что в JSON был найден объект, в котором ожидалась строка. Не уверен, откуда это происходит, но я подозреваю, что веб-часть больше, чем реальный код.
В любом случае, поместите точку останова в коде контроллера и посмотрите, правильно ли десериализован запрос и ударился ли вообще механизм GraphQL.
Я также предлагаю вам упростить настройку:
public GraphQLController(AgendamentoService agendamentos, ConfiguracaoService config, ProcessoService processos, ParametroService parametros, AuthenticationService autenticacao) {
GraphQLSchema schema = new GraphQLSchemaGenerator()
.withResolverBuilders(
//Resolve by annotations
new AnnotatedResolverBuilder())
.withOperationsFromSingletons(agendamentos, config, processos, parametros, autenticacao)
.withValueMapperFactory(new JacksonValueMapperFactory())
.generate();
graphQL = GraphQL.newGraphQL(schema).build();
}
в
public GraphQLController(AgendamentoService agendamentos, ConfiguracaoService config, ProcessoService processos, ParametroService parametros, AuthenticationService autenticacao) {
GraphQLSchema schema = new GraphQLSchemaGenerator()
.withOperationsFromSingletons(agendamentos, config, processos, parametros, autenticacao)
.generate();
graphQL = GraphQL.newGraphQL(schema).build();
}
так как остальные строки избыточны. Они только устанавливают то, что уже является поведением по умолчанию.
вы можете использовать VTL для этого варианта использования
Map<String, Object> requestBody = new HashMap<>();
String query = "query MyQuery {"
+ " getTemplate("
+ " id: \"$id\""
+ " ){"
+ " id"
+ " listOfPlaceholders"
+ " messageTemplate"
+ " type"
+ " }"
+ "}";
VelocityContext queryContext = new VelocityContext();
queryContext.put("id", data.get("id"));
StringWriter queryWriter = new StringWriter();
Velocity.evaluate(context, queryWriter, "TemplateName", query);
System.out.println(queryWriter.toString());
requestBody.put("query", queryWriter.toString());