The request body is sent as json even though the content type is set as application/x-www-form-urlencoded

Viewed 844

This is related to an existing spring boot question raised by me(Request Body is not properly encoded and hidden when using spring form encoder in Feign Client).

According to this question, we can add either content type in headers or add during request mapping itself as consumes.

So what I did was added content type in headers in the client configuration class

public class EmailClientConfiguration  {
    
    @Bean
    public RequestInterceptor requestInterceptor(Account<Account> account) {
        return template -> {
             template.header("Content-Type", "application/x-www-form-urlencoded");
        };
    }
    
    @Bean
    public OkHttpClient client() {
        return new OkHttpClient();
    }
    
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
    
    @Bean
    public Decoder feignDecoder() {
        return new JacksonDecoder();
    }
    
    @Bean
    public Encoder feignFormEncoder () {
        return new SpringFormEncoder(new JacksonEncoder());
    }
}

and I see in the headers the content type is correctly set as application/x-www-form-urlencoded when the request is sent. But the request body is still sent in json format and also not hidden.

Request Body:

Map<String, String> requestBody = new HashMap<>();
requestBody.put("username", "xyz");
requestBody.put("email", "xyz@gmail.com");
requestBody.put("key", "xxx");

Request Body received in server end:

{"{\n  \"key\" : \"xxx\",\n  \"email\" : \"xyz@gmail.com\",\n  \"username\" : \"xyz\"\n}"

When I add consumes in my request mapping as application/x-www-form-urlencoded

@FeignClient(name = "email", url = "localhost:3000", 
    configuration = EmailClientConfiguration.class)
public interface EmailClient {

    @PostMapping(value = "/email/send", consumes = "application/x-www-form-urlencoded")
    ResponseDto sendEmail(@RequestBody Map<String, String> requestBody);
    
}

it works fine(request body is hidden in server end and also properly encoded). And when I removed the header in the configuration class and adding only consumes works fine without no issues but the vice versa has this problem.

I searched in internet for this and couldn't find any answer.

1 Answers

Feign encodes the request body and parameters before passing the request to any RequestInterceptor (and rightly so). If you do not declare consumes = "application/x-www-form-urlencoded", SprinFormEncoder doesn't know that you're trying to send form data, so it delegates serialization to the inner JacksonEncoder which only does JSON (see for yourself by printing template.body() before setting the header).

Handling such a well-supported header in the interceptor doesn't seem like a good idea, when you already have consumes. If you insist on doing so, you have to provide your own encoder which doesn't rely on the header value and always outputs form-urlencoded data.

Related