Сервлет-фильтр "прокси", который действует только на ответ от удаленной конечной точки
Мне нужно, чтобы некоторые HTTP-запросы были перенаправлены на веб-приложение/службу загрузки Spring Boot, а приложение-запрос Spring ничего не делает и действует как пересылка между HTTP-клиентом ( другая услуга) и запрос истинного адресата. Но когда ответ возвращается в приложение Spring (от этого адресата), мне нужно приложение Spring, чтобы иметь возможность проверить ответ и, возможно, предпринять какие-либо действия по нему. Итак:
Итак, по существу, фильтр, который действует как сквозной запрос, и только действительно делает что-либо после выполнения удаленной службы и возвращает ответ.
Моя лучшая попытка до сих пор заключалась в настройке фильтра сервлета:
@Override
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(request, response)
// How and where do I put my code?
if(responseContainsFizz(response)) {
// Send an alert (don't worry about this code)
}
}
Можно ли это сделать? Если да, где я могу поместить код, который проверяет и реагирует на ответ? С моим кодом, как это происходит, я получаю исключения, возникающие при попытке нажать контроллер из браузера:
java.lang.IllegalStateException: STREAM
at org.eclipse.jetty.server.Response.getWriter(Response.java:910) ~[jetty-server-9.2.16.v20160414.jar:9.2.16.v20160414]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_92]
rest of stack trace omitted for brevity
Любые идеи?
Ответы
Ответ 1
В документации API сервлета причина, по которой вы получаете IllegalStateException
, связана с тем, что вы пытаетесь вызвать ServletResponse.getWriter
после того, как ServletResponse.getOutputStream
уже был вызван в ответ. Таким образом, кажется, что метод, который вам нужно вызвать, это ServletResponse.getOutputStream()
.
Однако, если вы пытаетесь получить доступ к телу ответа, лучшим решением является обернуть ответ в ServletResponseWrapper
, чтобы вы могли захватить данные:
public class MyFilter implements Filter
{
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
@Override
public void destroy()
{
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
MyServletResponseWrapper responseWrapper = new MyServletResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, responseWrapper);
if (evaluateResponse(responseWrapper)) {
// Send an alert
}
}
private boolean evaluateResponse(MyServletResponseWrapper responseWrapper) throws IOException
{
String body = responseWrapper.getResponseBodyAsText();
// Perform business logic on the body text
return true;
}
private static class MyServletResponseWrapper extends HttpServletResponseWrapper
{
private ByteArrayOutputStream copyOutputStream;
private ServletOutputStream wrappedOutputStream;
public MyServletResponseWrapper(HttpServletResponse response)
{
super(response);
}
public String getResponseBodyAsText() throws IOException
{
String encoding = getResponse().getCharacterEncoding();
return copyOutputStream.toString(encoding);
}
@Override
public ServletOutputStream getOutputStream() throws IOException
{
if (wrappedOutputStream == null) {
wrappedOutputStream = getResponse().getOutputStream();
copyOutputStream = new ByteArrayOutputStream();
}
return new ServletOutputStream()
{
@Override
public boolean isReady()
{
return wrappedOutputStream.isReady();
}
@Override
public void setWriteListener(WriteListener listener)
{
wrappedOutputStream.setWriteListener(listener);
}
@Override
public void write(int b) throws IOException
{
wrappedOutputStream.write(b);
copyOutputStream.write(b);
}
@Override
public void close() throws IOException
{
wrappedOutputStream.close();
copyOutputStream.close();
}
};
}
}
}
Ответ 2
Ответ можно легко манипулировать/заменить/расширять e с помощью фильтра и оболочки ответа.
В фильтре перед вызовом chain.doFilter(request, wrapper)
вы создаете PrintWriter
для нового содержимого ответа и объекта-обертки.
После вызова chain.doFilter(request, wrapper)
выполняется манипуляция с активацией.
Оболочка используется для получения доступа к ответу как String.
Фильтр:
@WebFilter(filterName = "ResponseAnalysisFilter", urlPatterns = { "/ResponseFilterTest/*" })
public class ResponseFilter implements Filter {
public ResponseFilter() {}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
PrintWriter out = response.getWriter();
CharResponseWrapper wrapper = new CharResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, wrapper);
String oldResponseString = wrapper.toString();
if (oldResponseString.contains("Fizz")) {
// replace something
String newResponseString = oldResponseString.replaceAll("Fizz", "Cheers");
// show alert with a javascript appended in the head tag
newResponseString = newResponseString.replace("</head>",
"<script>alert('Found Fizz, replaced with Cheers');</script></head>");
out.write(newResponseString);
response.setContentLength(newResponseString.length());
}
else { //not changed
out.write(oldResponseString);
}
// the above if-else block could be replaced with the code you need.
// for example: sending notification, writing log, etc.
out.close();
}
}
Обертка ответа:
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);
}
}
Тестовый сервлет:
@WebServlet("/ResponseFilterTest/*")
public class ResponseFilterTest extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
response.getWriter().append(
"<html><head><title>replaceResponse filter</title></head><body>");
if (request.getRequestURI().contains("Fizz")) {
response.getWriter().append("Fizz");
}
else {
response.getWriter().append("Limo");
}
response.getWriter().append("</body></html>");
}
}
Тестовые URL:
Дополнительная информация и примеры об фильтрах:
http://www.oracle.com/technetwork/java/filters-137243.html#72674
http://www.leveluplunch.com/java/tutorials/034-modify-html-response-using-filter/
https://punekaramit.wordpress.com/2010/03/16/intercepting-http-response-using-servlet-filter/