Mon*_*rlo 17 jax-rs jersey jersey-1.0
如何强制URIBuilder.path(...)编码参数如"%AD"?
的方法path,replacePath和segment的URIBuilder并不总是编码参数与百分比,正确.
当参数包含字符"%"后跟两个字符一起形成URL编码字符时,"%"不会编码为"%25".
例如
URI uri = UriBuilder.fromUri("https://dummy.com").queryParam("param", "%AD");
String test = uri.build().toString();
Run Code Online (Sandbox Code Playgroud)
"test"是" https://dummy.com?param=%AD "
但它应该是" https://dummy.com?param=%25AD "(字符"%"编码为"%25")
UriBuilderImpl.queryParam(...)当"%"后面的两个字符是十六进制时,该方法的行为与此类似.即,方法"com.sun.jersey.api.uri.UriComponent.isHexCharacter(char)"对于"%"之后的字符返回true.
我认为UriBuilderImpl的行为是正确的,因为我猜它试图不编码已编码的参数.但在我的场景中,我永远不会尝试使用已经编码的参数创建URL.
我该怎么办?
我的Web应用程序使用新泽西州,在许多地方我建立一个使用类UriBuilder的URI或调用该方法getBaseUriBuilder的UriInfo对象.
每次调用方法时queryParam,我都可以用"%25"替换"%" ,replaceQueryParam或者segment.但我正在寻找一个不那么麻烦的解决方案.
如何让Jersey返回我自己的UriBuilder实现?
我想过创建一个扩展UriBuilderImpl的类来覆盖这些方法,并在调用之前执行此替换super.queryParam(...)或其他任何操作.
在调用UriBuilder.fromURL(...)UriInfo.getBaseUriBuilder(...)等时,有没有办法让Jersey返回我自己的UriBuilder而不是UriBuilderImpl ?
看看这个方法RuntimeDelegate,我想到了扩展RuntimeDelegateImpl.我的实现将覆盖该方法createUriBuilder(...),该方法将返回我自己的方法UriBuilder,而不是UriBuilderImpl.然后,我会添加文件,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,因此容器加载该文件而不是my javax.ws.rs.ext.RuntimeDelegate.因此,它不会加载我的RuntimeDelegate实现.
有可能提供我自己的实现RuntimeDelegate吗?
我应该采取不同的方法吗?
Mic*_*dos 35
这可以借助来自Jersey 的UriComponent或直接来自Java的URLEncoder来实现:
UriBuilder.fromUri("https://dummy.com")
.queryParam("param",
UriComponent.encode("%AD",
UriComponent.Type.QUERY_PARAM_SPACE_ENCODED))
.build();
Run Code Online (Sandbox Code Playgroud)
结果如下:
https://dummy.com/?param=%25AD
Run Code Online (Sandbox Code Playgroud)
要么:
UriBuilder.fromUri("https://dummy.com")
.queryParam("param", URLEncoder.encode("%AD", "UTF-8"))
.build()
Run Code Online (Sandbox Code Playgroud)
将导致:
https://dummy.com/?param=%25AD
Run Code Online (Sandbox Code Playgroud)
对于更复杂的示例(即在查询参数中编码JSON),这种方法也是可能的.我们假设你有一个类似的JSON {"Entity":{"foo":"foo","bar":"bar"}}.使用UriComponent查询结果进行编码时,param会如下所示:
https://dummy.com/?param=%7B%22Entity%22:%7B%22foo%22:%22foo%22,%22bar%22:%22bar%22%7D%7D
Run Code Online (Sandbox Code Playgroud)
像这样的JSON甚至可以通过@QueryParam资源字段/方法参数注入(参见查询参数中的JSON或如何通过JAX-RS参数注释注入自定义Java类型).
你使用哪个泽西版?在标签中你提到泽西2,但在RuntimeDelegate你正在使用泽西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=%2520http://localhost:8080?name=%20http://localhost:8080?name=%2520http://localhost:8080?name=%20
通过http://comments.gmane.org/gmane.comp.java.jsr311.user/71
此外,基于Class UriBuilder文档,以下示例显示了如何获取您所追求的内容.
URI的大多数组件都允许使用URI模板,但它们的值仅限于特定组件.例如
Run Code Online (Sandbox Code Playgroud)UriBuilder.fromPath("{arg1}").build("foo#bar");将导致'#'的编码,使得结果URI为"foo%23bar".要创建URI"foo #bar"使用
Run Code Online (Sandbox Code Playgroud)UriBuilder.fromPath("{arg1}").fragment("{arg2}").build("foo", "bar")代替.URI模板名称和分隔符从不编码,但在构建URI时会对其值进行编码.构建URI时会忽略模板参数正则表达式,即不执行验证.
可以在启动时手动覆盖jersey中的默认行为,例如使用调用的静态助手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);
}
}
Run Code Online (Sandbox Code Playgroud)
警告:当然,这种方式不是很容易配置,如果你在一些可能被其他人重用的库代码中这样做,这可能会引起一些刺激.
例如,当我在Confluence插件中编写一个休息客户端时,我遇到了与OP相同的问题,并最终得到了"手动编码每个参数"的解决方案,因为插件是通过OSGi加载的,因此根本无法触及RuntimeDelegateImpl(java.lang.ClassNotFoundException: com.sun.ws.rs.ext.RuntimeDelegateImpl改为运行时).
(仅仅为了记录,在jersey2中看起来非常相似;特别是挂钩自定义RuntimeDelegateImpl的代码是相同的.)
| 归档时间: |
|
| 查看次数: |
30659 次 |
| 最近记录: |