Spring/Eureka/Feign — FeignClient устанавливает заголовок Content-Type в application/x-www-form-urlencoded

Когда я использую FeignClient, он устанавливает Content-Type на application/x-www-form-urlencoded вместо application/json;charset=UTF-8.

Если я использую RestTemplate для отправки того же сообщения, заголовок сообщения Content-Type правильно устанавливается на application/json;charset=UTF-8.

И FeignClient, и RestTemplate используют Eureka для обнаружения служб, и я обнаружил эту проблему путем отладки HTTP-сообщения, полученного сервером.

Контроллер на стороне сервера выглядит так:

@RestController
@RequestMapping("/site/alarm")
public class SiteAlarmController {
    @RequestMapping(method = RequestMethod.POST)
    @ResponseBody
    public ResponseEntity<RaiseAlarmResponseDto> raiseAlarm(@RequestBody RaiseSiteAlarmRequestDto requestDto) {
        ...
    }

Мой FeignClient интерфейс в сервисе, вызывающем будильник, выглядит так:

@FeignClient("alarm-service")
public interface AlarmFeignService {
    @RequestMapping(method = RequestMethod.POST, value = "/site/alarm")
    RaiseAlarmResponseDto raiseAlarm(@RequestBody RaiseSiteAlarmRequestDto requestDto);
}

Заголовки HTTP-сообщений из FeignClient:

Accept: */*
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/1.7.0_60
Host: smit005s-MacBook-Pro.local:9120
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 323

Службе аварийной сигнализации не нравится Content-Type, и она выдает следующее исключение:

2015-04-22 12:12:28.580 thread="qtp1774842986-25" class="org.eclipse.jetty.servlet.ServletHandler" level="WARN" 
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is feign.FeignException: status 415 reading AlarmFeignService#raiseAlarm(RaiseSiteAlarmRequestDto); content:
{"timestamp":1429701148576,"status":415,"error":"Unsupported Media Type","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Unsupported Media Type","path":"/site/alarm"}
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978) ~[spring-webmvc-4.1.5.RELEASE.jar:4.1.5.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857) ~[spring-webmvc-4.1.5.RELEASE.jar:4.1.5.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:618) ~[tomcat-embed-core-8.0.20.jar:8.0.20]
    ...
    ... /* commented rest of stack out */
    ...

Если я изменю код на стороне клиента, чтобы использовать RestTemplate следующим образом:

@Service
public class AlarmService {
    @Autowired
    private RestTemplate restTemplate;
...
    public void send(RaiseSiteAlarmRequestDto alarm) {
        RaiseAlarmResponseDto result = restTemplate.postForObject("http://alarm-service/site/alarm", 
            raiseSiteAlarmRequestDto, RaiseAlarmResponseDto.class);
    }
}

Он работает с RestTemplate, alarm-service получает сообщение и успешно его обрабатывает. Заголовки сообщений, отправленные RestTemplate:

Accept: application/json, application/*+json
Content-Type: application/json;charset=UTF-8
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/1.7.0_60
Host: smit005s-MacBook-Pro.local:9120
Connection: keep-alive
Content-Length: 323

person user1232555    schedule 22.04.2015    source источник
comment
Вы пытались отключить ResponseEntity<T> вашего контроллера, чтобы проверить, не мешает ли это работе Feign Client? Другое мое предположение заключается в том, что Feign не может десериализовать ваш объект RaiseAlarmResponseDto.   -  person jebeaudet    schedule 22.04.2015
comment
@RequestBody на @FeignClient ничего не делает. Можете ли вы сделать успешный звонок без притворства, но с эврикой?   -  person spencergibb    schedule 22.04.2015
comment
Спасибо @spencergibb, это было хорошее предложение. Изменение его на RestTemplate действительно работает, и он обнаруживает службу через Eureka. Когда я устанавливаю отладку на стороне получателя, я вижу, что фиктивный клиент отправляет его с заголовком Content-Type, неправильно установленным на application/x-www-form-urlencoded. В то время как RestTemplate устанавливает Content-Type на application/json;charset=UTF-8. Я обновлю вопрос с этим.   -  person user1232555    schedule 23.04.2015
comment
Вы пытались добавить consumes="application/json" к @RequestMapping на @FeignClient?   -  person spencergibb    schedule 23.04.2015
comment
Да, это работает. Но из примеров, которые я видел, это должно быть ненужным.   -  person user1232555    schedule 23.04.2015
comment
Привет, @spencergib, с простым изменением я могу воссоздать это на примере твоего притворного проекта на gitib. Итак, я должен поднять ошибку против весеннего облака для этого? Тем временем мы можем прогрессировать, используя директивы потребления и производства, как вы предложили.   -  person user1232555    schedule 24.04.2015
comment
@user1232555 user1232555 да, поднимите вопрос, сошлитесь на этот вопрос о переполнении стека и расскажите нам, как вы можете воссоздать это.   -  person spencergibb    schedule 25.04.2015
comment
привет, я думаю, что эта проблема все еще продолжается, планируется ли ее исправить в будущем?   -  person burcakulug    schedule 25.03.2016


Ответы (1)


Ответ заключался в том, чтобы сделать так, как предлагает @spencergibb; используйте директиву consumes в аннотации @RequestMapping на интерфейсе FeignClient. В этой документации Spring/Netflix также есть пример.

Так, например, объявление интерфейса @FeignClient в клиенте теперь выглядит так:

@FeignClient("alarm-service")
public interface AlarmFeignService {
    @RequestMapping(method = RequestMethod.POST, value = "/site/alarm", consumes = "application/json"))
    RaiseAlarmResponseDto raiseAlarm(RaiseSiteAlarmRequestDto requestDto);
}

Обратите внимание, что это необходимо только на стороне клиента, а на контроллере на стороне сервера это изменение не требуется.

Было бы неплохо, если бы это было сделано по умолчанию на @FeignClient, и тогда это соответствовало бы RestTemplate и аннотации серверного контроллера @RequestMapping. Возможно, это можно будет сделать в будущем выпуске spring-cloud.

person user1232555    schedule 24.04.2015
comment
Это не работает для application/x-www-form-urlencoded, вы получите: feign.codec.EncodeException: Не удалось написать запрос: не найдено подходящего HttpMessageConverter для типа запроса [com.*.pojos.OAuthLoginPojo] и типа контента [application /x-www-форма-urlencoded] - person Jorge Machado; 25.05.2017
comment
добавление пользовательского конвертера у меня тоже не работает - person Dan Brandt; 13.11.2019