サーブレットフィルターを使用して応答にコンテンツを挿入する人の例をネットとstackoverflowで検索しましたが、出力のキャプチャ/圧縮やヘッダーの変更を行う人の例しか見つけることができません。私の目標は、すべてのHTML応答の終了</ body>の直前にHTMLのチャンクを追加することです。
私は自分のPrintWriterを使用するようにHttpServletResponseWrapperを拡張するソリューションに取り組んでおり、その上で書き込みメソッドをオーバーライドしています。書き込みメソッド内で、最後の7文字を保存して、それが終了bodyタグと等しいかどうかを確認し、残りのドキュメントの通常の書き込み操作を続ける前に、HTMLチャンクと終了bodyタグを書き込みます。
誰かがすでにこの問題を解決し、おそらく私よりもエレガントに解決しているに違いないと思います。サーブレットフィルターを使用してコンテンツを応答に挿入する方法の例に感謝します。
更新しました
コメントに応答して、私は http://www.Oracle.com/technetwork/Java/filters-137243.html からCharResponseWrapperを実装しようとしています。ここに私のコードがあります:
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();
私もリクエストをラッピングしていますが、そのコードは機能し、レスポンスに影響を与えるべきではありません。
私が使用しているコードベースは、応答を処理するときにgetWriterではなくgetOutputStreamメソッドを呼び出すため、他の回答に含まれる例は役に立ちません。ライターに2回アクセスすると、OutputStreamとPrintWriterの両方で機能し、正しくエラーが発生する、より完全な回答を次に示します。これは、すばらしい例 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);
}
}
}
HttpServletResponseWrapperを実装して、応答を変更する必要があります。このドキュメントを参照してください The Essentials of Filters 、応答を変換する例があります。
編集
応答フィルターを使用した単純なサーブレットを試しましたが、完全に機能しました。サーブレットは文字列Test
を出力し、応答フィルターはそれに文字列filtered
を追加し、最後にブラウザーから実行すると、応答Test filtered
これはあなたが達成しようとしているものです。
Apache Tomcat 7で以下のコードを実行しましたが、例外なく動作しています。
サーブレット:
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.getWriter().println("Test");
}
フィルター:
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(記事とまったく同じ)
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>
ITechの回答は私にとって部分的に機能し、これはその回答に基づいています。
ただし、一部のWebサーバー(およびAppEngine標準)はoutputStreamを閉じますchain.doFilterの最初の呼び出しは、フィルター内です。
したがって、事前に保存されたPrintWritterに書き込む必要がある場合、ストリームは閉じられ、空白の画面が表示されます。 (私は何が起こっているのかを理解するためのエラーさえも受け取りませんでした)。
そのため、私にとっての解決策は、「ダミー」のServletOutputStreamを作成し、ResponseWrapperのgetOutputStreamメソッドに戻すことでした。
これらの変更とiTechのソリューションにより、完全にレンダリングされたjsp応答をjson応答内のhtmlに挿入できました(引用符などの競合する文字を適切にエスケープします)。
これは私のコードです:
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() {}
}
私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;
}
};
}
}
すばらしいです!ただし、コンテンツの長さを更新してください。
String out = new String(bytes);
// DO YOUR REPLACEMENTS HERE
out = out.replace("</head>", "WTF</head>");
response.setContentLength(out.length());
response.getOutputStream().write(out.getBytes());