Swagger Codegen — тип контента для GET, который не устанавливается в Spring-MVC

Скачал Swagger Pet Store (простой) со страницы редактора swagger.io и сделал mvn jetty:run

При открытии DevTools в Chrome и выполнении GET я не вижу установленного поля заголовка Content-Type.

Это в GET /pets{id}

снимок экрана


person Michael H    schedule 04.11.2015    source источник
comment
Немного сложно увидеть все заголовки на изображении. Но вы получаете тип контента, установленный как META-тег в ответе HTML, а не в заголовках HTTP, что может быть побочным эффектом того факта, что вы получаете страницу с ошибкой. Без заголовков REQUEST трудно быть уверенным, но я думаю, что это говорит о том, что вы запросили JSON, я не знаю, что это такое   -  person Roger Willcocks    schedule 04.11.2015
comment
Под этим я подразумеваю заголовок REQUEST для curl. DevTools говорят, что принимают приложение/xml   -  person Roger Willcocks    schedule 04.11.2015
comment
да, но заголовки запросов devtools вообще не имеют установленного типа контента. Причина, по которой я разместил этот пример, заключается в том, что в моем реальном коде на контроллере Spring я получаю: {timestamp: 1446597153755, status: 400, error: Bad Request, exception: org.springframework.web.HttpMediaTypeNotSupportedException, message: Content type 'null' не поддерживается, путь:/hacker/api/mef/sca/v1/SCA_ETH_FDFr_EC/5}   -  person Michael H    schedule 04.11.2015
comment
Это потому, что заголовок в REQUEST не Content-Type, а Accept. RESPONSE имеет Content-Type и в выходных данных curl указывается в META-теге, а не в заголовке HTTP (не показан). Попробуйте использовать Accept: application/xml в вашем примере с завитком. Я подозреваю, что ошибка сопоставления типов MIME и DevTools показывает хром, запрашивающий приложение/xml   -  person Roger Willcocks    schedule 04.11.2015
comment
Content-Type и Accept in Header request означают две разные вещи. В этом случае Content-Type отсутствует и, следовательно, имеет значение null. curl -X GET --header Accept: application/json --header Content-Type: application/json xxxx не жалуется   -  person Michael H    schedule 04.11.2015
comment
@MichaelH Не пытайтесь отредактировать ответ, вместо этого отредактируйте свой вопрос, указав дополнительную информацию.   -  person nha    schedule 05.11.2015


Ответы (3)


Хорошо, нашел основную причину этой проблемы благодаря wing328 в проекте swagger codegen на github.

Оказывается, в верхней части нашего файла определения yaml у нас есть:

consumes:
 - application/json

Это заставляет все сгенерированные методы контроллера ожидать application/json для типа содержимого. Это прерывает вызовы API GET, DELETE и PATCH, которые в нашем случае не имеют никакой полезной нагрузки.

person Michael H    schedule 05.11.2015
comment
Потрясающий. Хотя мне кажется странным, что определение типа содержимого сломает вещи, у которых нет тела. Или это ожидаемый тип возврата, ломающий вещи? - person Roger Willcocks; 08.11.2015
comment
он добавляет это в начало вашего класса контроллера: @Controller @RequestMapping(значение = /SCA_ETH_FDFr_EC, потребляет = {APPLICATION_JSON_VALUE}, производит = {APPLICATION_JSON_VALUE}) Это, по-видимому, сообщает всем вашим методам контроллера, что им нужно приложение/json. Мораль этой истории заключается в том, чтобы быть осторожным с вашим yaml... определить потребление/производство в разделах API - person Michael H; 09.11.2015

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

14.17 Тип контента

Поле заголовка объекта Content-Type указывает тип носителя тела объекта, отправленного получателю, или, в случае метода HEAD, тип носителя, который был бы отправлен, если бы запрос был GET. Content-Type = "Content-Type" ":" media-type

Content-Type — это бессмысленный заголовок в контексте запроса. Вдвойне для «GET», поскольку запрос не имеет тела.

Если бы вы могли опубликовать заголовки и тело запроса и ответа как для Chrome, так и для curl, я мог бы решить эту проблему для вас.

Сказав это, просмотр StackOverflow быстро говорит мне, что Spring может вызвать эту ошибку по нескольким причинам, не связанным с кодировкой контента. Например, объект, который вы пытаетесь сериализовать, не имеет конструктора без параметров.

Есть ли у вас какие-либо настройки ведения журнала, которые будут отслеживать InnerExceptions и стек, чтобы увидеть, что такое базовая ошибка?

Вот что делает Spring с моим приложением (не зоомагазином). Хотите ли вы снимки экрана со страницы пользовательского интерфейса swagger для моего приложения?

Спасибо, что посмотрели на этого Роджера.

2015-11-04 18:32:13.977 DEBUG 32416 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/hacker/api/mef/sca/v1/SCA_ETH_FDFr_EC/5]
2015-11-04 18:32:13.977 DEBUG 32416 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /api/mef/sca/v1/SCA_ETH_FDFr_EC/5
2015-11-04 18:32:13.980 DEBUG 32416 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolving exception from handler [null]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'null' not supported
2015-11-04 18:32:13.980 DEBUG 32416 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Invoking @ExceptionHandler method: public void com.ciena.sca.exception.ApiExceptionHandler.handleOtherErrors(java.lang.Exception,javax.servlet.http.HttpServletResponse)
2015-11-04 18:32:13.980 ERROR 32416 --- [nio-8080-exec-1] c.c.sca.exception.ApiExceptionHandler    : Content type 'null' not supported
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'null' not supported
    at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:231)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:349)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:296)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:56)
    at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:299)
    at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1120)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:932)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:299)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:102)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:76)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:217)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
2015-11-04 18:32:13.982 DEBUG 32416 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2015-11-04 18:32:13.982 DEBUG 32416 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Successfully completed request
2015-11-04 18:32:13.983 DEBUG 32416 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/hacker/error]
2015-11-04 18:32:13.983 DEBUG 32416 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /error
2015-11-04 18:32:13.983 DEBUG 32416 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]
2015-11-04 18:32:13.983 DEBUG 32416 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/hacker/error] is: -1
2015-11-04 18:32:13.985 DEBUG 32416 --- [nio-8080-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Written [{timestamp=Wed Nov 04 18:32:13 EST 2015, status=400, error=Bad Request, exception=org.springframework.web.HttpMediaTypeNotSupportedException, message=Content type 'null' not supported, path=/hacker/api/mef/sca/v1/SCA_ETH_FDFr_EC/5}] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@2ed7a433]
2015-11-04 18:32:13.985 DEBUG 32416 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2015-11-04 18:32:13.985 DEBUG 32416 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Successfully completed request
person Roger Willcocks    schedule 04.11.2015
comment
В порядке. первые 3 строчки мне читаются как 1. Начать обработку. 2. Найдите обработчик для этого URL. 3. Ошибка, потому что я не получил сопоставление с обработчиком. Поэтому я бы сказал, что проблема заключается в анализе/маршрутизации URL-адресов. Не могу слишком помочь с этим, так как это будет зависеть от приложения, и вам нужно будет начать раскрывать потенциально важные детали безопасности. - person Roger Willcocks; 05.11.2015

Согласно спецификации HTTP/1.1 ни GET, ни DELETE запросы не должны иметь тела сообщения, а если оно есть, то сервер должен его игнорировать. Я думаю, именно поэтому swagger-UI не отправляет заголовок Content-Type с запросами GET или DELETE.

Подробнее здесь: https://tools.ietf.org/html/rfc2616#section-4.3

person Sarvar Nishonboyev    schedule 25.12.2019