java - getparameter - formulario dinamico primefaces
Cómo obtener parámetros de formulario en filtro de solicitud (2)
Asegúrese de que ResourceFilterFactory
crea una instancia de ResourceFilter
para el método TestResource#execute
, que luego crea una instancia ContainerRequestFilter
:
public class MyFilterFactory implements ResourceFilterFactory {
@Override
public List<ResourceFilter> create(final AbstractMethod am) {
return new ArrayList<ResourceFilter>() {{
add(new ResourceFilter() {
@Override
public ContainerRequestFilter getRequestFilter() {
return new ContainerRequestFilter() {
@Override
public ContainerRequest filter(final ContainerRequest request) {
System.out.println(request.getFormParameters());
return request;
}
};
}
@Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
});
}};
}
}
A partir de la trace
que ha proporcionado, no estoy seguro de si se llama a su ContainerRequestFilter
. Debería haber un encabezado de rastreo más que contenga algo como esto:
→matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@b98df1f
Todo el rastro de mi prueba:
HTTP/1.1 201 Created
Location: http://localhost:8080/helloworld-webapp/helloworld/
Content-Type: text/plain
X-Jersey-Trace-000: accept root resource classes: "/helloworld"
X-Jersey-Trace-001: match path "/helloworld" -> "/application/.wadl(/.*)?", "/helloworld(/.*)?"
X-Jersey-Trace-002: accept right hand path java.util.regex.Matcher[pattern=/helloworld(/.*)? region=0,11 lastmatch=/helloworld]: "/helloworld" -> "/helloworld" : ""
X-Jersey-Trace-003: accept resource: "helloworld" -> @Path("/helloworld") com.sun.jersey.samples.helloworld.resources.HelloWorldResource@7449df0f
X-Jersey-Trace-004: match path "" -> ""
X-Jersey-Trace-005: accept resource methods: "helloworld", POST -> com.sun.jersey.samples.helloworld.resources.HelloWorldResource@7449df0f
X-Jersey-Trace-006: matched resource method: public javax.ws.rs.core.Response com.sun.jersey.samples.helloworld.resources.HelloWorldResource.execute(java.lang.String,java.lang.String)
X-Jersey-Trace-007: matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@6bc1b916
X-Jersey-Trace-008: matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@6bc1b916
X-Jersey-Trace-009: matched message body writer: java.lang.String@f62, "text/plain" -> com.sun.jersey.core.impl.provider.entity.StringProvider@4aae6c4e
Transfer-Encoding: chunked
Server: Jetty(6.1.24)
EDIT 1:
Habilitar solicitud LoggingFilter
:
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
</init-param>
EDICION 2:
También asegúrese de que ningún otro filtro Servlet o Jersey haya leído antes el InputStream
. En tal caso, la corriente de entrada de la entidad puede dejar de estar disponible (pero aún puede inyectar @FormParam
en su método de recursos, como en este caso).
Estoy tratando de obtener los parámetros de formulario de una solicitud en un filtro de solicitud:
@Override
public ContainerRequest filter(final ContainerRequest request) {
final Form formParameters = request.getFormParameters();
//logic
return request;
}
Sin embargo, la forma siempre parece estar vacía. La documentación HttpRequestContext.getFormParameters()
dice:
Obtenga los parámetros de formulario de la entidad de solicitud.
Este método asegurará que la entidad de solicitud esté protegida de manera que la aplicación pueda consumirla.
Devuelve: los parámetros del formulario, si hay una entidad de solicitud y el tipo de contenido es "application / x-www-form-urlencoded", de lo contrario se devolverá una instancia que no contenga ningún parámetro.
Mi recurso está anotado con @Consumes("application/x-www-form-urlencoded")
, aunque no se haya emparejado hasta después del filtro de solicitud. ¿Es por eso que esto no funciona?
Intenté investigar un poco, pero no pude encontrar ninguna prueba concluyente de si esto es posible. Hubo una discusión de 4 años en la que Paul Sandoz dice:
Si está trabajando en filtros Jersey o con
HttpRequestContext
puede obtener los parámetros de formulario de la siguiente manera: [enlace roto a Jersey 1.1.1HttpRequestContext.getFormParameters
]
También encontré esta discusión de hace 3 años sobre cómo obtener campos de formulario multipart / form-data en un filtro de solicitud. En él, Paul Sandoz usa el siguiente código:
// Buffer
InputStream in = request.getEntityInputStream();
if (in.getClass() != ByteArrayInputStream.class) {
// Buffer input
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ReaderWriter.writeTo(in, baos);
} catch (IOException ex) {
throw new ContainerException(ex);
}
in = new ByteArrayInputStream(baos.toByteArray());
request.setEntityInputStream(in);
}
// Read entity
FormDataMultiPart multiPart = request.getEntity(FormDataMultiPart.class);
Intenté emular ese enfoque para Form
lugar, pero el resultado de request.getEntityInputStream()
siempre es una transmisión vacía. Y al ver el origen de getFormParameters
, ese método ya está haciendo lo mismo:
@Override
public Form getFormParameters() {
if (MediaTypes.typeEquals(MediaType.APPLICATION_FORM_URLENCODED_TYPE, getMediaType())) {
InputStream in = getEntityInputStream();
if (in.getClass() != ByteArrayInputStream.class) {
// Buffer input
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
ReaderWriter.writeTo(in, byteArrayOutputStream);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
in = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
setEntityInputStream(in);
}
ByteArrayInputStream byteArrayInputStream = (ByteArrayInputStream) in;
Form f = getEntity(Form.class);
byteArrayInputStream.reset();
return f;
} else {
return new Form();
}
}
No puedo descifrar qué está sorbiendo el flujo de entrada de entidad antes de llegar a él. Algo en Jersey debe consumirlo porque los parámetros de forma se pasan luego al método de recursos. ¿Qué estoy haciendo mal aquí, o es esto imposible (y por qué)?
EDITAR: Aquí hay un ejemplo de una solicitud que se envía:
POST /test/post-stuff HTTP/1.1
Host: local.my.application.com:8443
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded
form_param_1=foo&form_param_2=bar
Aquí está el registro de solicitudes (algo redundante):
INFO: 1 * Server in-bound request
1 > POST https://local.my.application.com:8443/test/post-stuff
1 > host: local.my.application.com:8443
1 > connection: keep-alive
1 > content-length: 33
1 > cache-control: no-cache
1 > origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm
1 > user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36
1 > content-type: application/x-www-form-urlencoded
1 > accept: */*
1 > accept-encoding: gzip,deflate,sdch
1 > accept-language: en-US,en;q=0.8
1 > cookie: [omitted]
1 >
Aquí están los encabezados de respuesta de esa solicitud, incluido el rastro de Jersey :
Content-Type →application/json;charset=UTF-8
Date →Fri, 09 Aug 2013 18:00:17 GMT
Location →https://local.my.application.com:8443/test/post-stuff/
Server →Apache-Coyote/1.1
Transfer-Encoding →chunked
X-Jersey-Trace-000 →accept root resource classes: "/post-stuff"
X-Jersey-Trace-001 →match path "/post-stuff" -> "/post/-stuff(/.*)?", [...], "(/.*)?"
X-Jersey-Trace-002 →accept right hand path java.util.regex.Matcher[pattern=/post/-stuff(/.*)? region=0,11 lastmatch=/post-stuff]: "/post-stuff" -> "/post-stuff" : ""
X-Jersey-Trace-003 →accept resource: "post-stuff" -> @Path("/post-stuff") com.application.my.jersey.resource.TestResource@7612e9d2
X-Jersey-Trace-004 →match path "" -> ""
X-Jersey-Trace-005 →accept resource methods: "post-stuff", POST -> com.application.my.jersey.resource.TestResource@7612e9d2
X-Jersey-Trace-006 →matched resource method: public javax.ws.rs.core.Response com.application.my.jersey.resource.TestResource.execute(java.lang.String,java.lang.String)
X-Jersey-Trace-007 →matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@b98df1f
X-Jersey-Trace-008 →matched message body writer: java.lang.String@f62, "application/json" -> com.sun.jersey.core.impl.provider.entity.StringProvider@1c5ddffa
Aquí está la configuración del servlet (nada especial):
<servlet>
<servlet-name>jersey</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.application.my.jersey</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>com.application.my.jersey.MyFilterFactory</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.feature.Trace</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Aquí está el recurso de ejemplo:
@Path("/post-stuff")
@Produces(MediaType.APPLICATION_JSON)
public final class TestResource {
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response execute(
@FormParam("form_param_1") final String formParam1,
@FormParam("form_param_2") final String formParam2
) {
return Response.created(URI.create("/")).entity("{}").build();
}
}
Estoy usando Jersey 1.17.
Para aquellos interesados, estoy tratando de rodar mi propia validación de parámetros requerida, como se describe en JERSEY-351 . Mi solución aquí funciona para query, cookie y params de encabezado, los parámetros de formulario me están imposibilitando.
Esto fue complicado. Quité otros filtros de Jersey para eliminarlos del problema, pero olvidé un filtro de servlet plano que se oculta en la parte inferior de web.xml
:
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.application.my.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
La eliminación de este filtro solucionó el problema: los parámetros de formulario aparecían en el filtro Jersey. ¿Pero por qué? Cavé más profundo, reduciendo el problema a una sola declaración en MyFilter
:
request.getParameter("some_param")
Traté de simplificar el problema aún más eliminando MyFilter
y haciendo la misma llamada en el filtro Jersey (mediante la inyección de HttpServletRequest
), pero los parámetros del formulario aún aparecían. El problema parece ocurrir específicamente al llamar a getParameter
en la instancia de org.apache.catalina.connector.RequestFacade
que pasa a javax.servlet.Filter.doFilter
. Entonces, ¿esto es, de hecho, un error de Tomcat?
La documentación de ServletRequest.getParameter
dice:
Si los datos del parámetro se enviaron en el cuerpo de la solicitud, como ocurre con una solicitud HTTP POST, leer el cuerpo directamente a través de
getInputStream()
ogetReader()
puede interferir con la ejecución de este método.
Entonces, ¿quizás lo contrario también es cierto? ¿Que la llamada getParameter
podría interferir con la corriente de entrada de la entidad? No está claro si el contrato del método permite este comportamiento, y si indica un error en Tomcat, Jersey o ninguno.
De todos modos, ese viejo filtro en realidad no era necesario, así que mi problema se solucionó, solo lo eliminé.
Aquí hay una reproducción completa del problema (Tomcat 7.0):
web.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>test</display-name>
<servlet>
<servlet-name>jersey</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.application.my</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>com.application.my.TestFilterFactory</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.feature.Trace</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>servletFilter</filter-name>
<filter-class>com.application.my.TestServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>servletFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
TestServletFilter.java
:
package com.application.my;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public final class TestServletFilter implements Filter {
@Override
public void init(FilterConfig config) { }
@Override
public void doFilter(
final ServletRequest request,
final ServletResponse response,
final FilterChain chain
) throws IOException, ServletException {
System.out.println("calling getParameter on " + request.getClass().getName());
request.getParameter("blah");
chain.doFilter(request, response);
}
@Override
public void destroy() { }
}
TestFilterFactory.java
:
package com.application.my;
import java.util.Collections;
import java.util.List;
import com.sun.jersey.api.model.AbstractMethod;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import com.sun.jersey.spi.container.ContainerResponseFilter;
import com.sun.jersey.spi.container.ResourceFilter;
import com.sun.jersey.spi.container.ResourceFilterFactory;
public final class TestFilterFactory implements ResourceFilterFactory {
@Override
public List<ResourceFilter> create(final AbstractMethod method) {
return Collections.<ResourceFilter>singletonList(new ResourceFilter() {
@Override
public ContainerRequestFilter getRequestFilter() {
return new ContainerRequestFilter() {
@Override
public ContainerRequest filter(final ContainerRequest request) {
System.out.println("form: " + request.getFormParameters());
return request;
}
};
}
@Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
});
}
}
TestResource.java
:
package com.application.my;
import java.net.URI;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/post-stuff")
@Produces(MediaType.APPLICATION_JSON)
public final class TestResource {
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response execute(
@FormParam("form_param_1") final String formParam1,
@FormParam("form_param_2") final String formParam2
) {
System.out.println("form param_1: " + formParam1);
System.out.println("form param_2: " + formParam2);
return Response.created(URI.create("/")).entity("{}").build();
}
}