V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
maninfog
V2EX  ›  Android

Retrofit 如何优雅的请求这样的后台接口?

  •  
  •   maninfog · 2018-08-13 19:19:32 +08:00 · 10405 次点击
    这是一个创建于 2324 天前的主题,其中的信息可能已经有所发展或是发生改变。

    请求方式:post ;

    请求体为至少包含 token 和 uuid 的 json。eg:{token:"",uuid:"",param1:"",param2:""}. 目前方式,采用继承。

    // 基类 request
    public class BaseRequest {
      String token;
      String uuid;
    
      public String toJson(){
        return new Gson().toJson(this);
      }
    }
    
    // 子类 
    public class TestRequest extends BaseRequest {
      String param1;
      String param2;
    }
    
    

    下面是 retrofit 接口。

        @POST
        fun getXX(@Body requestJson: String)
        
        //使用的伪代码
        TestRequest testRequest = new TestRequest();
        testRequest.param1="";
        testRequest.param2="";
        String json = testRequest.tojson();
        // 网络请求
        getXX(json);
    

    这种方式缺点很明显,不同的请求接口需要定义不同的请求类去继承,我想要的效果是这样的。

        @POST
        fun getXX(String param1,String param2)
        
        接口只需要这样写,然后借助一些注解或者 convert 之类的,在真正请求时,会将其转化成上面的格式。
    

    还请大佬解惑!!

    30 条回复    2018-08-14 21:25:32 +08:00
    StephenDev
        1
    StephenDev  
       2018-08-13 19:24:52 +08:00 via Android
    你可以试试请求的时候直接扔对象进去,然后将对象转为 map,key 是字段名,value 就是 param,retrofit 支持这样 map 格式
    maninfog
        2
    maninfog  
    OP
       2018-08-13 19:28:40 +08:00
    @StephenDev #1 您应该说的是 @FieldMap 这个注解,但是这个 post 到服务器应该是 param1=""&param2=""这种结构,而不是 json 格式数据,所以应该是不适用的。
    xiangyuecn
        3
    xiangyuecn  
       2018-08-13 19:38:18 +08:00
    发现别人家定义的注解用多了,写代码飞快。。。自己用简单对象搭积木反而不会,哈哈哈哈哈哈。传说:一顿操作猛如虎::一看战绩 0-5 (逃
    MoHen9
        4
    MoHen9  
       2018-08-13 19:41:07 +08:00 via Android
    为什么不把 token 和 uuid 放在 header 中?
    vjnjc
        5
    vjnjc  
       2018-08-13 19:44:40 +08:00 via Android
    没看明白要求。
    按照一楼的思路你用 @body,然后 body 是个 hashmap 不就好了么
    maninfog
        6
    maninfog  
    OP
       2018-08-13 19:56:21 +08:00 via Android
    @MoHen9 后台是这样我也没办法…目前还不能客户端教后台写接口😄
    maninfog
        7
    maninfog  
    OP
       2018-08-13 19:59:24 +08:00 via Android
    @vjnjc 需求是我用某个注解标记请求参数(比如 @Query @Field 这样的),真正请求时,这些被标记的请求参数会和 token 以及 uuid 一起被序列化成 json 数据作为 body post 到服务器。
    kingme
        8
    kingme  
       2018-08-13 20:06:35 +08:00
    通过拦截器阿,在拦截器里面重新组织
    maninfog
        9
    maninfog  
    OP
       2018-08-13 20:08:48 +08:00 via Android
    @kingme 拦截器只能得到请求头的参数把,对于方法中的参数,拦截器应该取不到阿
    MoHen9
        10
    MoHen9  
       2018-08-13 20:09:00 +08:00 via Android
    @maninfog 楼上正解,可以通过 okhttp 的拦截器动态添加
    pynix
        11
    pynix  
       2018-08-13 20:16:16 +08:00
    认证信息放到 header 中。
    twoyuan
        12
    twoyuan  
       2018-08-13 20:16:50 +08:00
    用 OkHttp 拦截器。通过 chain.request() 取到 request 对象调用 newBuilder() 加上需要的参数后后丢给 chain.process()
    pynix
        13
    pynix  
       2018-08-13 20:19:02 +08:00
    这种调用方式好像也没什么问题,你搞一个构造函数就可以了。

    getXX(new TestRequest(p1,p2));
    maninfog
        14
    maninfog  
    OP
       2018-08-13 20:22:45 +08:00 via Android
    @pynix 这个只是一个方法,一个项目中应该有几十个方法,请求参数都不一样,这样的话就要写几十个这样的类,不够优雅😔
    maninfog
        15
    maninfog  
    OP
       2018-08-13 20:24:35 +08:00 via Android
    @twoyuan 想过拦截器 但是后台是用的一个整的 json 而不是键值对的形式 所以用拦截器估计不行吧
    twoyuan
        16
    twoyuan  
       2018-08-13 20:30:04 +08:00
    @maninfog #15 可以,在拦截器里取出的 Request Body 就是 JSON,然后追加完参数再构造新的 request 对象就好了
    pynix
        17
    pynix  
       2018-08-13 20:30:28 +08:00
    @maninfog 拦截器应该可以的,不过你从拦截器拿到的 body 可能已经序列化了,你需要反序列化,加入字段再序列化,感觉更坑吧,也有可能不是这样的,我没看过拦截器。
    xiangyuecn
        18
    xiangyuecn  
       2018-08-13 20:49:19 +08:00
    @maninfog #6 这句话真难听,form 表单 application/x-www-form-urlencoded 请求的 body 格式是一个标准格式。。。后端已经吐了一口老血

    顺带杠一句:楼上几位大佬真水( doge
    xiangyuecn
        19
    xiangyuecn  
       2018-08-13 20:54:34 +08:00
    我好像理解错了。。。杠的那句收回来,哈哈


    主题到底是 A:

    后端要求:post: {key1:value,key2:value}
    你发送:post: key1=value&key2=value

    还是 B:

    后端要求:post: key1=value&key2=value
    你发送:post: {key1:value,key2:value}
    maninfog
        20
    maninfog  
    OP
       2018-08-13 20:58:54 +08:00 via Android
    @xiangyuecn 主题是: 后端要求 post json 所以我也得 post json
    maninfog
        21
    maninfog  
    OP
       2018-08-13 21:03:29 +08:00 via Android
    @pynix 的确 即使行的话 性能上也有所损耗 要去能在某个接口中拿到方法的参数以及注解就好了
    xiangyuecn
        22
    xiangyuecn  
       2018-08-13 21:16:38 +08:00   ❤️ 1
    杠也杠了,擦一下屁股。虽然没用过 Retrofit,但查了一下,他可以 post 字符串出去,看人家写的:


    这样的话,现在优雅的层面完全就可以不在 Retrofit 这个层面上了,完全在如何优雅的生成一段 json 数据上。
    依我暴力的习惯:

    写一个静态类,类里面两个方法:
    HashMap BuildPostMap()
    String MapToJson(HashMap)

    粗暴的使用:
    1. BuildPostMap:生成一个 map,map 里面已包含所有必要参数数据
    2. ........ 根据不同接口 put 参数到 map,至于参数从哪里来.....(这不重要)
    3. MapToJson:map 转成 json,这么通用的功能,估计这个方法都可以省略
    719465553
        23
    719465553  
       2018-08-13 21:21:54 +08:00
    @POST
    fun getXX(@Body request: Request)
    pynix
        24
    pynix  
       2018-08-13 21:24:24 +08:00
    @maninfog 如果后端是本公司的,应该可以尝试更正吧,认证信息一般放 header,body 放数据。。。。
    maninfog
        25
    maninfog  
    OP
       2018-08-13 21:27:23 +08:00 via Android
    @xiangyuecn 还是感谢你 提供了这种暴力的思路……缺陷在于参数和接口有点分离 …我将继续探寻是否有更好的做法😄
    kassadin
        26
    kassadin  
       2018-08-14 03:35:52 +08:00
    @maninfog #7 这个思路就行,用 @query, 然后参数都在 url 上,post body 为空。

    手动在 interceptor 中把参数取出来存 map 转 json 并去掉,map 添加通用参数 token, uid, 然后 map 转 requestBody 就行了

    需要注意的就是 query 会有同名 a=1&a=2,尽量规避同名参数就可以了。
    maninfog
        27
    maninfog  
    OP
       2018-08-14 08:41:15 +08:00 via Android
    @kassadin Retrofit 好像 post 方法不支持 @Query,会报错,所以应该只能用到 @Field,用这个的话不知道拦截器中能获取到不,一会儿我去试试吧。
    yescpu
        28
    yescpu  
       2018-08-14 10:02:20 +08:00
    Retrofit+OKHttp 可以很优雅的处理这个问题:

    class CommonInterceptor implements Interceptor{
    Map<String, String> mCommonParams;

    public Response intercept(Chain chain) throws IOException {

    if(get){
    request = addCommonParamsToUrl(request);
    }else if(post){
    RequestBody requestBody = request.body();
    if(formType){
    addCommonparamsToForm(requestBody);
    }else if(jsonType){
    addCommonParamsToJson(requestBody);
    }
    ...
    if (requestBody != null) {
    Request.Builder requestBuilder = request.newBuilder();
    request = requestBuilder
    .post(requestBody)
    .build();
    }
    }
    reture request;
    }
    }
    kassadin
        29
    kassadin  
       2018-08-14 14:34:46 +08:00
    @maninfog @query 是拼接 url 的,肯定可以。 @Field 用的是 StringConverter,合不出来 json。 @Body 只支持一个参数,只解析一次,自定义 converter 也实现不了多个具名参数拼 json。
    snowflake007
        30
    snowflake007  
       2018-08-14 21:25:32 +08:00
    没太看懂,token uuid ,不用每个请求接口都定义吧,拦截器插入到 Header 不是很好。
    https://www.jianshu.com/p/04ce0c91e3ee
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2618 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 15:20 · PVG 23:20 · LAX 07:20 · JFK 10:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.