Reddit Api unsupported_grant_type Retrofit 错误 (java)

Reddit Api unsupported_grant_type error with Retrofit (java)

我正在使用 "Token Retrieval (code flow)" 概述 here 通过 OAuth2 为 Reddit Api 检索访问令牌。我已设法获得 "code" 字符串,但在尝试检索访问令牌时出现 unsupported_grant_type 错误。我想用 Retrofit2 来完成这个。

private void getAccessToken(String code) {

    if (mRetrofit == null) {
        mRetrofit = new Retrofit.Builder()
                .baseUrl(RedditConstants.REDDIT_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
    Api api = mRetrofit.create(Api.class);

    String authString = RedditConstants.REDDIT_CLIENT_ID + ":";
    String encodedAuthString = Base64.encodeToString(authString.getBytes(), Base64.NO_WRAP);

    Map<String, String> headers = new HashMap<>();
    // headers.put("Content-Type", "application/x-www-form-urlencoded");
    // headers.put("Accept", "application/json");
    headers.put("Authorization", "Basic " + encodedAuthString);
    headers.put("grant_type" , "authorization_code");
    headers.put("code", code);
    headers.put("redirect_uri", RedditConstants.REDDIT_REDIRECT_URL);
    headers.put("User-Agent", RedditConstants.REDDIT_USER_AGENT);

    Call<RedditAccessToken> call = api.login(headers, MediaType.parse("application/x-www-form-urlencoded"));
    call.enqueue(new Callback<RedditAccessToken>() {
        @Override
        public void onResponse(Call<RedditAccessToken> call, Response<RedditAccessToken> response) {
                Log.d("Findme", "body: " + response.body().toString());
                Log.d("Findme", "response: " + response.toString());
        }

        @Override
        public void onFailure(Call<RedditAccessToken> call, Throwable t) {
            Log.e("Fineme", "onFailure: " + t.getMessage());
        }
    });

}

记录结果:

D/Findme: body: RedditAccessToken{scope='null', token_type='null', 
error='unsupported_grant_type', expires_in='0', access_token='null', 
refresh_token='null'}
D/Findme: response: Response{protocol=h2, code=200, message=, 
url=https://www.reddit.com/api/v1/access_token/}

改装界面:

public interface Api {

    @POST("access_token/")
    Call<RedditAccessToken> login (
        @HeaderMap Map<String, String> headers,
        @Header("Content-Type") MediaType contentType
    );
}

改造数据模型:

public class RedditAccessToken {

    @SerializedName("scope")
    @Expose
    private String scope;

    @SerializedName("token_type")
    @Expose
    private String token_type;

    @SerializedName("expires_in")
    @Expose
    private long expires_in;

    @SerializedName("access_token")
    @Expose
    private String access_token;

    @SerializedName("refresh_token")
    @Expose
    private String refresh_token;

    @SerializedName("error")
    @Expose
    private String error;

    public String getScope() {
        return scope;
    }

    public String getRefresh_token() {
        return refresh_token;
    }

    public void setRefresh_token(String refresh_token) {
        this.refresh_token = refresh_token;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public String getToken_type() {
        return token_type;
    }

    public void setToken_type(String token_type) {
        this.token_type = token_type;
    }

    public long getExpires_in() {
        return expires_in;
    }

    public void setExpires_in(long expires_in) {
        this.expires_in = expires_in;
    }

    public String getAccess_token() {
        return access_token;
    }

    public void setAccess_token(String access_token) {
        this.access_token = access_token;
    }

    @Override
    public String toString() {
        return "RedditAccessToken{" +
                "scope='" + scope + '\'' +
                ", token_type='" + token_type + '\'' +
                ", error='" + error + '\'' +
                ", expires_in='" + expires_in + '\'' +
                ", access_token='" + access_token + '\'' +
                ", refresh_token='" + refresh_token + '\'' +
                '}';
    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }
}

我已经解决了错误,现在一切正常。首先,我应该使用 Retrofit Annotation @FormUrlEncoded,它会自动将 mime 类型调整为 application/x-www-form-urlencoded。此外,只有 HTTP 基本身份验证字符串 header 是必需的。其他 POST 数据应作为 Retrofit 字段提交。这些更改导致了以下代码。

String authString = RedditConstants.REDDIT_CLIENT_ID + ":";
String encodedAuthString = 
       Base64.encodeToString(authString.getBytes(), Base64.NO_WRAP);

Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Basic " + encodedAuthString);

Map<String, String> fields = new HashMap<>();
fields.put("grant_type", "authorization_code");
fields.put("code", code);
fields.put("redirect_uri", RedditConstants.REDDIT_REDIRECT_URL);
fields.put("User-Agent", RedditConstants.REDDIT_USER_AGENT);


Call<RedditAccessToken> call = api.login(headers, fields);
call.enqueue(new Callback<RedditAccessToken>()

改装界面:

public interface Api {

    @FormUrlEncoded
    @POST("access_token/")
    Call<RedditAccessToken> login (
            @HeaderMap Map<String, String> headers,
            @FieldMap Map<String, String> fields
    );
}     

太好了!!!谢谢@zachery-osborn

这对我有用 :D 首先我有 unsupported_grant_type 错误

我的服务器是ASP WebAPI 2 Owin OAuthAuthorizationServerProvider for get UserToken in Login

url for get Token Example WebAPI: http://localhost:41155/token

内容类型:application/x-www-form-urlencoded

BulkEdit 模式中的正文示例邮递员:

用户名:admin

密码:admin

grant_type:密码

回复: {"access_token":"wZ8Q54Mxpaq.....","token_type":"bearer","expires_in":86399}

改造中 API 接口:

 @FormUrlEncoded
@POST("token")
Call<ResponseBody> GetUserToken(@FieldMap Map<String, String> fields);

在Activity

 Map<String, String> fields = new HashMap<>();
    fields.put("username", "parsian");
    fields.put("password", "admin");
    fields.put("grant_type", "password");RetrofitInitialize.With(MainActivity.this).webServices.GetUserToken(fields);