Spring/Eureka/Feign - FeignClient将Content-Type头设置为application/x-www-form-urlencoded

8
当我使用FeignClient时,它会将Content-Type设置为application/x-www-form-urlencoded,而不是application/json;charset=UTF-8
如果我使用RestTemplate发送相同的消息,则消息头Content-Type会正确设置为application/json;charset=UTF-8FeignClientRestTemplate都使用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);
}
FeignClient 的 HTTP 消息头如下:
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

@FeignClient 上的 @RequestBody 没有任何作用。你能在不使用 Feign,但使用 Eureka 的情况下成功调用吗? - spencergibb
感谢@spencergibb提供的好建议。将其更改为RestTemplate确实有效,并且它通过Eureka发现了该服务。当我在接收方上打开调试时,我可以看到feign客户端将其发送并将“Content-Type”标头错误地设置为“application/x-www-form-urlencoded”。而RestTemplate将“Content-Type”设置为“application/json;charset=UTF-8”。我会更新问题并说明这一点。 - user1232555
你尝试在@FeignClient@RequestMapping上添加consumes="application/json"了吗? - spencergibb
嗨@spencergib,通过简单的更改,我可以在您的githib上重新创建此问题的feign示例项目。那么,我应该针对spring-cloud提出错误吗?与此同时,我们可以按照您建议的使用consumes和produces指令来进行进展。 - user1232555
@user1232555 是的,请提出一个问题,引用这个 Stack Overflow 的问题,并告诉我们您如何重现它。 - spencergibb
显示剩余3条评论
2个回答

21

答案是按照@spencergibb的建议做; 在FeignClient接口的@RequestMapping注释中使用consumes指令。这个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版本中实现这一点。

1
它不适用于application/x-www-form-urlencoded,您将会得到以下错误信息:feign.codec.EncodeException: Could not write request: no suitable HttpMessageConverter found for request type [com.*.pojos.OAuthLoginPojo] and content type [application/x-www-form-urlencoded]。 - Jorge Machado
添加自定义转换器对我也没有用。 - Dan Brandt

1

适用于Spring Boot版本3

Feign默认使用的Content-Type是application/x-www-form-urlencoded,这就是为什么您看到这种行为。要将Content-Type更改为application/json;charset=UTF-8,您可以配置Feign使用不同的编码器。

通过创建一个自定义的FeignConfig类并使用@Configuration进行注解,来配置Feign使用JacksonEncoder

@Configuration
public class FeignConfig {
    @Bean
    public Encoder feignEncoder() {
        return new JacksonEncoder();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接