[Stream]使用Stream巧妙地拼接url参数

473 阅读2分钟

在使用第三方 API 的时候,总是会需要在 url 后面拼接一些请求参数,也就是 ? 符号后面的一串键值对。

看一个具体的例子,比如说要使用高德地图的 api,需要拼接一些参数,比如说出发地 orgin,目的地 destination 的经纬度等参数,在代码中最直接的拼接方法如下:

String url = "https://restapi.amap.com/v3/direction/driving"
StringBuilder sb = new StringBuilder();
sb.append("?").append("orgin").append("=").append(depLongitude).append(",").append(depLatitude)
  .append("destination").append("=")..append(destLongitude).append(",").append(destLatitude);
  
  // 下面省略一堆参数拼接...

如果使用 StringBuilder 傻瓜式拼接就会导致代码写得非常臃肿,而且如果以后有新的条件那么拼接的代码就会更多,非常不优雅,非常丑陋。所以,需要一个优化的办法。

下面介绍使用 Stream API 来优化参数拼接:

使用一个 Map 来存放查询的参数,使用 @Value 注解来获取编写在 yml 文件中的值,然后让 Map 调用 entrySet 方法之后再转为 Stream。这其中的每一项就是 entry,然后把 entry 通过 map 方法转为 k=v 的形式,这一步完成之后 Stream 中的元素就是一堆字符串集合,最终使用 joining 方法传入参数 & 符号将它们拼接到一起,就得到了查询的参数。

最后一步,就是把原始的第三方 api 和查询参数拼接,中间用 ? 符号分隔。

@Service
public class MapService {

    // https://restapi.amap.com/v3/config/district?keywords=%E5%8D%97%E4%BA%AC&subdistrict=2
    @Value("${system.map.path-api}")
    private String districtUrl;

    @Value("${system.map.key}")
    private String userKey;
    
    @Autowired
    private RestTemplate restTemplate;

    
    public DirectionResponse direction(String depLongitude, String depLatitude, String destLongitude,
                                       String destLatitude) {
        Map<String, String> conditions = new HashMap<String, String>() {{
            put("origin", depLongitude + "," + depLatitude);
            put("destination", destLongitude + "," + destLatitude);
            put("key", userKey);
        }};

        String s = conditions.entrySet().stream().map(e -> e.toString()).collect(joining("&"));
        String url = mapUrl + "?" + s;
        log.info("调用的接口地址: {}", url);

        // 调用高德接口
        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
        log.info("高德地图路径规划返回信息: {}", response.getBody());

        // 解析接口
        return parseDirectionEntity(response.getBody());
    }

}

为了方便以后调用,可以封装一个工具类:

/**
 * URL工具类
 */
public class UrlUtil {

    /**
     * 根据条件拼装url
     * @param api        基准的api地址
     * @param conditions 存放查询条件的map
     * @return 最终的api地址
     */
    public static String createQueryParameters(String api, Map<String, String> conditions) {
        String params = conditions.entrySet()
                                  .stream()
                                  .map(e -> e.toString())
                                  .collect(Collectors.joining("&"));
        return api + "?" + params;
    }
}

这样就不用写一堆 StringBuilder 的拼接代码了,非常丑陋。高手和菜鸟区别就在这里。