升级Retrofit2遇到的POST请求中Content-Type字段问题

本文介绍了在将Retrofit从1.9升级到2.3时遇到的POST请求Content-Type变化的问题。1.9版本的Content-Type包含charset,而2.3版本则不包含。为解决此问题,可以通过在接口声明中添加@FormUrlEncoded注解。解释了Retrofit2在生成调用时如何处理接口注解,并详细阐述了解决过程中涉及的ServiceMethod、RequestBuilder、RequestBody等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近将项目中的Retrofit由1.9升级到2.3版本。
在没有更改请求配置的情况下,发现请求头的Content-Type不一样了。

通过抓包查看请求,
1.9版本POST请求的Header字段

Content-Type:application/x-www-form-urlencoded; charset=utf-8

2.3版本POST请求Header字段

Content-Type:application/x-www-form-urlencoded

先说解决办法:
在Interface声明中添加注解

@Headers("Content-Type:application/x-www-form-urlencoded; charset=utf-8")

例如原有代码

@FormUrlEncoded
@POST("mobileapi")
Observable<Result<YourData>> queryData(@Field("pageNo") Integer num);

变为

@FormUrlEncoded
@POST("mobileapi")
@Headers("Content-Type:application/x-www-form-urlencoded; charset=utf-8")
Observable<Result<YourData>> queryData(@Field("pageNo") Integer num);

原因:
对于此处的POST请求,在Retrofit1.9中通过FormUrlEncodedTypedOutput类获取Content-Type

@Override public String mimeType() {
  return "application/x-www-form-urlencoded; charset=UTF-8";
}

而Retrofit2.3中,通过FormBody类获取Content-Type

private static final MediaType CONTENT_TYPE =
    MediaType.parse("application/x-www-form-urlencoded");

因此原有代码的情况下,1.9获取的默认值带有charset,2.3版本则没有。

为何在添加注解后就可以改变Header字段了呢?
在Retrofit2生成调用时:
1. 通过ServiceMethod类的parseParameterAnnotation方法处理接口注解
2. 当发现注解声明了Content-Type,会读取并存储其字段
3. 建立请求时,通过RequestBuilder类进行,将参数传递给Builder生成request
4. requestBuilder根据FormUrlEncode注解,会默认生成FormBody类型的body
5. requestBuilder判断第一步得到的content-type是否为空,此时不为空,则新建ContentTypeOverridingRequestBody覆盖body变量
6. 发起请求时,进入okhttp3的RealCall类处理
7. 调用getResponseWithInterceptionChain,按顺序添加用户定义的拦截器,以及okhttp内部拦截器
8. 内部拦截器BridgeInterceptor执行,调用body变量读取Content-Type
9. 根据第5步处理,由于接口声明了Content-Type,这里获取到的是ContentTypeOverridingRequestBody类型的body,返回了用户指定的字段,因此加上了charset

<think>好的,用户想知道如何通过Android发送POST请求登录新大陆物联网云平台获取Token,用于物联网竞赛的数据通信工程。我需要先理清楚登录流程和需要的参数。 首先,根据引用[1],设备是通过发送Mac码进行鉴权握手,成功后会显示在线。不过这里用户需要的是登录获取Token,可能需要用户名、密码等凭证。通常,云平台的登录API会要求POST请求,携带JSON格式的账号密码。 接下来,要确定API的URL。假设新大陆平台的登录接口可能是类似https://api.nl-iot.com/auth/login这样的路径。需要查阅官方文档确认,但用户可能无法访问,所以给出示例。 然后,请求头需要设置Content-Type为application/json。请求体包含username和password,可能还有clientType,比如Android。服务器返回的Token通常在响应体的某个字段,比如data.token。 在Android中,可以使用OkHttp或Retrofit库发送POST请求。这里选OkHttp作为示例,因为比较基础易懂。需要添加依赖,然后编写异步请求代码,处理成功和失败的情况。 权限方面,记得添加INTERNET权限。异常处理也很重要,比如网络不可用或JSON解析错误。 用户可能在竞赛中需要快速实现,所以代码示例要简洁,关键步骤注释清楚。同时提醒替换实际参数,比如URL、用户名和密码。可能还需要处理返回的Token存储,用于后续请求的鉴权。 最后,生成相关问题,帮助用户深入理解,比如参数说明、超时处理、Token刷新和错误码。这些扩展问题能帮助他们在遇到问题时进一步排查。</think>### Android使用POST请求登录新大陆物联网云平台获取Token的步骤解析 #### 一、接口分析 根据引用[1]的鉴权流程,新大陆物联网平台采用基于Token的鉴权机制。登录接口需要以下参数: - URL:通常为`https://api.nl-iot.com/auth/login`(具体以平台文档为准) - 请求头:`Content-Type: application/json` - 请求体:JSON格式的账号凭证 请求体示例: ```json { "username": "竞赛账号", "password": "加密密码", "clientType": "Android" } ``` #### 二、Android实现代码 使用OkHttp3库实现POST请求: ```kotlin // build.gradle添加依赖 implementation 'com.squareup.okhttp3:okhttp:4.9.3' // AndroidManifest.xml添加权限 <uses-permission android:name="android.permission.INTERNET"/> // 核心请求代码 val client = OkHttpClient() val json = MediaType.parse("application/json; charset=utf-8") val requestBody = RequestBody.create(json, """ { "username": "your_username", "password": "your_password", "clientType": "Android" } """.trimIndent()) val request = Request.Builder() .url("https://api.nl-iot.com/auth/login") .post(requestBody) .build() client.newCall(request).enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { val responseData = response.body()?.string() // 解析示例响应:{"code":200,"data":{"token":"eyJhb..."},"msg":"success"} val jsonObject = JSONObject(responseData) if (jsonObject.getInt("code") == 200) { val token = jsonObject.getJSONObject("data").getString("token") // 保存Token到SharedPreferences } } override fun onFailure(call: Call, e: IOException) { // 处理网络异常 } }) ``` #### 三、关键注意点 1. 加密处理:建议对密码进行MD5或SHA256加密后传输[^1] 2. 异步处理:必须使用子线程执行网络请求 3. Token存储:建议使用Android Keystore或EncryptedSharedPreferences 4. 错误处理:需处理`JSONException`和`IOException` #### 四、响应处理 成功响应示例: ```json { "code": 200, "data": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expire": 7200 }, "msg": "success" } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值