改造:如何发送带有常量字段的 POST 请求?
Posted
技术标签:
【中文标题】改造:如何发送带有常量字段的 POST 请求?【英文标题】:Retrofit: How to send a POST request with constant fields? 【发布时间】:2014-12-29 02:16:23 【问题描述】:我想发送一个带有一个实际参数的简单 POST 请求:
@POST("/token")
@FormUrlEncoded
void extendSession(@Field("refresh_token")final String refreshToken);
但是这个请求还应该发送一些服务器请求的常量值,例如client_id
、client_secret
和grant_type
,它们是常量,不应该是应用程序API的一部分。
最好的方法是什么?
【问题讨论】:
【参考方案1】:这取决于你的方法。如果您有常量,则可以构建调用所需的默认值 Map。 @FieldMap
适合构建包含所有必填字段的地图
private void extendSession(String token)
Map params = buildDefaultParams();
params.put("refreshToken", token);
getRestAdapter().create(MyApi.class).extendsSession(params);
private Map buildDefaultParams()
Map defaults = new HashMap();
defaults.put("client_id", CLIENT_ID);
defaults.put("client_secret", CLIENT_SECRET);
defaults.put("grant_type", GRANT_TYPE);
return defaults;
/**then you change your interface to this **/
@POST("/token")
@FormUrlEncoded
void extendSession(@FieldMap() Map refreshToken);
【讨论】:
这就是我最终要做的,虽然它不是一个“优雅”的解决方案。 Github 上有一个未决问题,我也将这篇文章链接到那里。 github.com/square/retrofit/issues/951【参考方案2】:一个有点老的问题,但在我自己考虑了一段时间之后,这就是我想出的。我很想听听任何想法!
按照标准,我在一个名为 AuthWebservice 的接口中定义了我的 @POST:
interface AuthWebservice
@POST("oauth/token")
@FormUrlEncoded
fun refreshToken(
@Field("grant_type")
grantType: GrantType,
@Field("client_id")
clientId: String,
@Field("client_secret")
clientSecret: String,
@Field("refresh_token")
refreshToken: String
): Call<AccessToken>
[注意:我使用 Dagger 进行依赖注入,但无论您在哪里实例化您的 Web 服务,以下逻辑都可以工作]
在我的 NetworkModule 中,我有以下获取 AuthWebservice 的实例:
@Module
class NetworkModule
...
@Provides
@Singleton
fun providesAuthWebservice(
retrofit: Retrofit
): AuthWebservice = retrofit.create(AuthWebservice::class.java)
...
这是我的解决方案: 我还在 AuthWebservice 中包含以下方法定义:
fun refreshToken(refreshToken: String): Call<AccessToken>
请注意,没有任何类型的注释,并且该方法返回的数据类型与包含所有参数的版本相同。这会编译,但如果你尝试调用它,它显然会在运行时失败,类似于以下内容:
java.lang.IllegalArgumentException:需要 HTTP 方法注释 (例如,@GET、@POST 等)。 对于方法 AuthWebservice.refreshToken
现在我创建了一个名为AuthWebserviceWrapper
的类,它采用AuthWebservice
的实例。在大多数情况下,它只是调用基础实例上的相应方法,except 用于我刚刚在上面添加的方法:
class AuthWebserviceWrapper(private val base: AuthWebservice) : AuthWebservice
// Just call the base method.
override fun refreshToken(
grantType: GrantType,
clientId: String,
clientSecret: String,
refreshToken: String
): Call<AccessToken> = base.refreshToken(
grantType,
clientId,
clientSecret,
refreshToken)
// Call the base method with defaults!
override fun refreshToken(refreshToken: String): Call<AccessToken> =
base.refreshToken(
GrantType.REFRESH_TOKEN, // Default value
BuildConfig.MY_CLIENT_ID, // Default value
BuildConfig.MY_CLIENT_SECRET, // Default value
refreshToken
)
最后,回到 NetworkModule
,我将 Retrofit 的默认实现包装成这样:
@Module
class NetworkModule
...
@Provides
@Singleton
fun providesAuthWebservice(
retrofit: Retrofit
): AuthWebservice = AuthWebserviceWrapper(retrofit.create(AuthWebservice::class.java))
...
现在,当我调用 refreshToken
时,我会得到带有默认值的方法:
class MyClass @Inject constructor(authWebservice: AuthWebservice)
fun doSomething(refreshToken: String)
val call = authWebservice.refreshToken(refreshToken)
这当然会引入一些样板,我不喜欢这些样板,但我认为最终这是进行 Web 服务调用的最干净的方式,而无需引入 @Body
或 @FieldMap
。
无论如何,这就是我的故事,我会坚持下去。
【讨论】:
【参考方案3】:您可以为此使用Java Method Invocation Builder。
@GenerateMethodInvocationBuilder
public interface ServiceApi
@POST("/token")
@FormUrlEncoded
void extendSession(
@Default("theToken") @Field("refresh_token") final String refreshToken,
@Default("theId") @Field("client_id") final String clientId,
@Default("theSecret") @Field("client_secret") final String clientSecret,
@Default("theType") @Field("grant_type") final String grantType);
然后你可以像这样调用api:
ServiceApiExtendedSessionBuilder.extendedSession()
.withRefreshToken("theRefreshToken")
.invoke(serviceApi);
【讨论】:
@Default
在Retrofit
中不存在。可能是该库中的注释。【参考方案4】:
您的接口可以接受GrantType
对象,而不是String
,该对象具有不同grant_type
s 的工厂方法。这些工厂方法将设置client_id
、client_secret
和grant_type
字段。
@POST("/oauth/token")
Call<Token> extendSession(@Body GrantType grantType);
【讨论】:
以上是关于改造:如何发送带有常量字段的 POST 请求?的主要内容,如果未能解决你的问题,请参考以下文章