Jersey 基于表单数据筛选请求

Jersey 基于表单数据筛选请求,jersey,jersey-2.0,Jersey,Jersey 2.0,在Jersey 1.x中,您可以使用ContainerRequest.getFormParameters()对表单数据进行请求过滤,但在Jersey 2.x中没有明显的等效项。我已经实现了ContainerRequestFilter接口,它允许我访问ContainerRequestContext,但是如何从那里获取表单数据呢 Jersey 1.x示例: public class MyFilter implements ContainerRequestFilter { public Conta

在Jersey 1.x中,您可以使用
ContainerRequest.getFormParameters()
对表单数据进行请求过滤,但在Jersey 2.x中没有明显的等效项。我已经实现了
ContainerRequestFilter
接口,它允许我访问
ContainerRequestContext
,但是如何从那里获取表单数据呢

Jersey 1.x示例:

public class MyFilter implements ContainerRequestFilter {
  public ContainerRequest filter(ContainerRequest request) {
    Form f = request.getFormParameters();

    // examine form data and filter as needed
  }
}
public class MyFilter implements ContainerRequestFilter {
  public void filter(ContainerRequestContext context) {
    // how do I get to the Form data now?
  }
}
Jersey 2.x示例:

public class MyFilter implements ContainerRequestFilter {
  public ContainerRequest filter(ContainerRequest request) {
    Form f = request.getFormParameters();

    // examine form data and filter as needed
  }
}
public class MyFilter implements ContainerRequestFilter {
  public void filter(ContainerRequestContext context) {
    // how do I get to the Form data now?
  }
}

表单POST参数在http请求体中发送,因此使用ContainerRequestContext可以执行以下操作

String q = IOUtils.toString(context.getEntityStream(), Charsets.UTF_8);
String[] params = q.split("&");  
Map<String, String> map = new HashMap<>();  
 for (String param : params)  
 {  
     String name = param.split("=")[0];  
     String value = param.split("=")[1];  
     map.put(name, value);  
 }  
String q=IOUtils.toString(context.getEntityStream(),Charsets.UTF_8);
字符串[]参数=q.split(&);
Map Map=newhashmap();
for(字符串参数:params)
{  
字符串名称=参数拆分(“=”[0];
字符串值=参数拆分(“=”[1];
map.put(名称、值);
}  

查看jersey 1实现,似乎
ContainerRequest#getEntity(类)
支持将实体流直接读取到
表单
类中

在jersey 2中,
getEntity
已重命名为
readEntity
,因此以下操作可能有效(未测试):

Form params=request.readEntity(Form.class)

泽西1号IML的建议IML(被盗和轻微修改):

/**
 * Stolen from the jersey 1 impl
 */
public static Form getFormParameters(ContainerRequest request) {
    if (MediaTypes.typeEqual(MediaType.APPLICATION_FORM_URLENCODED_TYPE, request.getMediaType())) {
        InputStream in = request.getEntityStream();
        if (in.getClass() != ByteArrayInputStream.class) {
            // Buffer input
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try {
                ReaderWriter.writeTo(in, baos);
            } catch (IOException e) {
                throw new IllegalArgumentException(e);
            }

            in = new ByteArrayInputStream(baos.toByteArray());
            request.setEntityStream(in);
        }

        ByteArrayInputStream bais = (ByteArrayInputStream) in;
        Form f = request.readEntity(Form.class);
        bais.reset();
        return f;
    } else {
        return new Form();
    }
}

经过大量的搜索和反复尝试,我在Jersey 2中找到了一个合适的方法来实现这一点。您必须手动使用请求实体主体,但您必须谨慎地使用不会阻止后续过滤器和资源也使用它的方式。下面是一个将实体读入表单对象的简单示例:

@Provider
public class FormDataFilter implements ContainerRequestFilter
{
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException
    {
        if (requestContext instanceof ContainerRequest)
        {
            ContainerRequest request = (ContainerRequest) requestContext;

            if ( requestContext.hasEntity()
              && MediaTypes.typeEqual(MediaType.APPLICATION_FORM_URLENCODED_TYPE,request.getMediaType()))
            {
                request.bufferEntity();
                Form f = request.readEntity(Form.class);
            }
        }
    } 
}

该键正在调用bufferEntity()。否则,实体将被标记为关闭,并在任何后续读取尝试中导致IllegalStateException。

以下是一种读取表单实体的方法,而不依赖于特定于实现的类,即它将同时使用Jersey(v2)或CXF(v3)

@Provider
公共类过滤器实现ContainerRequestFilter{
@上下文
私人供应商;
@凌驾
公共无效筛选器(ContainerRequestContext请求)引发IOException{
if(!request.hasEntity()| | |!MediaTypes.typeEqual(应用程序_表单_URLENCODED_类型,request.getMediaType()){
//如果不是表格。。。
返回;
}
ByteArrayInputStream resettableIS=toResettableStream(request.getEntityStream());
Form Form=providers.getMessageBodyReader(Form.class,Form.class,新注释[0],应用程序\表单\ URL编码\类型)
.readFrom(Form.class,Form.class,新注释[0],应用程序\表单\ URL编码\类型,null,resettableIS);
//用形式做事
resettableIS.reset();
request.setEntityStream(resettableIS);
}
@非空
private ByteArrayInputStream toResettableStream(InputStream entityStream)引发IOException{
ByteArrayOutputStream bas=新的ByteArrayOutputStream();
字节[]缓冲区=新字节[1024];
内伦;
而((len=entityStream.read(buffer))>-1){
写入(缓冲区,0,len);
}
paos.flush();
返回新的ByteArrayInputStream(baos.toByteArray());
}
}
这工作得很好,并且具有仅使用JAX-RSAPI的优点,因此是可移植的


但是请注意,CXF2.x使用的JAX-RSAPI2.0-m10还没有
表单
类。在这种情况下,可以简单地用
多值map.class
替换
Form.class
,代价是一些未检查/原始类型的警告。

谢谢,看起来您必须直接操作实体流。我只希望泽西队能把这个留在那里,帮助处理流的转换、解码和构建多值地图。似乎是一个非常常见的用例,它保证在API中。抱歉,但在我的例子中它不起作用,我还需要来自
ContainerRequestContext
的form param,它正在使用ContainerRequestContext:这不会像现在一样起作用,因为ReaderWriter不是Jersey 2 API的一部分。不确定是否有替换,迁移指南没有提到。请注意,
ContainerRequest
是Jersey类,而不是JAX-RS标准的一部分。不幸的是,根据本文,仅使用JAX-RS是不可能做到这一点的。