liujingxing / rxhttp

🔥🔥🔥 Based on OkHttp encapsulation, support Kotlin Coroutines、RxJava2、RxJava3; 30s to get started.
https://juejin.im/post/5ded221a518825125d14a1d4
Apache License 2.0
3.75k stars 458 forks source link

服务器返回数据特殊处理 #490

Closed hujxhed closed 6 months ago

hujxhed commented 6 months ago

服务器数据返回规则: 请求成功:直接返回对象 { "userId": "1", "name": "2", "age": 3 } 请求失败:返回 { "code":"E1001", "msg":"密码错误" }

这样一来就不能用之前的Result来解析:

public class Result<T> {
    public int code;
    public String msg;
    public T data;
}

我在ResultParser是这样写的:

@Parser(name = "Result", wrappers = [PageList::class])
open class ResultParser<T> : TypeParser<T> {

    protected constructor() : super()
    constructor(type: Type?) : super(type)

    @Throws(IOException::class)
    override fun onParse(response: okhttp3.Response): T {
        val str = response.body()?.string().str()
        val result: T
        if (response.isSuccessful) {
            result = JSONObject.parseObject(str) as T
        } else {
            val mBaseError = JSONObject.parseObject(str, BaseError::class.java)
            ToastUtils.showLong(mBaseError.msg.str())
            throw ParseException(mBaseError.code.str(), mBaseError.msg.str(), response)
        }
        return result
    }
}
class BaseError : Serializable {
    var code: String? = null
    var msg: String? = null
}

使用:

RxHttp.postForm(AppConfig.URL_LOGIN)
      .toObservableResult(UserInfo::class.java)
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe({ userInfo ->
          Log.e(mTag, "userinfo=$userInfo")
      }, { throwable ->
          Log.e(mTag, "报错:" + throwable.toString())
      })

这样解析出来报错: java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to com.zj.drone.bean.UserInfo

hujxhed commented 6 months ago

我看网上这样写的: inline fun <reified T> getObject(json: String): T = Gson().fromJson(json, T::class.java) 但是这个调用的时: val result: UserInfo = Gson().fromJson(str) 不是用的T,用的是UserInfo,但是在这里我不能写死UserInfo,要不然只能解析Userinfo 我想要的是: val result: T = Gson().fromJson(str) 能实现吗?

liujingxing commented 6 months ago

这样估计更适合你

open class BaseError : Serializable {
    var code: String? = null
    var msg: String? = null
}

class UserInfo : BaseError {
    var userId : Int = 0
    val name: String? = null
    val age : Int = 0
}

此时不需要自定义解析器

RxHttp.postForm(AppConfig.URL_LOGIN)
      .toObservable<UserInfo>()
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe({ userInfo ->
          if (userInfo.code != null) {
             //处理错误逻辑
          } else {
          }
          Log.e(mTag, "userinfo=$userInfo")
      }, { throwable ->
          Log.e(mTag, "报错:" + throwable.toString())
      })

当然,如果你想在自定义解析器里统一判断code,可以这么写

@Parser(name = "Response")
open class ResponseParser<T:BaseError> : TypeParser<T> {
    protected constructor() : super()
    constructor(type: Type) : super(type)

    @Throws(IOException::class)
    override fun onParse(response: okhttp3.Response): T {
        val data: T = response.convert(types[0])
        if (data.code != null) {
            throw ParseException(data.code, data.msg, response)
        }
        return data
    }
}

此时发请求

RxHttp.postForm(AppConfig.URL_LOGIN)
      .toObservableResponse<UserInfo>()
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe({ userInfo ->
          Log.e(mTag, "userinfo=$userInfo")
      }, { throwable ->
          //这里拿到code、msg处理错误逻辑,errorCode、errorMsg是Throwable对象的扩展字段,demo里可以找到
          val code = throwable.errorCode
          val msg = throwable.errorMsg
          Log.e(mTag, "报错:" + throwable.toString())
      })
hujxhed commented 6 months ago

感谢大神,你这个方法可以实现,但每个Bean都需要继承BaseError 我现在是这样做的,目前看没有问题,但不保证,你帮我看看有没有什么隐患?

@Parser(name = "Result", wrappers = [PageList::class])
open class ResultParser<T> : TypeParser<T> {

    private lateinit var type: Type

    //该构造方法是必须的
    protected constructor() : super()

    //如果依赖了RxJava,该构造方法也是必须的
    constructor(type: Type) : super(type) {
        this.type = type
    }

    @Throws(IOException::class)
    override fun onParse(response: okhttp3.Response): T {
        var str = response.body()?.string().str()
        val result: T
        if (response.isSuccessful) {
            //更新token
            val token = response.header("zzb-access-token").str()
            if (!token.isNullOrBlank()) {
                BaseToken.setToken(token)
            }

            /*if (type.typeName == String::class.java.name) {
                Log.e("Hujx", "是String类型的!")
            }*/

            if (str.isBlank() && type.typeName != String::class.java.name) {
                str = "{}"
            }

            result = JSONObject.parseObject(str, type) as T
        } else {
            val mBaseError = JSONObject.parseObject(str, BaseError::class.java)
            ToastUtils.showLong(mBaseError.msg.str())
            throw ParseException(mBaseError.code.str(), mBaseError.msg.str(), response)
        }
        return result
    }

}
hujxhed commented 6 months ago

我用RxJava,是不是一定会调用下面这个构造方法? constructor(type: Type) : super(type) { this.type = type } 我把type存起来,在下面使用,会不会有问题?

hujxhed commented 6 months ago

我看了下你刚才给发的代码,我这样好像是画蛇添足了,直接用types[0]就能拿到T的实际类型了

@Parser(name = "Result", wrappers = [PageList::class])
open class ResultParser<T> : TypeParser<T> {

    protected constructor() : super()

    constructor(type: Type) : super(type)

    @Throws(IOException::class)
    override fun onParse(response: okhttp3.Response): T {
        var str = response.body()?.string().str()
        val result: T
        if (response.isSuccessful) {
            //更新token
            val token = response.header("zzb-access-token").str()
            if (!token.isNullOrBlank()) {
                BaseToken.setToken(token)
            }

            /*if (type.typeName == String::class.java.name) {
                Log.e("Hujx", "是String类型的!")
            }*/

            if (str.isBlank() && types[0].typeName != String::class.java.name) {
                str = "{}"
            }

            result = JSONObject.parseObject(str, types[0]) as T
        } else {
            val mBaseError = JSONObject.parseObject(str, BaseError::class.java)
            ToastUtils.showLong(mBaseError.msg.str())
            throw ParseException(mBaseError.code.str(), mBaseError.msg.str(), response)
        }
        return result
    }

}

直接这样用就OK了,是吧?

liujingxing commented 6 months ago

可以,但你自己解析数据,会导致请求响应的日志不会输出