Retrofit Post Request Error – java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $

huangapple go评论77阅读模式
英文:

Retrofit Post Request Error - java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $

问题

我正在尝试使用 Retrofit、MVVM、Coroutines 和 Hilt 发送一个 POST 请求。

在发送 POST 请求后,我得到了以下错误:

java.lang.IllegalStateException: 预期的是对象(BEGIN_OBJECT),但在第 1 行第 1 列找到了字符串(STRING),路径为 $

(我已经搜索过了,但没有找到有帮助的解决方法)

AppModule

@Provides
@Singleton
fun provideRetrofit(gson: Gson): Retrofit = Retrofit.Builder()
    .baseUrl(EndPoints.BASE_URL)
    .client(
        OkHttpClient.Builder().apply {
            if (BuildConfig.DEBUG) {
                val logging = HttpLoggingInterceptor()
                logging.level = HttpLoggingInterceptor.Level.BODY
                addInterceptor(logging)
            }
        }.build()
    )
    .addConverterFactory(ScalarsConverterFactory.create())
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()

@Provides
@Singleton
fun provideApiService(retrofit: Retrofit) = retrofit.create(ApiService::class.java)

ApiService

@POST(EndPoints.REGISTER_WITH_EMAIL)
suspend fun registerUserWithEmail(@Body newUser: NewUser): Response<JsonResponse>

ApiDataSource

suspend fun createNewUserEmail(newUser: NewUser) = apiService.registerUserWithEmail(newUser)

NewUser

data class NewUser(
    @SerializedName("username") val userName: String?,
    @SerializedName("password") val password: String?,
    @SerializedName("email") val email: String?,
    @SerializedName("birthday") val birthday: String?,
    @SerializedName("birth_year") val birthYear: String?,
    @SerializedName("fullname") val fullname: String?,
    @SerializedName("gender") val gender: String?
)

RegisterRepo

suspend fun saveUserWithEmail(newUser: NewUser) = safeApiCall {
    apiDataSource.createNewUserEmail(newUser)
}

RegisterViewModel

// SaveUserDetails
private val _saveDetailsEmail = MutableLiveData<Resource<JsonResponse>>()

fun doRegisterUserEmail(newUser: NewUser) = viewModelScope.launch {
    try {
        _saveDetailsEmail.value = registerRepo.saveUserWithEmail(newUser)
    } catch (exception: Exception) {
        // Handle exception
    }
}

JsonResponse

data class JsonResponse(val success: String, val message: String)

RegisterFragment

val userDetails = NewUser(usernameString, passwordPassed, emailPassed, birthdayPassed, birthYearPassed, fullnameString, genderString)
registerViewModel.doRegisterUserEmail(userDetails)

registerViewModel.saveDetailsEmail.observe(viewLifecycleOwner, Observer {
    when (it.status) {
        Resource.Status.SUCCESS -> {
            if (it.data?.success == "0") {
                // Display error
                Toast.makeText(requireContext(), it.data.message + "Registration", Toast.LENGTH_LONG).show()
            } else if (it.data?.success == "1") {
                // Go to home activity
                Toast.makeText(requireContext(), it.data.message + "Registrationx", Toast.LENGTH_LONG).show()
                val action = DetailsFragmentDirections.actionDetailsFragmentToHomeActivity()
                navController.navigate(action)
            }
        }
        Resource.Status.LOADING -> {
            // Show loading
        }
        Resource.Status.ERROR -> {
            Toast.makeText(requireContext(), it.toString(), Toast.LENGTH_LONG).show()
        }
    }
})

RESOURCE 类更新

data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
    enum class Status {
        SUCCESS,
        ERROR,
        LOADING
    }

    companion object {
        fun <T> success(data: T): Resource<T> {
            return Resource(Status.SUCCESS, data, null)
        }

        fun <T> error(message: String, data: T? = null): Resource<T> {
            return Resource(Status.ERROR, data, message)
        }

        fun <T> loading(data: T? = null): Resource<T> {
            return Resource(Status.LOADING, data, null)
        }
    }
}

这是使用 POSTMAN 测试后的响应,它是正常工作的:

{"success":"1","message":"User details saved"}

UPDATE - 完整 LOGCAT

(这里是日志信息,包含了发送请求和响应的信息)

PHP 代码

(这里是 PHP 代码,用于处理 POST 请求并将数据存储到数据库)

英文:

I am trying to make a Post Request using Retrofit, MVVM, Coroutines and Hilt.

After sending the post request I get this error

java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $

(I have searched but none was helpful)

AppModule

@Provides
@Singleton
fun provideRetrofit(gson: Gson) : Retrofit = Retrofit.Builder()
    .baseUrl(EndPoints.BASE_URL)
    .client(OkHttpClient.Builder().also { client -&gt;
        if (BuildConfig.DEBUG){
            val logging = HttpLoggingInterceptor()
            logging.setLevel(HttpLoggingInterceptor.Level.BODY)
            client.addInterceptor(logging)
        }
       }.build()
    )
    .addConverterFactory(ScalarsConverterFactory.create())
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()

@Provides
@Singleton
fun provideApiService(retrofit: Retrofit) = retrofit.create(ApiService::class.java)

ApiService

@POST(EndPoints.REGISTER_WITH_EMAIL)
suspend fun registerUserWithEmail(@Body newUser: NewUser) : Response&lt;JsonResponse&gt;

ApiDataSource

suspend fun createNewUserEmail(newUser: NewUser) = apiService.registerUserWithEmail(newUser)

NewUser

data class NewUser (
@SerializedName(&quot;username&quot;) val userName: String?,
@SerializedName(&quot;password&quot;) val password: String?,
@SerializedName(&quot;email&quot;) val email: String?,
@SerializedName(&quot;birthday&quot;) val birthday: String?,
@SerializedName(&quot;birth_year&quot;) val birthYear: String?,
@SerializedName(&quot;fullname&quot;) val fullname: String?,
@SerializedName(&quot;gender&quot;) val gender: String?)

RegisterRepo

suspend fun saveUserWithEmail(newUser: NewUser) = safeApiCall { 
apiDataSource.createNewUserEmail(newUser) }

RegisterViewModel

//SaveUserDetails

private val _saveDetailsEmail = MutableLiveData&lt;Resource&lt;JsonResponse&gt;&gt;()

fun doRegisterUserEmail(newUser: NewUser) = viewModelScope.launch {
    try {
        _saveDetailsEmail.value = registerRepo.saveUserWithEmail(newUser)
    }
    catch (exception: Exception){

    }
}

JsonResponse

data class JsonResponse (val success: String, val message: String)

RegisterFragment

val userDetails = NewUser(usernameString, passwordPassed, emailPassed, birthdayPassed, birthYearPassed, fullnameString, genderString)
    registerViewModel.doRegisterUserEmail(userDetails)


    registerViewModel.saveDetailsEmail.observe(viewLifecycleOwner, Observer {
        when(it.status){
            Resource.Status.SUCCESS -&gt; {
                if(it.data?.success == &quot;0&quot;){
                    //Display error
                    Toast.makeText(requireContext(), it.data.message + &quot;Registration&quot;, Toast.LENGTH_LONG ).show()
                }
                else if(it.data?.success == &quot;1&quot;){
                    //go to home activity
                    Toast.makeText(requireContext(), it.data.message + &quot;Registrationx&quot;, Toast.LENGTH_LONG ).show()
                    val action  = DetailsFragmentDirections.actionDetailsFragmentToHomeActivity()
                    navController.navigate(action)
                }
            }
            Resource.Status.LOADING -&gt; {
                //Show loading
            }
            Resource.Status.ERROR -&gt; {
                Toast.makeText(requireContext(), it.toString(), Toast.LENGTH_LONG).show()
            }
        }
    })

}

This is the response after testing with POSTMAN. it was working

{&quot;success&quot;:&quot;1&quot;,&quot;message&quot;:&quot;User details saved&quot;} 

Update RESOURCE class

data class Resource&lt;out T&gt;(val status: Status, val data: T?, val message: String?) {

enum class Status {
    SUCCESS,
    ERROR,
    LOADING
}


companion object {
    fun &lt;T&gt; success(data: T): Resource&lt;T&gt; {
        return Resource(Status.SUCCESS, data, null)
    }

    fun &lt;T&gt; error(message: String, data: T? = null): Resource&lt;T&gt; {
        return Resource(Status.ERROR, data, message)
    }

    fun &lt;T&gt; loading(data: T? = null): Resource&lt;T&gt; {
        return Resource(Status.LOADING, data, null)
    }
}

UPDATE - FULL LOGCAT

    6:01.640 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: --&gt; POST 
   https://2e3045dc760e.ngrok.io/snappmi/account/register/register_with_email.php
    09-20 14:26:01.640 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: Content-Type: application/json
    09-20 14:26:01.640 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: Content-Length: 150
    09-20 14:26:01.640 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: {&quot;birth_year&quot;:&quot;1999&quot;,&quot;birthday&quot;:&quot;1999-09-20&quot;,&quot;email&quot;:&quot;makanaki@gmail.com&quot;,&quot;fullname&quot;:&quot;makanaki&quot;,&quot;gender&quot;:&quot;Male&quot;,&quot;password&quot;:&quot;yellow&quot;,&quot;username&quot;:&quot;maka&quot;}
    09-20 14:26:01.640 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: --&gt; END POST (150-byte body)
    09-20 14:26:01.660 20137-20137/com.snappmi.snappmi W/DisplayListCanvas: DisplayListCanvas is started on unbinded RenderNode (without mOwningView)
    09-20 14:26:01.660 20137-20137/com.snappmi.snappmi W/DisplayListCanvas: DisplayListCanvas is started on unbinded RenderNode (without mOwningView)
    09-20 14:26:08.140 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;-- 200 https://2e3045dc760e.ngrok.io/snappmi/account/register/register_with_email.php (6500ms)
    09-20 14:26:08.140 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: content-type: text/html; charset=UTF-8
    09-20 14:26:08.140 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: date: Sun, 20 Sep 2020 13:27:07 GMT
    09-20 14:26:08.140 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: server: Apache/2.4.43 (Win64) OpenSSL/1.1.1g PHP/7.4.6
    09-20 14:26:08.140 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: x-powered-by: PHP/7.4.6
    09-20 14:26:08.140 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: content-length: 1475
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;b&gt;Notice&lt;/b&gt;:  Undefined index: username in &lt;b&gt;C:\xampp\htdocs\snappmi\account\register\register_with_email.php&lt;/b&gt; on line &lt;b&gt;7&lt;/b&gt;&lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;b&gt;Notice&lt;/b&gt;:  Undefined index: password in &lt;b&gt;C:\xampp\htdocs\snappmi\account\register\register_with_email.php&lt;/b&gt; on line &lt;b&gt;8&lt;/b&gt;&lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;b&gt;Notice&lt;/b&gt;:  Undefined index: email in &lt;b&gt;C:\xampp\htdocs\snappmi\account\register\register_with_email.php&lt;/b&gt; on line &lt;b&gt;9&lt;/b&gt;&lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;b&gt;Notice&lt;/b&gt;:  Undefined index: birthday in &lt;b&gt;C:\xampp\htdocs\snappmi\account\register\register_with_email.php&lt;/b&gt; on line &lt;b&gt;10&lt;/b&gt;&lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;b&gt;Notice&lt;/b&gt;:  Undefined index: birth_year in &lt;b&gt;C:\xampp\htdocs\snappmi\account\register\register_with_email.php&lt;/b&gt; on line &lt;b&gt;11&lt;/b&gt;&lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;b&gt;Notice&lt;/b&gt;:  Undefined index: fullname in &lt;b&gt;C:\xampp\htdocs\snappmi\account\register\register_with_email.php&lt;/b&gt; on line &lt;b&gt;12&lt;/b&gt;&lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;b&gt;Notice&lt;/b&gt;:  Undefined index: gender in &lt;b&gt;C:\xampp\htdocs\snappmi\account\register\register_with_email.php&lt;/b&gt; on line &lt;b&gt;13&lt;/b&gt;&lt;br /&gt;
    09-20 14:26:08.150 20137-20305/com.snappmi.snappmi I/okhttp.OkHttpClient: &lt;br /&gt;
  }

PHP SCODE

if ($_SERVER[&#39;REQUEST_METHOD&#39;]==&#39;POST&#39;){

 $username = $_POST[&#39;username&#39;];
 $password = $_POST[&#39;password&#39;];
 $email = $_POST[&#39;email&#39;];
 $birthday = $_POST[&#39;birthday&#39;];
 $birth_year = $_POST[&#39;birth_year&#39;];
 $fullname = $_POST[&#39;fullname&#39;];
 $gender = $_POST[&#39;gender&#39;];
 $joined = date(&#39;Y/m/d H:i:s&#39;);
 $hashed_password = password_hash($password, PASSWORD_DEFAULT);

if(usernameExists($conn, $username))
 {
     $result[&quot;success&quot;] = &quot;0&quot;;
     $result[&quot;message&quot;] = &quot;Username already taken&quot;;
     echo json_encode($result);
 }

 elseif(!usernameExists($conn, $username)){

      $sql = &quot;INSERT INTO users (username, password, email, birthday, birth_year, fullname, gender, joined) VALUES (?,?,?,?,?,?,?,?)&quot;;

       $stmt= $conn-&gt;prepare($sql);
       $stmt-&gt;execute([$username, $hashed_password, $email, $birthday, $birth_year, $fullname, $gender, $joined]);

       if($stmt){
           $result[&quot;success&quot;] = &quot;1&quot;;
           $result[&quot;message&quot;] = &quot;success&quot;;
           echo json_encode($result);
       }

 }

 else{

      $result[&quot;success&quot;] = &quot;0&quot;;
      $result[&quot;message&quot;] = &quot;An error occured&quot; .mysqli_error($conn);

      echo json_encode($result);
 }

}

答案1

得分: 1

我必须解码发送到服务器的 JSON 数据,特别是在这里内容类型为 application/json 时。

$data = file_get_contents('php://input');
$json_data = json_decode($data, true);
// 我在上面添加了这些

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $username = $json_data["username"];
    $password = $json_data["password"];
    $email = $json_data["email"];
}
英文:

I had to decode the JSON sent to the server especially when the content type here is application/json

$data = file_get_contents(&#39;php://input&#39;);
$json_data = json_decode($data , true);
//I added these above

if ($_SERVER[&#39;REQUEST_METHOD&#39;]==&#39;POST&#39;){

 $username = $json_data[&quot;username&quot;];
 $password = $json_data[&quot;password&quot;];
 $email = $json_data[&quot;email&quot;];

}

huangapple
  • 本文由 发表于 2020年9月20日 16:17:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/63976924.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定