android - 改造:删除 Content-Length 标头
问题描述
我正在使用 Retrofit(2.9.0 版)向 API 服务器发出请求。默认情况下,此库将Content-Length
标头添加到请求中。不幸的是,出于“安全”目的,服务器拒绝了此标头-我对此没有影响。是否可以使用 Retrofit 从请求中删除此标头?还是我必须创建自己的 rest api 客户端?
removeHeader("Content-Length")
的功能Request.Builder
不起作用,看起来库在请求构建后添加了它。我可以更改此标头调用的值,但这addHeader("Content-Length", "any-value")
不是我需要的。
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder().apply {
removeHeader("Content-Length") // Doesn't works
}
return chain.proceed(builder.build())
}
#编辑
对于下面的人。我已经通过日志拦截器确认了这一点,因此无需在这里编写无用的代码。上面的功能来自... Interceptor
...但是如果您愿意,那可以提供帮助:
标头拦截器
class HeaderInterceptor() : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder().apply {
removeHeader("Content-Length") // Doesn't works
}
return chain.proceed(builder.build())
}
}
建筑改造客户
@Singleton
@Provides
fun providesUserApi(headerInterceptor: HeaderInterceptor): UsersApi {
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
val client = OkHttpClient.Builder().apply {
addInterceptor(tokenInterceptor)
addInterceptor(headerInterceptor)
}.build()
return Retrofit.Builder()
.baseUrl(Constants.BASE_USERS_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client).build()
.create(UsersApi::class.java)
}
API接口
interface UsersApi {
@POST("/api/auth/login")
suspend fun login(@Body user: LoginRequest): Response<LoginResponse>
}
LoginRequest
是简单的 POJO 数据类,它包含登录/密码字段,LoginResponse
是服务器返回的数据。
日志猫
I/okhttp.OkHttpClient: --> POST https://this_is_api_server.com/api/auth/login
I/okhttp.OkHttpClient: Content-Length: 60
I/okhttp.OkHttpClient: {"device_type":"ANDROID","email":"admin","password":"admin"}
I/okhttp.OkHttpClient: --> END POST (60-byte body)
I/okhttp.OkHttpClient: <-- HTTP FAILED: java.net.SocketException: socket failed: EPERM (Operation not permitted)
来自客户端的事件日志
我用XXX屏蔽了域名和IP地址。
I/okhttp.OkHttpClient: [2 ms] callStart: Request{method=POST, url=https://securedapiserver.com/api/auth/v1/loginByEmail, tags={class retrofit2.Invocation=package.name.api.UsersApi.login() [LoginRequest(device_type=ANDROID, email=test, password=test)]}}
I/okhttp.OkHttpClient: --> POST https://securedapiserver.com/api/auth/v1/loginByEmail
I/okhttp.OkHttpClient: Content-Length: 58
I/okhttp.OkHttpClient: {"device_type":"ANDROID","email":"test","password":"test"}
I/okhttp.OkHttpClient: --> END POST (58-byte body)
I/okhttp.OkHttpClient: [98 ms] proxySelectStart: https://securedapiserver.com/
I/okhttp.OkHttpClient: [103 ms] proxySelectEnd: [DIRECT]
I/okhttp.OkHttpClient: [105 ms] dnsStart: securedapiserver.com
I/okhttp.OkHttpClient: [158 ms] dnsEnd: [securedapiserver.com/XXX.XXX.XXX.XXX, securedapiserver.com/XXX.XXX.XXX.XXX, securedapiserver.com/XXX.XXX.XXX.XXX, securedapiserver.com/XXX.XXX.XXX.XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX]
I/okhttp.OkHttpClient: [175 ms] connectStart: securedapiserver.com/XXX.XXX.XXX.XXX:443 DIRECT
I/okhttp.OkHttpClient: [176 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [178 ms] connectStart: securedapiserver.com/XXX.XXX.XXX.XXX:443 DIRECT
I/okhttp.OkHttpClient: [179 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [180 ms] connectStart: securedapiserver.com/XXX.XXX.XXX.XXX:443 DIRECT
I/okhttp.OkHttpClient: [181 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [182 ms] connectStart: securedapiserver.com/XXX.XXX.XXX.XXX:443 DIRECT
I/okhttp.OkHttpClient: [183 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [184 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [184 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [186 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [187 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [188 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [189 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [191 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [194 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [196 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [197 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [198 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [199 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [201 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [201 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [202 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [203 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/System.out: [java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted)]
I/okhttp.OkHttpClient: <-- HTTP FAILED: java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [212 ms] callFailed: java.net.SocketException: socket failed: EPERM (Operation not permitted)
E/NetworkCall: socket failed: EPERM (Operation not permitted)
** 编辑 2021-03-19 **
感谢@Yuri Schimke,问题得到了解决。我不得不完全删除模拟器并创建一个新的。看起来http堆栈有问题?
解决方案
java.net.SocketException: socket failed: EPERM (Operation not permitted)
看起来像客户端错误。Android 操作系统阻止您的应用发送网络请求。您应该检查您是否已授予您的应用程序的 INTERNET 权限。您可能对 stacktrace 有一个例外,您可以将其包含在内以确认这一点,而没有看到我们通常在这里猜测。
要确认标头,请查看添加 OkHttp 网络拦截器并在需要时进行更改https://square.github.io/okhttp/interceptors/
或者只是从使用日志拦截器来确认您的怀疑开始https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(Level.HEADERS);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.build();
要确认这是客户端套接字错误,还是来自服务器的有效 400 响应,您应该使用此方法https://stackoverflow.com/a/66398516/1542667来记录连接事件。
[0 ms] callStart: Request{method=GET, url=https://httpbin.org/get}
[11 ms] proxySelectStart: https://httpbin.org/
[12 ms] proxySelectEnd: [DIRECT]
[12 ms] dnsStart: httpbin.org
[55 ms] dnsEnd: [httpbin.org/54.147.165.197, httpbin.org/34.231.30.52, httpbin.org/54.91.118.50, httpbin.org/18.214.80.1, httpbin.org/54.166.163.67, httpbin.org/34.199.75.4]
[62 ms] connectStart: httpbin.org/54.147.165.197:443 DIRECT
[176 ms] secureConnectStart
[747 ms] secureConnectEnd: Handshake{tlsVersion=TLS_1_2 cipherSuite=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 peerCertificates=[CN=httpbin.org, CN=Amazon, OU=Server CA 1B, O=Amazon, C=US, CN=Amazon Root CA 1, O=Amazon, C=US] localCertificates=[]}
[765 ms] connectEnd: h2
[767 ms] connectionAcquired: Connection{httpbin.org:443, proxy=DIRECT hostAddress=httpbin.org/54.147.165.197:443 cipherSuite=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 protocol=h2}
[775 ms] requestHeadersStart
[783 ms] requestHeadersEnd
[993 ms] responseHeadersStart
[994 ms] responseHeadersEnd: Response{protocol=h2, code=200, message=, url=https://httpbin.org/get}
[999 ms] responseBodyStart
[999 ms] responseBodyEnd: byteCount=267
[999 ms] connectionReleased
[1000 ms] callEnd
推荐阅读
- python - 画布线未出现在屏幕上
- c# - 在视图模型中验证域名
- node.js - Nodejs 集群中的 PostgreSQL 连接池
- jmeter - 通过 JMeter JSR223 采样器表单提供参数的正确语法是什么?
- lexicon - 如何在 Liferay 7.2 中实现 Clay
- javascript - MxCodec 解码功能不适用于 XML
- sequelpro - 在服务器之间使用 sequel pro 复制数据库和表的最快方法
- vb.net - Visual Studio 2019“无法从 packages.config 解析版本值 '1.0.0-preview2'”
- flutter - 参数不匹配的闭包调用:函数'_myListView.functionOne'
- build - 在 CMake 中,如何按顺序运行依赖项或目标?