_"%AD"
_のようなパラメータをURIBuilder.path(...)
に強制的にエンコードさせる方法は?
path
のメソッドreplacePath
、segment
、およびURIBuilder
は、必ずしもパーセントでパラメーターを正しくエンコードするとは限りません。
パラメータに「%」という文字の後に2つの文字が続き、URLエンコードされた文字を形成する場合、「%」は「%25」としてエンコードされません。
例えば
_URI uri = UriBuilder.fromUri("https://dummy.com").queryParam("param", "%AD");
String test = uri.build().toString();
_
「テスト」は「 https://dummy.com?param=%AD "
しかし、それは " https://dummy.com?param=%25AD "( "%25"が "%25"としてエンコードされている)である必要があります
メソッドUriBuilderImpl.queryParam(...)
は、「%」に続く2つの文字が16進数である場合にこのように動作します。つまり、メソッド「com.Sun.jersey.api.uri.UriComponent.isHexCharacter(char)」は、「%」に続く文字に対してtrueを返します。
UriBuilderImplの動作は正しいと思います。すでにエンコードされているパラメーターをエンコードしようとしないためです。しかし、私のシナリオでは、既にエンコードされたパラメーターを使用してURLを作成しようとはしません。
どうすればよいですか?
私のWebアプリケーションはJerseyを使用しており、多くの場所でUriBuilderクラスを使用してURIを構築するか、getBaseUriBuilder
オブジェクトのUriInfo
メソッドを呼び出します。
メソッドqueryParam
、replaceQueryParam
またはsegment
を呼び出すたびに、「%」を「%25」に置き換えることができます。しかし、私はそれほど煩わしくない解決策を探しています。
UriBuilderの独自の実装を返すようにジャージーを作成するにはどうすればよいですか?
これらのメソッドをオーバーライドし、super.queryParam(...)
などを呼び出す前にこの置換を実行するUriBuilderImplを拡張するクラスを作成することを考えました。
UriBuilder.fromURL(...)
、UriInfo.getBaseUriBuilder(...)などを呼び出すときに、JerseyがUriBuilderImplではなく自分のUriBuilderを返すようにする方法はありますか?
メソッドRuntimeDelegate
を見て、RuntimeDelegateImpl
を拡張することを考えました。私の実装では、UriBuilder
ではなく、独自のUriBuilderImpl
を返すメソッドcreateUriBuilder(...)
をオーバーライドします。次に、ファイル_META-INF/services/javax.ws.rs.ext.RuntimeDelegate
_を追加し、その中に私のRuntimeDelegateImpl
の完全なクラス名を追加します。
問題は、jersey-bundle.jarに_META-INF/services/javax.ws.rs.ext.RuntimeDelegate
_を指す_com.Sun.jersey.server.impl.provider.RuntimeDelegateImpl
_がすでに含まれているため、コンテナーが_javax.ws.rs.ext.RuntimeDelegate
_ではなくそのファイルをロードすることです。したがって、RuntimeDelegate
implementationは読み込まれません。
RuntimeDelegate
の独自の実装を提供することは可能ですか?
別のアプローチを取るべきですか?
これは、ジャージーからの UriComponent またはJavaからの URLEncoder の助けを借りて可能です:
UriBuilder.fromUri("https://dummy.com")
.queryParam("param",
UriComponent.encode("%AD",
UriComponent.Type.QUERY_PARAM_SPACE_ENCODED))
.build();
その結果:
https://dummy.com/?param=%25AD
または:
UriBuilder.fromUri("https://dummy.com")
.queryParam("param", URLEncoder.encode("%AD", "UTF-8"))
.build()
結果は:
https://dummy.com/?param=%25AD
より複雑な例(クエリパラメータでのJSONのエンコードなど)では、このアプローチも可能です。 {"Entity":{"foo":"foo","bar":"bar"}}
のようなJSONがあるとします。 UriComponent
を使用してエンコードすると、クエリパラメータの結果は次のようになります。
https://dummy.com/?param=%7B%22Entity%22:%7B%22foo%22:%22foo%22,%22bar%22:%22bar%22%7D%7D
このようなJSONは、@QueryParam
を介してリソースフィールド/メソッドパラメータに注入することもできます(クエリパラメータの JSONまたはカスタムの注入方法Javaタイプを参照) JAX-RSパラメータアノテーションを介して )。
どのジャージーバージョンを使用していますか?タグではJersey 2について言及していますが、RuntimeDelegate
セクションではJersey 1のものを使用しています。
次の例が役立つかどうかを確認してください。以下のリンク先のスレッドでは、使用可能な関数とそれらのさまざまな出力に関する広範な議論があります。
以下:
UriBuilder.fromUri("http://localhost:8080").queryParam("name", "{value}").build("%20");
UriBuilder.fromUri("http://localhost:8080").queryParam("name", "{value}").buildFromEncoded("%20");
UriBuilder.fromUri("http://localhost:8080").replaceQuery("name={value}).build("%20");
UriBuilder.fromUri("http://localhost:8080").replaceQuery("name={value}).buildFromEncoded("%20");
出力されます:
http://localhost:8080?name=%2520
http://localhost:8080?name=%20
http://localhost:8080?name=%2520
http://localhost:8080?name=%20
http://comments.gmane.org/gmane.comp.Java.jsr311.user/71 経由
また、 クラスUriBuilderのドキュメントに基づいて 、次の例は、目的を取得する方法を示しています。
URIテンプレートは、URIのほとんどのコンポーネントで許可されていますが、その値は特定のコンポーネントに制限されています。例えば。
UriBuilder.fromPath("{arg1}").build("foo#bar");
結果のURIが「foo%23bar」になるように、「#」のエンコードが行われます。 URI "foo#bar"を作成するには
UriBuilder.fromPath("{arg1}").fragment("{arg2}").build("foo", "bar")
代わりに。 URIテンプレート名と区切り文字は決してエンコードされませんが、それらの値はURIが構築されるときにエンコードされます。 URIを構築する場合、テンプレートパラメータの正規表現は無視されます。つまり、検証は実行されません。
起動時にジャージーのデフォルトの動作を手動で上書きすることが可能です。 RuntimeDelegate.setInstance(yourRuntimeDelegateImpl)
を呼び出す静的ヘルパーを使用します。
したがって、パーセントがすでにエンコードされたシーケンスの一部であるように見えても、パーセントをエンコードするUriBuilderが必要な場合は、次のようになります。
[...]
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.ext.RuntimeDelegate;
import com.Sun.jersey.api.uri.UriBuilderImpl;
import com.Sun.ws.rs.ext.RuntimeDelegateImpl;
// or for jersey2:
// import org.glassfish.jersey.uri.internal.JerseyUriBuilder;
// import org.glassfish.jersey.internal.RuntimeDelegateImpl;
public class SomeBaseClass {
[...]
// this is the lengthier custom implementation of UriBuilder
// replace this with your own according to your needs
public static class AlwaysPercentEncodingUriBuilder extends UriBuilderImpl {
@Override
public UriBuilder queryParam(String name, Object... values) {
Object[] encValues = new Object[values.length];
for (int i=0; i<values.length; i++) {
String value = values[i].toString(); // TODO: better null check here, like in base class
encValues[i] = percentEncode(value);
}
return super.queryParam(name, encValues);
}
private String percentEncode(String value) {
StringBuilder sb = null;
for (int i=0; i < value.length(); i++) {
char c = value.charAt(i);
// if this condition is is true, the base class will not encode the percent
if (c == '%'
&& i + 2 < value.length()
&& isHexCharacter(value.charAt(i + 1))
&& isHexCharacter(value.charAt(i + 2))) {
if (sb == null) {
sb = new StringBuilder(value.substring(0, i));
}
sb.append("%25");
} else {
if (sb != null) sb.append(c);
}
}
return (sb != null) ? sb.toString() : value;
}
// in jersey2 one can call public UriComponent.isHexCharacter
// but in jersey1 we need to provide this on our own
private static boolean isHexCharacter(char c) {
return ('0' <= c && c <= '9')
|| ('A' <=c && c <= 'F')
|| ('a' <=c && c <= 'f');
}
}
// here starts the code to hook up the implementation
public static class AlwaysPercentEncodingRuntimeDelegateImpl extends RuntimeDelegateImpl {
@Override
public UriBuilder createUriBuilder() {
return new AlwaysPercentEncodingUriBuilder();
}
}
static {
RuntimeDelegate myDelegate = new AlwaysPercentEncodingRuntimeDelegateImpl();
RuntimeDelegate.setInstance(myDelegate);
}
}
警告:もちろん、そのように構成することはあまりできません。他のユーザーが再利用する可能性があるライブラリコードでこれを行うと、多少の不快感が生じる可能性があります。
たとえば、Confluenceプラグインでレストクライアントを作成するときにOPと同じ問題があり、プラグインがOSGiを介してロードされ、単純にRuntimeDelegateImpl
(Java.lang.ClassNotFoundException: com.Sun.ws.rs.ext.RuntimeDelegateImpl
代わりに実行時)。
(そして、念のために言うと、これはjersey2では非常によく似ています。特に、カスタムのRuntimeDelegateImplをフックするコードは同じです。)