square / retrofit

A type-safe HTTP client for Android and the JVM
https://square.github.io/retrofit/
Apache License 2.0
43.11k stars 7.3k forks source link

The "Content-Type" is not added to header when use @Headers("Content-Type: application/json") #3198

Closed thachpholly closed 3 years ago

thachpholly commented 5 years ago

I tried to add Content-Type header by @Headers("Content-Type: application/json") but it did not add to header.

@POST("users") @Headers("Content-Type: application/json") Call register(@Body UserRegistrationDto dto);

I worked around by add a Interceptor:

public class MyInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); //work around if(request.body().contentType() != null){ request = request.newBuilder() .addHeader("Content-Type", request.body().contentType().toString()).build(); } Response response = chain.proceed(request); return response; } }

The cause is Content-Type is not add to header in case body is not null . Thank for support!

JakeWharton commented 5 years ago

We have a test that validate this works as expected:

https://github.com/square/retrofit/blob/8c93b59dbc57841959f5237cb141ce0b3c18b778/retrofit/src/test/java/retrofit2/RequestFactoryTest.java#L2715-L2726

Please provide a failing test case or executable sample that demonstrates the problem.

qs-lll commented 5 years ago

same wrong with me

sorry not same wrong

Sash0k commented 3 years ago

seems, i have reproduced it after upgrading okhttp version: 3.14.9 -> 4.9.1

cases:

okhttp has interceptors for auth, and rebuilds headers. Init section:

    @Singleton
    fun provideOkHttpClient(interceptor: Interceptor, authenticator: Authenticator): OkHttpClient {
        val client = OkHttpClient.Builder()
        client.connectTimeout(15, TimeUnit.SECONDS)
        client.readTimeout(30, TimeUnit.SECONDS)
        client.authenticator(authenticator)
        client.addInterceptor(interceptor)
        if (BuildConfig.DEBUG) {
            val loggingInterceptor = HttpLoggingInterceptor()
            loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
            client.addInterceptor(loggingInterceptor)
        }
        return client.build()
    }

UPDATE My bad, header is exist but replaced inside Interceptor

    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        final String auth = "Authorization";

        Request request = chain.request();  // WOW: we have Content-Type: text/plain here in GET-request, but not in POST
        String token = mAuthController.getToken(request.url().encodedPath(), request.method());
        request = request
                .newBuilder()
                .headers(Headers.of(super.headers)
                        .newBuilder()
                        .removeAll(auth)
                        .addUnsafeNonAscii(auth, token)
                        .build())
                .build();
        return chain.proceed(request);
    }

But it's look for me as interesting behaviour, retrofit @Headers intercepted in GET, but not in POST. I suppose, it added somewhere later, and, as result, POST-request works.

JakeWharton commented 3 years ago

Without an executable test case that shows the incorrect behavior I am going to close. As I stated above, we have a test case which shows that this is working.

rishujam commented 1 year ago

@thachpholly If content type is not added in header than where? I am getting same issue in POST methods