Itoun
Itoun

Reputation: 3226

Android/Kotlin retrofit2.adapter.rxjava2.HttpException: HTTP 415 Unsupported Media Type

I'm just beginning with Retrofit and I got this error when I'm just trying to make a simple POST request

retrofit2.adapter.rxjava2.HttpException: HTTP 415 Unsupported Media Type

I don't know at all where is the mistake.
I have followed this tutorial to implement Retrofit and RxJava.

There is my RetrofitClient implementing my OkHttpClient:

object RetrofitClient {

    private const val CONNECTION_TIMEOUT: Long = 60
    private val BASE_URL = "https://pictalio-dev.azurewebsites.net/api/"

    private var retrofit: Retrofit? = null


    fun getClient(token: String): Retrofit {
        if (retrofit == null) {
            retrofit = Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .client(getOkHttpClient(token))
                    .build()
        }
        return retrofit!!
    }

    private fun getOkHttpClient(token: String): OkHttpClient {
        val okClientBuilder = OkHttpClient.Builder()
        val httpLoggingInterceptor = HttpLoggingInterceptor()
        okClientBuilder.addInterceptor { chain ->
            val original = chain.request()

            val requestBuilder = original.newBuilder()
                    .header("Authorization", token)

            val request = requestBuilder.build()
            chain.proceed(request)
        }
        httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BASIC
        okClientBuilder.addInterceptor(httpLoggingInterceptor)
        okClientBuilder.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
        okClientBuilder.readTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
        okClientBuilder.writeTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
        return okClientBuilder.build()
    }
}

There is my ApiService:

interface ApiService {
    @FormUrlEncoded
    @POST("account/login")
    fun getUserLogin(
            @Field("email") email: String,
            @Field("password") password: String
    ): Single<User>
}

There is how I call and treat my request's return:

class SignInActivity : AppCompatActivity(), View.OnClickListener, RequestSignIn.sendDataResponse, RequestGetUser.sendDataResponse {

    private var user: User? = null
    private var pDialog: SweetAlertDialog? = null
    lateinit var emailEditText: EditText
    lateinit var passwordEditText: EditText
    var persistentUser: PersistentUser? = null
    //Initialization of disposable
    private val disposable = CompositeDisposable()

    private lateinit var apiService: ApiService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.signin_activity)

        emailEditText = findViewById(R.id.email)
        passwordEditText = findViewById(R.id.password)
        //Initialization of apiService
        apiService = RetrofitClient.getClient("").create(ApiService::class.java)
    }

    override fun onClick(v: View) {
        val email: EditText
        val password: EditText

        email = findViewById(R.id.email)
        password = findViewById(R.id.password)
            R.id.login -> {
                if (Tools.isValidEmail(email.text.toString())) {
          disposable.add(apiService.getUserLogin(email.text.toString(), password.text.toString())
                            .subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe({ user ->
                                pDialog!!.dismissWithAnimation()
                                Log.e("USER", "token: " + user.token)

                            }, Throwable::printStackTrace)
                    )
                } else {
                    Toast.makeText(this@SignInActivity, getString(R.string.enter_valid_email), Toast.LENGTH_SHORT).show()
                    //emailnotvalid
                }
            }
        }

    }

    override fun onDestroy() {
        super.onDestroy()
        disposable.dispose()
    }
}

Any kind of help would be appreciated.

EDIT :
I have removed @FormUrlEncoded and add .header("Content-Type", "application/json")
and I'm sending now a JSONObject as Body as follows :

disposable.add(apiService.getUserLogin(JSONObject(map)))  

But I have a 400 BAD REQUEST from my server.
I even added ScalarsConverter to my RetrofitClient.

.addConverterFactory(ScalarsConverterFactory.create())

So I have tried with a RequestBody as follows

fun getUserLogin(@Body requestBody: String): Single<User>
disposable.add(apiService.getUserLogin(RequestBody.create(MediaType.parse("application/json"), JSONObject(map).toString())) . 

and it worked as well! How can I simply send a JSONObject instead of every time create a RequestBody?

Upvotes: 0

Views: 4265

Answers (1)

user2340612
user2340612

Reputation: 10713

Status code 4xx means that there's an error in the request sent by the client; in this specific case 415 means that the Content-Type of your request is not supported by the server.

Checking the URL you provided in your code, the server is expecting an application/json-patch+json MIME type, but you're providing application/x-www-form-urlencoded instead (that's what the @FormUrlEncoded annotation is for).

To fix the issue you should change getUserLogin method to use the appropriate Content-Type (see this answer) and to send the appropriate JSON payload (see this answer, which also tells you how to set a custom header).

Upvotes: 2

Related Questions