【发布时间】:2018-11-28 15:50:50
【问题描述】:
我有一个 Spring Boot 应用程序。 我更改每个帖子请求的请求正文。 是否可以在请求到达控制器之前修改请求正文。 请举个例子。
【问题讨论】:
-
我认为只允许设置请求参数僵尸。就我而言,修改请求正文。
标签: java spring spring-boot
我有一个 Spring Boot 应用程序。 我更改每个帖子请求的请求正文。 是否可以在请求到达控制器之前修改请求正文。 请举个例子。
【问题讨论】:
标签: java spring spring-boot
简答题
是的,但不容易。
详情
我知道更改请求正文的三个选项
“之前”它到达控制器中的处理程序方法;
既然你已经在使用 spring-boot, 选项3,自定义Spring HandlerInterceptor, 似乎是您的最佳选择。
这是一个指向 Baeldung Article 的链接,涵盖 spring HandlerInterceptors。
Baeldung 文章不是您问题的完整答案
因为你只能读取一次HttpServletRequest返回的InputStrem。
您需要创建一个扩展 HttpServletRequest 的包装类
并将您的包装类中的每个请求包装在您的自定义 HandlerInterceptor 或自定义过滤器中(过滤器可能是这里的方法)。
包装类
HttpServletRequest InputStreamByteArrayOutputStream。toByteArray 从流中检索实际的byte[]。getInputStream 方法。getInputStream 时,将byte[] 包装在一个ByteArrayInputStream 中。返回此信息流。如何包装请求
【讨论】:
另一种选择是向 HttpServletRequest 对象添加一个属性。之后,您可以使用 @RequestAttribute 注释在 Controller 类中读取该属性。
在拦截器中
@Component
public class SimpleInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws ServletException, IOException {
String parameter = request.getParameter("parameter");
if (parameter == "somevalue") {
request.setAttribute("customAttribute", "value");
}
return true;
}
}
在控制器中
@RestController
@RequestMapping("")
public class SampleController {
@RequestMapping(value = "/sample",method = RequestMethod.POST)
public String work(@RequestBody SampleRequest sampleRequest, @RequestAttribute("customAttribute") String customAttribute) {
System.out.println(customAttribute);
return "This works";
}
}
这具有不修改请求正文的优点。
【讨论】:
SampleController 中指定SimpleInterceptor 吗?如果你定义了多个拦截器呢?
我的回答是使用 HTTP 过滤器。
RequestFilter.java
@Component
public class RequestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
RequestWrapper wrappedRequest = new RequestWrapper((HttpServletRequest) request);
chain.doFilter(wrappedRequest, response);
}
@Override
public void destroy() {
}
}
RequestWrapper.java
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
private ObjectMapper objectMapper = new ObjectMapper();
public RequestWrapper(HttpServletRequest request) throws IOException {
// So that other request method behave just like before
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
// Store request body content in 'requestBody' variable
String requestBody = stringBuilder.toString();
JsonNode jsonNode = objectMapper.readTree(requestBody);
//TODO -- Update your request body here
//Sample
((ObjectNode) jsonNode).remove("key");
// Finally store updated request body content in 'body' variable
body = jsonNode.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
【讨论】:
一种方法是通过反思。 ProceedingJoinPoint 包含传递给方法的 args 对象
@Aspect
@Component
public class AopInterceptor {
@Around(value = "@annotation(xyz.rpolnx.spring.web.poc.annotation.AopIntercepExample)")
public Object handler(final ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Object[] args = joinPoint.getArgs();
Class<?> someClass = args[0].getClass();
Field field = someClass.getDeclaredField("custom");
field.setAccessible(true);
field.set(args[0], "custom");
field.setAccessible(false);
return joinPoint.proceed();
}
}
@RestController
public class SimpleController {
@PostMapping("/aop")
@AopIntercepExample
public Person handleAopIntercept(@RequestBody Person nodes) {
return nodes;
}
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AopIntercepExample {
}
public class Person {
private String name;
private String id;
private String custom;
}
【讨论】:
这是我使用 RequestBodyAdvice 实现它的方法:
@ControllerAdvice
public class CustomRequestBodyAdvice implements RequestBodyAdvice {
一个。支持:在这里,您可以通过指定请求主体的类型来控制您定位的控制器,以及更好的请求主体
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
log.info("In supports() method of {}", getClass().getSimpleName());
return methodParameter.getContainingClass() == AuthorController.class && type.getTypeName() == AuthorDTO.class.getTypeName();
}
b. beforeBodyReady
<!-- language: lang-js -->
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
log.info("In beforeBodyRead() method of {}", getClass().getSimpleName());
return httpInputMessage;
}
c。 afterBodyRead: 这里是你可以修改请求正文的地方
<!-- language: lang-js -->
@Override
public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
log.info("In afterBodyRead() method of {}", getClass().getSimpleName());
if (body instanceof AuthorDTO) {
AuthorDTO authorDTO = (AuthorDTO) body;
authorDTO.setName("Test");
return authorDTO;
}
return body;
}
d。处理空体
<!-- language: lang-js -->
@Override
public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
log.info("In handleEmptyBody() method of {}", getClass().getSimpleName());
return body;
}
来源:http://www.javabyexamples.com/quick-guide-to-requestbodyadvice-in-spring-mvc
【讨论】:
type.getTypeName() == AuthorDTO.class.getTypeName() 这是正确的吗?我认为你应该使用String#equals。
== 来测试字符串。