2017-01-11 9 views
2

Я пытаюсь сделать работу GraphQL Java server с сервером GraphiQL.Почему мой контроллер Spring не обрабатывает запрос OPTIONS, отправленный GraphiQL?

Использование GraphiQL работает локально я отправить запрос со следующими параметрами:

GraphiQL query and its results

My Spring контроллер (скопированный из here) выглядит следующим образом:

@Controller 
@EnableAutoConfiguration 
public class MavenController { 
    private final MavenSchema schema = new MavenSchema(); 
    private final GraphQL graphql = new GraphQL(schema.getSchema()); 
    private static final Logger log = LoggerFactory.getLogger(MavenController.class); 

    @RequestMapping(value = "/graphql", method = RequestMethod.OPTIONS, produces = MediaType.APPLICATION_JSON_VALUE) 
    @ResponseBody 
    public Object executeOperation(@RequestBody Map body) { 
     log.error("body: " + body); 
     final String query = (String) body.get("query"); 
     final Map<String, Object> variables = (Map<String, Object>) body.get("variables"); 
     final ExecutionResult executionResult = graphql.execute(query, (Object) null, variables); 
     final Map<String, Object> result = new LinkedHashMap<>(); 
     if (executionResult.getErrors().size() > 0) { 
      result.put("errors", executionResult.getErrors()); 
      log.error("Errors: {}", executionResult.getErrors()); 
     } 
     log.error("data: " + executionResult.getData()); 
     result.put("data", executionResult.getData()); 
     return result; 
    } 
} 

В теории, executeOperation должен быть вызван, когда я отправлю запрос в GraphiQL. Это не так, я не вижу оператора журнала на выходе консоли.

Что я делаю wron? Как я могу убедиться, что вызван MavenController.executeOperation, когда запрос представлен в GraphiQL?

Обновление 1 (13.01.2017 13:35 MSK): Вот учебник о том, как воспроизвести ошибку. Моя цель - создать Java-сервер GraphQL, с которым я могу взаимодействовать с помощью GraphiQL. Если это возможно, это должно работать на местном уровне.

Я read, что для того, чтобы сделать это, необходимо предпринять следующие шаги:

  1. Настройка сервера GraphQL. Пример этого можно найти здесь https://github.com/graphql-java/todomvc-relay-java. В этом примере используется Spring Boot, но вы можете использовать любой HTTP-сервер, который вам нравится получать.
  2. Настройте сервер GraphiQL. Это немного выходит за рамки этого проекта, но в основном вам нужно, чтобы GraphiQL разговаривал с сервером на шаге 1 выше. Он будет использовать интроспекцию для загрузки схемы.

Я проверил проект todomvc-relay-java, модифицировал его в соответствии с моими потребностями и поместить его в папку E:\graphiql-java\graphql-server. Вы можете загрузить архив с этим каталогом here.

Шаг 1: Установка Node.js

Шаг 2

Перейти к E:\graphiql-java\graphql-server\app и запустить npm install там.

Шаг 3

Run npm start из того же каталога.

Шаг 4

Перейти к E:\graphiql-java\graphql-server и запустить gradlew start там.

Шаг 5

docker run -p 8888:8080 -d -e GRAPHQL_SERVER=http://localhost:8080/graphql merapar/graphql-browser-docker Run.

источники Docker: graphql-browser-docker

Шаг 6

Запуск Chrome с проверкой отключена XSS, эл. г. "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --args --disable-xss-auditor. Некоторые источники утверждают, что вам нужно убить все другие экземпляры Chrome, чтобы эти аргументы имели какой-либо эффект.

Шаг 7

Открыть http://localhost:8888/ в этом браузере.

Шаг 8

Попробуйте запустить запрос,

{ allArtifacts(group: "com.graphql-java", name: "graphql-java") { group name version } }

Фактические результаты:

1) Ошибка в вкладке консольном Chrome: Fetch API cannot load http://localhost:8080/graphql. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8888' is therefore not allowed access. The response had HTTP status code 403. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled..

Errors in the "Console" tab

2) Ошибки в вкладке сети в Chrome:

Errors in the "Network" tab

Обновление 2 (14.01.2017 13:51): В настоящее время CORS сконфигурирован в Java-приложения следующим образом.

Основной класс:

@SpringBootApplication 
public class Main { 
    public static void main(String[] args) throws Exception { 
     SpringApplication.run(Main.class, args); 
    } 

    @Bean 
    public WebMvcConfigurer corsConfigurer() { 
     return new WebMvcConfigurerAdapter() { 
      @Override 
      public void addCorsMappings(CorsRegistry registry) { 
       registry 
         .addMapping("/**") 
         .allowedMethods("OPTIONS") 
         .allowedOrigins("*") 
         .allowedHeaders(
           "Access-Control-Request-Headers", 
           "Access-Control-Request-Method", 
           "Host", 
           "Connection", 
           "Origin", 
           "User-Agent", 
           "Accept", 
           "Referer", 
           "Accept-Encoding", 
           "Accept-Language", 
           "Access-Control-Allow-Origin" 
         ) 
         .allowCredentials(true) 
         ; 
      } 
     }; 
    } 
} 

WebSecurityConfigurerAdapter подкласс:

@Configuration 
@EnableWebSecurity 
public class SpringWebSecurityConfiguration extends WebSecurityConfigurerAdapter { 
    @Override 
    protected void configure(final HttpSecurity http) throws Exception { 
     System.out.println("SpringWebSecurityConfiguration"); 
     UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 
     CorsConfiguration config = new CorsConfiguration(); 
     config.setAllowCredentials(true); 
     config.addAllowedOrigin("*"); 
     config.addAllowedHeader("*"); 
     config.addAllowedMethod("OPTIONS"); 
     source.registerCorsConfiguration("/**", config); 
     http 
       .addFilterBefore(new CorsFilter(source), ChannelProcessingFilter.class) 
       .httpBasic() 
       .disable() 
       .authorizeRequests() 
        .anyRequest() 
        .permitAll() 
       .and() 
       .csrf() 
        .disable(); 
    } 
    @Override 
    public void configure(WebSecurity web) throws Exception { 
    } 
} 

Контроллер:

@Controller 
@EnableAutoConfiguration 
public class MavenController { 
    private final MavenSchema schema = new MavenSchema(); 
    private final GraphQL graphql = new GraphQL(schema.getSchema()); 
    private static final Logger log = LoggerFactory.getLogger(MavenController.class); 

    @CrossOrigin(
      origins = {"http://localhost:8888", "*"}, 
      methods = {RequestMethod.OPTIONS}, 
      allowedHeaders = {"Access-Control-Request-Headers", 
      "Access-Control-Request-Method", 
      "Host", 
      "Connection", 
      "Origin", 
      "User-Agent", 
      "Accept", 
      "Referer", 
      "Accept-Encoding", 
      "Accept-Language", 
      "Access-Control-Allow-Origin"}, 
      exposedHeaders = "Access-Control-Allow-Origin" 
      ) 
    @RequestMapping(value = "/graphql", method = RequestMethod.OPTIONS, produces = MediaType.APPLICATION_JSON_VALUE) 
    @ResponseBody 
    public Object executeOperation(@RequestBody Map body) { 
     [...] 
    } 
} 

MavenController.executeOperation является метод, который должен быть вызван, когда я выполняю запрос запроса в GraphiQL.

Update 3 (15.01.2017 22:05): Пытались использовать CORSFilter, новый исходный код here. Нет результата, я все еще получаю ошибку «Неверный ответ CORS».

+1

https://spring.io/guides/gs/rest-service-cors/ –

+0

@AlanHay См. Мое обновление 2. –

ответ

1

Проблема заключается в использовании конфигурации .allowedMethods("OPTIONS") в вашем приложении.

Запросы перед полетом по проекту, отправленные с использованием метода запроса OPTIONS.

Access-Control-Request-Method добавлен браузером автоматически, а свойство allowedMethods фактически контролирует, какой метод запроса разрешен для фактического запроса.

С docs,

Заголовок Access-Control-Request-Method уведомляет сервер как часть от предполетного запроса, когда фактический запрос отправляется, он будет быть отправлен с запросом POST метод.

Так что POST метод и и запрос не, как вы только что позволяет OPTIONS, когда проверка выполняется.

Таким образом, изменение allowedMethods на * будет соответствовать настройке браузера POST метод запроса для фактического запроса.

После того, как вы сделаете это, вы получите 405, так как ваш контроллер разрешает только OPTIONS для вашего запроса POST.

Таким образом, вам необходимо обновить сопоставление запроса контроллера, чтобы позволить POST для фактического запроса получить успешное выполнение после предполетного запроса.

Пример ответа:

{ 
    "timestamp": 1484606833696, 
    "status": 500, 
    "error": "Internal Server Error", 
    "exception": "graphql.AssertException", 
    "message": "arguments can't be null", 
    "path": "/graphql" 
} 

Я не уверен, что выглядит, как вы конфигурации CORS, установленные на слишком многих местах. Мне просто нужно изменить .allowedMethods в конфигурации безопасности весны, чтобы он работал так, как я описал. Поэтому вы можете посмотреть на это.

1

Вы должны настроить сервлет диспетчера весны (http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/DispatcherServlet.html) для обработки ОПЦИЙ.

Через XML вы должны сделать:

<servlet> 
    <servlet-name>yourSpringSvltname</servlet-name> 
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
    <init-param> 
     <param-name>dispatchOptionsRequest</param-name> 
     <param-value>true</param-value> 
    </init-param> 
    <load-on-startup>1</load-on-startup> 
    </servlet> 

Via Java Config:

public class MyWebAppInitializer implements WebApplicationInitializer { 

    @Override 
    public void onStartup(ServletContext container) { 
     XmlWebApplicationContext appContext = new XmlWebApplicationContext(); 
     appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml"); 

     ServletRegistration.Dynamic dispatcher = 
     container.addServlet("dispatcher", new DispatcherServlet(appContext)); 
dispatchersetDispatchOptionsRequest(true); 
     dispatcher.setLoadOnStartup(1); 
     dispatcher.addMapping("/"); 
    } 

} 
+2

Я не знаю, работает ли этот ответ, но это правильно, что вам нужно включить/настроить CORS весной. CORS - это стандарт, реализованный веб-браузерами для предотвращения атак XSS. Он требует, чтобы сервер указывал, что он принимает запросы от определенных URL-адресов переднего плана, так что запросы не могут просто появляться в любом месте в Интернете. В браузере, который реализует CORS, я не могу просто добавить кусок JavaScript на example.com на bankofamerica.com, потому что BoA хочет обрабатывать запросы только с его сторонних концов, а не example.com. Вы все равно можете сделать запрос вручную, но не от имени незнакомых пользователей –

+0

@DmitryMinkovsky Спасибо.Я уже настраиваю CORS в своем приложении Java. Вы можете увидеть текущую конфигурацию в моем обновлении 2. –

1

У меня был тот же вопрос некоторое время назад, и я решил, создав из моих собственных CORS fiter боба.

Пример:

public class CORSFilter implements Filter { 

@Override 
public void init(FilterConfig filterConfig) throws ServletException { 

} 

@Override 
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 
    HttpServletRequest request = (HttpServletRequest) req; 
    HttpServletResponse response = (HttpServletResponse) res; 

    response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); 
    response.setHeader("Access-Control-Allow-Credentials", "true"); 
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT, OPTIONS"); 
    response.setHeader("Access-Control-Max-Age", "3600"); 
    response.setHeader("Access-Control-Allow-Headers", "accept, access-control-allow-origin, authorization, content-type"); 

    if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { 
     response.setStatus(HttpServletResponse.SC_OK); 
    } else { 
     chain.doFilter(req, res); 
    } 
} 

@Override 
public void destroy() { 

} 

}

И я определил это в своем классе конфигурации:

@Bean(name = "corsFilter") 
@Order(Ordered.HIGHEST_PRECEDENCE) 
public CORSFilter corsFilter() { 
    return new CORSFilter(); 
} 

И, наконец, определили его на мой сервлет запуска конфигурации:

servletContext.addFilter("corsFilter", new DelegatingFilterProxy("corsFilter")).addMappingForUrlPatterns(null, false, "/*"); 

P.S. Надеюсь, что это поможет вам.

+0

Спасибо. Я пробовал это, но это не помогло. Вы можете найти ссылку на архив с новым исходным кодом (с 'CORSFilter') в моем обновлении 3. –

+0

Это действительно странно. Кажется, у вас нет действительной конфигурации для вашего приложения. Потому что фильтр должен работать и обрабатывать запрос первым. – eg04lt3r

 Смежные вопросы

  • Нет связанных вопросов^_^