grails - respuestas - Accediendo al cuerpo sin procesar de una solicitud PUT o POST
que es http request (3)
Como se puede ver aquí
http://jira.codehaus.org/browse/GRAILS-2017
Simplemente apagando grails el manejo automático de XML hace que el texto sea accesible en los controladores. Me gusta esto
class EventsController {
static allowedMethods = [add:''POST'']
def add = {
log.info("Got request " + request.reader.text)
render "OK"
}}
Mejor, Anders
Estoy implementando una API RESTful en Grails, y uso un esquema de autenticación personalizado que implica firmar el cuerpo de la solicitud (de manera similar al esquema de autenticación S3 de Amazon). Por lo tanto, para autenticar la solicitud, necesito acceder al contenido del cuerpo crudo POST o PUT para calcular y verificar la firma digital.
Estoy haciendo la autenticación en un beforeInterceptor en el controlador. Así que quiero que algo como request.body esté accesible en el interceptor, y aún así poder usar request.JSON en la acción real. Me temo que si leo el cuerpo en el interceptor utilizando getInputStream o getReader (métodos proporcionados por ServletRequest), el cuerpo aparecerá vacío en la acción cuando intente acceder a él a través de request.JSON.
Estoy migrando de Django a Grails, y tuve el mismo problema en Django hace un año, pero fue rápidamente parchado. Django proporciona un atributo request.raw_post_data que puede usar para este propósito.
Por último, para ser agradable y RESTful, me gustaría que esto funcione para las solicitudes POST y PUT.
Cualquier consejo o consejos sería muy apreciado. Si no existe, preferiría consejos sobre cómo implementar una solución elegante sobre ideas para hacks rápidos y sucios. =) En Django, edité algunos manejadores de solicitudes de middleware para agregar algunas propiedades a la solicitud. Soy muy nuevo en Groovy y Grails, así que no tengo idea de dónde vive ese código, pero no me importaría hacer lo mismo si fuera necesario.
Es posible anulando HttpServletRequest en un filtro de servlet.
Debe implementar un HttpServletRequestWrapper que almacene el cuerpo de la solicitud: src / java / grails / util / http / MultiReadHttpServletRequest.java
package grails.util.http;
import org.apache.commons.io.IOUtils;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletInputStream;
import java.io.*;
import java.util.concurrent.atomic.AtomicBoolean;
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
private byte[] body;
public MultiReadHttpServletRequest(HttpServletRequest httpServletRequest) {
super(httpServletRequest);
// Read the request body and save it as a byte array
InputStream is = super.getInputStream();
body = IOUtils.toByteArray(is);
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStreamImpl(new ByteArrayInputStream(body));
}
@Override
public BufferedReader getReader() throws IOException {
String enc = getCharacterEncoding();
if(enc == null) enc = "UTF-8";
return new BufferedReader(new InputStreamReader(getInputStream(), enc));
}
private class ServletInputStreamImpl extends ServletInputStream {
private InputStream is;
public ServletInputStreamImpl(InputStream is) {
this.is = is;
}
public int read() throws IOException {
return is.read();
}
public boolean markSupported() {
return false;
}
public synchronized void mark(int i) {
throw new RuntimeException(new IOException("mark/reset not supported"));
}
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
}
}
Un filtro de servlet que anula el servlet actualRequest: src / java / grails / util / http / MultiReadServletFilter.java
package grails.util.http;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
public class MultiReadServletFilter implements Filter {
private static final Set<String> MULTI_READ_HTTP_METHODS = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER) {{
// Enable Multi-Read for PUT and POST requests
add("PUT");
add("POST");
}};
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if(servletRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) servletRequest;
// Check wether the current request needs to be able to support the body to be read multiple times
if(MULTI_READ_HTTP_METHODS.contains(request.getMethod())) {
// Override current HttpServletRequest with custom implementation
filterChain.doFilter(new MultiReadHttpServletRequest(request), servletResponse);
return;
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
}
A continuación, debe ejecutar grails install-templates
y editar el archivo web.xml en src / templates / war y agregarlo después de la definición de charEncodingFilter:
<filter>
<filter-name>multireadFilter</filter-name>
<filter-class>grails.util.http.MultiReadServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>multireadFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Debería poder llamar a request.inputStream
veces como lo necesite.
No he probado este código / procedimiento concreto pero he hecho cosas similares en el pasado, así que debería funcionar ;-)
Nota: tenga en cuenta que las solicitudes enormes pueden matar su aplicación (OutOfMemory ...)
Parece que la única manera de poder tener acceso continuo tanto a la transmisión como a solicitar parámetros para las solicitudes POST es escribir una envoltura que anule la lectura de la secuencia así como el acceso a los parámetros. Este es un gran ejemplo: