node.js - Response body is null even though server is sending a non null response - Stack Overflow

时间: 2025-01-06 admin 业界

I am trying to make an android app using jetpack compose + kotlin for client side and nodejs + express.js for server side to make my RESTApi.

For one of my endpoint:

@POST /getUserIdAndName

My server expects a request body as:

 {
    "id": Int
}

And gives response in the format:

{
    "code": Int,
    "body": [
                {
                    "id": Int,
                    "name": String,
                    "photo": String
                },
                {
                    "id": Int,
                    "name": String,
                    "photo": String
                },
... so on
            ]
}

For my client side I modeled the response in this way:

  1. My main response class :

    data class DetailsResponseDto<T:ResponseBody>(
        val code: Int,
        val body: T?,
        val message: String
    )
    
  2. My Response Body for this endpoint:

    import com.example.hola.core.domain.utils.ResponseBody
    import com.squareup.moshi.Json
    import com.squareup.moshi.JsonClass
    
    data class UserIdAndNameResponse(
        val id : Int?,
        val name : String?,
        val photo: String?
    )
    
    @JsonClass(generateAdapter = true)
    data class UsersList(
        @Json(name = "body")
        val body : List<UserIdAndNameResponse>
    ) : ResponseBody
    
  3. Endpoint:

    @POST("/getUserIdAndName")
    suspend fun getUserIdAndName(@Body myUserId: Map<String, Int>) : DetailsResponseDto<UsersList>
    

    "ResponseBody" is just a marker interface, that I used in my app's architechture so that all different types of response body can have same identity.

    interface ResponseBody
    

When I used it to fetch the data from api it is giving as my response body is null. But at the same time When I checked my server logs, it is giving the correct output. I asked ChatGPT too about this problem but I can't get any solution that should also respect my app's architectural constraints.

Constraints are as follows:

  1. I can't remove "ResponseBody" marker interface to simplify my response data class (ChatGPT suggested this already), because as I said it will break my architecture.

  2. I can't change my server side request/response format, because in other cases where this same endpoint is used I need data in this format only.

How can I solve this problem?

My client side implementation of the endpoint is as follows:

interface GetOtherUsersDetailAbstract {
    suspend fun <D>getUserIdAndNames(myUserId: Int) : Result<D, Error>
}
class EstablishNewConversations : GetOtherUsersDetailAbstract {


    private val TAG = "EstablishNewConversations"
    //Use retrofit to send the request
    val moshi = Moshi.Builder()
        .add(KotlinJsonAdapterFactory())
        .build()

    val api : HolaAppRestApi = Retrofit.Builder()
        .baseUrl(BuildConfig.BASE_URL)
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .build()
        .create()

    override suspend fun <D> getUserIdAndNames(myUserId: Int): com.example.hola.core.domain.utils.Result<D, Error> {
        Log.d(TAG,"Get user id and names called")
        val requestBody = mapOf("id" to myUserId)

        return try{
            val response = api.getUserIdAndName(requestBody)
            Log.d(TAG, "Raw API Response: ${response}")

            if (response.body != null){

                detailResponseToResult(api.getUserIdAndName(requestBody))
                    .mapAResult { usersList: UsersList? ->
                        if (usersList == null) {
                            Log.d(TAG,"Users list is null")
                            UsersList(emptyList()) as D
                        }
                        Log.d(TAG,"Users list is not null: $usersList")

                        usersList as D
                    }

            } else{
                Log.d(TAG,"Response body is null")
                Result.Error(ConversationError.NO_CONVERSATION_FOUND)
            }

        }catch (e:Exception){
            Log.e(TAG,"Error fetching user ID and names: ${e.message}",e)
            Result.Error(ConversationError.UNEXPECTED_ERROR)
        }
    }
}

Logs are like this:

2025-01-04 04:52:19.179 17837-17862 EstablishN...versations com.example.hola                     D  Get user id and names called
2025-01-04 04:52:19.998 17837-17862 EstablishN...versations com.example.hola                     D  Raw API Response: DetailsResponseDto(code=1, body=null, message=Success)
2025-01-04 04:52:19.998 17837-17862 EstablishN...versations com.example.hola                     D  Response body is null

My server side implementation is:

exports.getUserIdAndName = async (req, res, next) => {
  const body = req.body;
  console.log("Got id as : ", body.id);
  console.log("Type is : ", typeof body.id);

  //Constructing base url
  const baseUrl = `${req.protocol}://${req.get("host")}`;

  if (!body.id) {
    return res.status(400).json({
      code: 0,
      body: null,
      error: "Invalid request. 'id' is required.",
    });
  }
  console.log("id is correct");

  try {
    const userIdAndNameList = await UserDetails.getUserIdAndNameList(
      body.id,
      baseUrl
    );

    if (userIdAndNameList.code !== 1) {
      console.log(`returning userList as ${JSON.stringify(userIdAndNameList)}`);
      return res.status(500).json({
        code: userIdAndNameList.code,
        body: null,
        message: userIdAndNameList.message,
      });
    }
    console.log(`returning userList as ${JSON.stringify(userIdAndNameList)}`);

    return res.status(200).json({
      code: 1,
      body: userIdAndNameList.data,
      message: userIdAndNameList.message,
    });
  } catch (err) {
    return res.status(500).json({
      code: mapError("remoteDatabaseErr", "crudOperationErr", "unknownError"),
      body: null,
      message: "Some Unknown Internal error happened",
    });
  }
};