はじめに
エキサイト株式会社 21卒 バックエンドエンジニアの山縣です。
UriComponentsのreplaceQueryParam()
を使用してクエリパラメータの値を書き換えたときに、書き換える前と書き換えた後とでクエリパラメータの順序が異なってしまい、
単体テストで落ちてしまいました。
本記事では、クエリパラメータの順序が異なるURIを比較する方法について共有します。
概要
クエリパラメータの順序が異なる2つのURIを用意します。
このとき、uri1とuri2を比較するとfalse
になります。
URI uri1 = URI.create("https://example.com?page=2&per_page=10"); URI uri2 = URI.create("https://example.com?per_page=10&page=2"); System.out.println(Objects.equals(uri1, uri2)); // → false
これは、URIがクエリパラメータを文字列で保持しており、equals
で比較したときにクエリパラメータは"page=2&per_page=10"
と"per_page=10&page=2"
の文字列比較を行っているため、false
になってしまうからです。
クエリパラメータの順序が異なったとしても同一のものとして比較したいときは、少し工夫する必要があります。
解決策
URIをUriComponentsに変換することで容易に比較することができるようになります。
UriComponentsではクエリパラメータをMap<K, List<V>>
のラッパーであるMultiValueMapで保持しているため、クエリパラメータの順序が異なるものを比較したときにtrue
を返します。
UriComponents u1 = UriComponentsBuilder.fromUri(uri1).build();
UriComponents u2 = UriComponentsBuilder.fromUri(uri2).build();
System.out.println(Objects.equals(u1, u2));
// → true
実際にUriComponentsのクエリパラメータの中身を見てみると、確かにMap形式で保持されていることが確認できます。
System.out.println(u1.getQueryParams());
// → {page=[2], per_page=[10]}
注意点
MultiValueMapは1つのキーに対して複数の値を扱うことができます。
そのため、クエリパラメータに同一のキーが複数指定されている場合、Listに変換されるため、false
を返す可能性があります。
URI uri3 = URI.create("https://example.com?page=2&per_page=10&page=3"); URI uri4 = URI.create("https://example.com?page=3&per_page=10&page=2"); UriComponents u3 = UriComponentsBuilder.fromUri(uri3).build(); UriComponents u4 = UriComponentsBuilder.fromUri(uri4).build(); System.out.println(u3.getQueryParams()); // → {page=[2, 3], per_page=[10]} System.out.println(u4.getQueryParams()); // → {page=[3, 2], per_page=[10]} System.out.println(Objects.equals(u3, u4)); // → false
したがって、同一のキーが複数指定される場合は注意しなくてはなりません
おわりに
UriComponentsを利用してクエリパラメータの順序が異なるURIを比較する方法についてまとめました。
クエリパラメータの順序が異なるURIを比較したいときって一度はあるのかなと思います。
URIをUriComponentsにして比較を行うことで、クエリパラメータ順序が異なっていたとしてもtrue
を返すようになります。
「クエリパラメータの順序が異なるURIでも同じものとして扱いたい!」といった方に本記事が参考になれば幸いです。