java - delegatingfilterproxy - spring filter
Buscando un ejemplo para insertar contenido en la respuesta usando un filtro servlet (3)
La base de código que estoy usando llama al método getOutputStream, en lugar de getWriter cuando procesa la respuesta, por lo que los ejemplos incluidos en la otra respuesta no ayudan. Aquí hay una respuesta más completa que funciona tanto con OutputStream como con PrintWriter, incluso con errores correctos, si se accede al escritor dos veces. Esto se deriva del gran ejemplo, PETICIÓN DE VOLQUETE Y RESPUESTA CON JAVAX.SERVLET.FILTER .
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyFilter implements Filter
{
private FilterConfig filterConfig = null;
private static class ByteArrayServletStream extends ServletOutputStream
{
ByteArrayOutputStream baos;
ByteArrayServletStream(ByteArrayOutputStream baos)
{
this.baos = baos;
}
public void write(int param) throws IOException
{
baos.write(param);
}
}
private static class ByteArrayPrintWriter
{
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
private PrintWriter pw = new PrintWriter(baos);
private ServletOutputStream sos = new ByteArrayServletStream(baos);
public PrintWriter getWriter()
{
return pw;
}
public ServletOutputStream getStream()
{
return sos;
}
byte[] toByteArray()
{
return baos.toByteArray();
}
}
public class CharResponseWrapper extends HttpServletResponseWrapper
{
private ByteArrayPrintWriter output;
private boolean usingWriter;
public CharResponseWrapper(HttpServletResponse response)
{
super(response);
usingWriter = false;
output = new ByteArrayPrintWriter();
}
public byte[] getByteArray()
{
return output.toByteArray();
}
@Override
public ServletOutputStream getOutputStream() throws IOException
{
// will error out, if in use
if (usingWriter) {
super.getOutputStream();
}
usingWriter = true;
return output.getStream();
}
@Override
public PrintWriter getWriter() throws IOException
{
// will error out, if in use
if (usingWriter) {
super.getWriter();
}
usingWriter = true;
return output.getWriter();
}
public String toString()
{
return output.toString();
}
}
public void init(FilterConfig filterConfig) throws ServletException
{
this.filterConfig = filterConfig;
}
public void destroy()
{
filterConfig = null;
}
public void doFilter(
ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
CharResponseWrapper wrappedResponse = new CharResponseWrapper(
(HttpServletResponse)response);
chain.doFilter(request, wrappedResponse);
byte[] bytes = wrappedResponse.getByteArray();
if (wrappedResponse.getContentType().contains("text/html")) {
String out = new String(bytes);
// DO YOUR REPLACEMENTS HERE
out = out.replace("</head>", "WTF</head>");
response.getOutputStream().write(out.getBytes());
}
else {
response.getOutputStream().write(bytes);
}
}
}
He estado buscando en la red y en stackoverflow un ejemplo de alguien que inserta contenido en la respuesta usando un filtro de servlet, pero solo puedo encontrar ejemplos de gente que captura / comprime la salida y / o cambia los encabezados. Mi objetivo es agregar un fragmento de HTML justo antes del cierre </ body> de todas las respuestas HTML.
Estoy trabajando en una solución que amplía el HttpServletResponseWrapper para usar mi propio PrintWriter, y luego anula los métodos de escritura al respecto. Dentro del método de escritura estoy almacenando los últimos 7 caracteres para ver si es igual a la etiqueta de cuerpo de cierre, y luego escribo mi fragmento de HTML más la etiqueta de cuerpo de cierre, antes de continuar con las operaciones de escritura normales para el resto del documento.
Siento que alguien ya debe haber resuelto este problema, y ​​probablemente más elegantemente que yo. Agradecería cualquier ejemplo de cómo usar un filtro de servlet para insertar contenido en una respuesta.
ACTUALIZADO
Respondiendo a un comentario, también estoy tratando de implementar CharResponseWrapper desde http://www.oracle.com/technetwork/java/filters-137243.html . Aquí está mi código:
PrintWriter out = response.getWriter();
CharResponseWrapper wrappedResponse = new CharResponseWrapper(
(HttpServletResponse)response);
chain.doFilter(wrappedRequest, wrappedResponse);
String s = wrappedResponse.toString();
if (wrappedResponse.getContentType().equals("text/html") &&
StringUtils.isNotBlank(s)) {
CharArrayWriter caw = new CharArrayWriter();
caw.write(s.substring(0, s.indexOf("</body>") - 1));
caw.write("WTF</body></html>");
response.setContentLength(caw.toString().length());
out.write(caw.toString());
}
else {
out.write(wrappedResponse.toString());
}
out.close();
También estoy completando la solicitud, pero ese código funciona y no debería afectar la respuesta.
La respuesta iTech funcionó parcialmente para mí y esto se basa en esa respuesta.
Pero debe tener en cuenta que parece que algunos servidores web (y AppEngine Standard) cierran el outputStream después de la primera llamada a chain.doFilter dentro de un filtro.
Entonces, cuando necesite escribir en PrintWritter previamente guardado, la transmisión se cerrará y obtendrá una pantalla en blanco. (No recibí ni siquiera un error para darme cuenta de lo que estaba sucediendo).
Así que la solución para mí fue crear un ServletOutputStream "ficticio" y volver al método getOutputStream de mi ResponseWrapper.
Estos cambios más la solución de iTech me permitieron insertar una respuesta jsp completamente procesada en html dentro de una respuesta json (escapando adecuadamente caracteres conflictivos como comillas).
Este es mi código:
Myfilter
@WebFilter({"/json/*"})
public class Myfilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//Save original writer
PrintWriter out = response.getWriter();
//Generate a response wrapper with a different output stream
ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response);
//Process all in the chain (=get the jsp response..)
chain.doFilter(request, responseWrapper);
//Parse the response
out.write("BEFORE"+responseWrapper.toString()+"AFTER"); //Just + for clear display, better use a StringUtils.concat
}
@Override
public void destroy() {}
}
Mi ResponseWrapper :
public class ResponseWrapper extends HttpServletResponseWrapper {
private StringWriter output;
public String toString() {
return output.toString();
}
public ResponseWrapper(HttpServletResponse response) {
super(response);
//This creates a new writer to prevent the old one to be closed
output = new StringWriter();
}
public PrintWriter getWriter() {
return new PrintWriter(output,false);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
//This is the magic to prevent closing stream, create a "virtual" stream that does nothing..
return new ServletOutputStream() {
@Override
public void write(int b) throws IOException {}
@Override
public void setWriteListener(WriteListener writeListener) {}
@Override
public boolean isReady() {
return true;
}
};
}
}
Tendrá que implementar HttpServletResponseWrapper para modificar la respuesta. Vea este documento http://www.oracle.com/technetwork/java/filters-137243.html , tiene un ejemplo que transforma la respuesta, que es más de lo que desea
Editar
Probé un Servlet simple con filtro de respuesta y funcionó perfectamente. El Servlet saca la Test
cadena y el filtro de respuesta anexa la cadena filtered
y finalmente, cuando corro desde el navegador, obtengo la respuesta Test filtered
que es lo que estás tratando de lograr.
Ejecuté el código siguiente en Apache Tomcat 7 y está funcionando sin excepciones.
Servlet:
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.getWriter().println("Test");
}
Filtrar:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("BEFORE filter");
PrintWriter out = response.getWriter();
CharResponseWrapper responseWrapper = new CharResponseWrapper(
(HttpServletResponse) response);
chain.doFilter(request, responseWrapper);
String servletResponse = new String(responseWrapper.toString());
out.write(servletResponse + " filtered"); // Here you can change the response
System.out.println("AFTER filter, original response: "
+ servletResponse);
}
CharResponseWrapper (exactamente como el artículo)
public class CharResponseWrapper extends HttpServletResponseWrapper {
private CharArrayWriter output;
public String toString() {
return output.toString();
}
public CharResponseWrapper(HttpServletResponse response) {
super(response);
output = new CharArrayWriter();
}
public PrintWriter getWriter() {
return new PrintWriter(output);
}
}
web.xml
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/TestServlet</url-pattern>
</servlet-mapping>
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/TestServlet/*</url-pattern>
</filter-mapping>