Spring Перехватчик MVC 3.2 @ResponseBody
В нашем приложении мы используем JSON для запроса и ответа. Методы контроллера аннотируются с помощью @RequestBody(). Возвращаемый объект, например. TransferResponse. Я хотел бы получить этот объект из @ResponseBody. Я установил метод postHandle перехватчика:
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws java.lang.Exception
{
....
}
Итак, как мне получить JSON в этом методе postHandle?
Спасибо заранее
GM
Ответы
Ответ 1
Как Павел Хорал уже упоминался, когда вызывается метод postHandle()
, объект тела ответа уже преобразован в JSON и написан для ответа. Вы можете попытаться написать свою собственную аннотацию и аспект, чтобы перехватить тела тела ответа контроллера.
// custom annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation {
}
// aspect
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyCustomAnnotationAspect {
@Around(value = "@annotation(org.package.MyCustomAnnotation)", argNames = "pjp")
public Object aroundAdvice(final ProceedingJoinPoint pjp) {
// this is your response body
Object responseBody = pjp.proceed();
return responseBody;
}
}
Включить поддержку аспектов AspectJ с помощью @EnableAspectJAutoProxy
Ответ 2
Наконец, у меня есть рабочее (но не элегантное) решение для этого случая.
Я думаю, что это может быть лучшее решение, но я не могу этого найти.
Сначала я был создан оболочку Request and Response, которая инкапсулирует полезную нагрузку, делая мой запрос Input Stream и Response Output Stream доступным и передислоцируемым.
Мне нужно использовать это в моем Фильтре, чтобы манипулировать как запросами, так и полезными нагрузками.
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.springframework.context.ApplicationContext;
import br.com.vivo.core.controller.impl.utils.ApplicationContextUtils;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
@WebFilter(urlPatterns = { "/*" })
public class HeadBodyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ApplicationContext applicationContext = ApplicationContextUtils.getApplicationContext();
ObjectMapper objectMapper = (ObjectMapper) applicationContext.getBean("jacksonObjectMapper");
JsonFactory jsonFactory = objectMapper.getFactory();
ByteResponseWrapper byteResponseWrapper = new ByteResponseWrapper((HttpServletResponse) response);
ByteRequestWrapper byteRequestWrapper = new ByteRequestWrapper((HttpServletRequest) request);
String jsonRequestString = new String(byteRequestWrapper.getBytes());
JsonParser requestParser = jsonFactory.createParser(jsonRequestString);
JsonNode rootRequestNode = objectMapper.readTree(requestParser);
if(rootRequestNode != null && rootRequestNode.has("body")) {
JsonNode requestBody = rootRequestNode.get("body");
writeJsonIntoRequest(byteRequestWrapper, requestBody, objectMapper);
}
chain.doFilter(byteRequestWrapper, byteResponseWrapper);
String jsonResponseString = new String(byteResponseWrapper.getBytes(), response.getCharacterEncoding());
JsonParser responseParser = jsonFactory.createParser(jsonResponseString);
JsonNode rootResponseNode = objectMapper.readTree(responseParser);
Object head = "Whoo hoo!";
ObjectNode responseObjectWrapper = objectMapper.createObjectNode();
responseObjectWrapper.put("head", objectMapper.valueToTree(head));
responseObjectWrapper.put("body", rootResponseNode);
writeJsonIntoResponse(response, responseObjectWrapper, objectMapper);
}
private void writeJsonIntoRequest(ByteRequestWrapper request,
JsonNode requestBody, ObjectMapper objectMapper) throws IOException {
String json = objectMapper.writeValueAsString(requestBody);
request.replaceRequestPayload(json.getBytes());
}
@Override
public void destroy() {
}
/**
* Escreve o json no response
*
* @param response
* @param rootNode
* @throws IOException
*/
private void writeJsonIntoResponse(final ServletResponse response, final JsonNode responseBody, final ObjectMapper objectMapper) throws IOException {
String json = objectMapper.writeValueAsString(responseBody);
// escreve o json
response.getOutputStream().write((json + "\r\n").getBytes(response.getCharacterEncoding()));
}
static class ByteResponseWrapper extends HttpServletResponseWrapper {
private PrintWriter writer;
private ByteOutputStream output;
public byte[] getBytes() {
writer.flush();
return output.getBytes();
}
public ByteResponseWrapper(HttpServletResponse response) {
super(response);
output = new ByteOutputStream();
writer = new PrintWriter(output);
}
@Override
public PrintWriter getWriter() {
return writer;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return output;
}
}
static class ByteRequestWrapper extends HttpServletRequestWrapper {
byte[] requestBytes = null;
private ByteInputStream byteInputStream;
public ByteRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream inputStream = request.getInputStream();
byte[] buffer = new byte[4096];
int read = 0;
while ( (read = inputStream.read(buffer)) != -1 ) {
baos.write(buffer, 0, read);
}
replaceRequestPayload(baos.toByteArray());
}
public byte[] getBytes() {
return requestBytes;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
return byteInputStream;
}
public void replaceRequestPayload(byte[] newPayload) {
requestBytes = newPayload;
byteInputStream = new ByteInputStream(new ByteArrayInputStream(requestBytes));
}
}
static class ByteOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos = new ByteArrayOutputStream();
@Override
public void write(int b) throws IOException {
bos.write(b);
}
public byte[] getBytes() {
return bos.toByteArray();
}
}
static class ByteInputStream extends ServletInputStream {
private InputStream inputStream;
public ByteInputStream(final InputStream inputStream) {
this.inputStream = inputStream;
}
@Override
public int read() throws IOException {
return inputStream.read();
}
}
}
Ответ 3
Как сказал Павел, вы, вероятно, не можете добраться до ответа JSON таким образом. Я считаю, что лучше всего реализовать Filter, который рассматривает ответ до его написания клиенту. Посмотрите OncePerRequestFilter для начальной точки.
Ответ 4
Поскольку вопрос был отправлен, ResponseBodyAdvice был добавлен в Spring MVC 4.1. Этот интерфейс позволяет приложениям изменять или полностью изменять корпус, прежде чем конвертор будет применен. документация для перехвата запросов также была специально обновлена для этой проблемы:
Обратите внимание, что метод postHandle для HandlerInterceptor не всегда идеально подходит для использования с методами @ResponseBody и ResponseEntity. В таких случаях HttpMessageConverter записывает и передает ответ до того, как вызывается postHandle, что делает невозможным изменение ответа, например, для добавления заголовка. Вместо этого приложение может реализовать ResponseBodyAdvice и объявить его как @ControllerAdvice bean или настроить его непосредственно на RequestMappingHandlerAdapter.