Spring/Eureka/Feign - FeignClient setting Content-Type header to application/x-www-form-urlencoded
Asked Answered
U

2

8

When I use a FeignClient it is setting the Content-Type to application/x-www-form-urlencoded instead of application/json;charset=UTF-8.

If I use a RestTemplate to send the same message the message header Content-Type is correctly set to application/json;charset=UTF-8.

Both the FeignClient and RestTemplate are using Eureka for service discovery, and I discovered this problem by debugging the HTTP message received by the server.

The controller on the server side looks like this:

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

My FeignClient interface in the service that calls the alarm looks like this:

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

The HTTP message headers from the FeignClient are:

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

The alarm service doesn't like the Content-Type and throws the following exception:

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 */
    ...

If I change the client side code to use a RestTemplate as follows:

@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);
    }
}

It works with the RestTemplate, the alarm-service receives the message and processes it successfully. The message headers sent by the RestTemplate are:

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
Uralite answered 22/4, 2015 at 12:53 Comment(8)
Have you tried ditching the ResponseEntity<T> of your controller to test if this was interfering with the Feign Client? My other guess is that Feign is not able to deserialize your RaiseAlarmResponseDto object.Cohe
The @RequestBody on the @FeignClient doesn't do anything. Can you make a successful call without feign, but with eureka?Tman
Thanks @Tman that was a good suggestion. Changing it to a RestTemplate does work, and it is discovering the service through Eureka. When I put debugging on the receiver side I can see the the feign client is sending it with the Content-Type header set incorrectly to application/x-www-form-urlencoded. While the RestTemplate sets the Content-Type to application/json;charset=UTF-8. I will update the question with this.Uralite
Have you tried adding consumes="application/json" to the @RequestMapping on the @FeignClient?Tman
Yes that works. But from the examples I've seen that should be unnecessary.Uralite
Hi @spencergib with a simple change I can recreate this on a feign example project of yours on githib. So should I raise a bug against spring-cloud for this? In meantime we can progress by using the consumes and produces directives as you suggested.Uralite
@Uralite yes, raise an issue, reference this stack overflow question and tell us how you can recreate this.Tman
hi, i think this issue is still going on, is it planned to be fixed in the future?Moraine
U
21

The answer was to do as @spencergibb suggests; use the consumes directive in the @RequestMapping annotation on the FeignClient interface. This Spring/Netflix documentaition also has an example.

So for example the @FeignClient interface declaration in the client is now:

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

Note this is only necessary on the client side and the server side controller does not need to have this change.

Would be nice if this was done by default on the @FeignClient and then it would be the consistent with RestTemplate and the server side controller @RequestMapping annotation. Maybe that can be done in a future release of spring-cloud.

Uralite answered 24/4, 2015 at 10:47 Comment(2)
It does not work for application/x-www-form-urlencoded you will get : 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]Internalize
adding custom converter doesn't work for me tooLuxemburg
B
-1

This is applicable for spring boot version 3:

The default Content-Type used by Feign is application/x-www-form-urlencoded, which is why you're seeing this behavior. To change the Content-Type to application/json;charset=UTF-8, you can configure Feign to use a different encoder.

Configure Feign to use the JacksonEncoder by creating a custom FeignConfig class and annotating it with @Configuration:

@Configuration
public class FeignConfig {
    @Bean
    public Encoder feignEncoder() {
        return new JacksonEncoder();
    }
}
Bluejacket answered 17/7, 2023 at 9:55 Comment(1)
The encoder seems to determine just the actual body content format, not the presence of the Accept or Content-Type header.Keli

© 2022 - 2024 — McMap. All rights reserved.