【问题标题】:Retrofit: How to send a POST request with constant fields?改造:如何发送带有常量字段的 POST 请求?
【发布时间】:2014-12-29 02:16:23
【问题描述】:

我想发送一个带有一个实际参数的简单 POST 请求:

@POST("/token")
@FormUrlEncoded
void extendSession(@Field("refresh_token")final String refreshToken);

但是这个请求还应该发送一些服务器请求的常量值,例如client_idclient_secretgrant_type,它们是常量,不应该是应用程序API的一部分。

最好的方法是什么?

【问题讨论】:

    标签: java android http http-post retrofit


    【解决方案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);
      

      【讨论】:

      • @DefaultRetrofit 中不存在。可能是该库中的注释。
      【解决方案4】:

      您的接口可以接受GrantType 对象,而不是String,该对象具有不同grant_types 的工厂方法。这些工厂方法将设置client_idclient_secretgrant_type 字段。

      @POST("/oauth/token")
      Call<Token> extendSession(@Body GrantType grantType);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-01
        • 1970-01-01
        • 2021-10-12
        • 1970-01-01
        • 2021-12-13
        • 2016-01-21
        相关资源
        最近更新 更多