Log raw request body before deserialization in Spring Boot REST controller - spring-boot

Given the following, kind of basic, REST controller in Spring Boot:
#RestController
public class NotificationController {
#PostMapping(path = "/api/notification")
public void handleNotification(#RequestBody #Valid NotificationRequest request) {
System.out.println(request.getMessage());
}
}
One requirement is to log the incoming request body before deserializing it to a NotificationRequest. We'd like to have a trace, e.g. if the request is not well-formed. My first idea was to use the HttpServletRequest directly but then I would lose the validation and automatic deserialization.
public void handleNotification(HttpServletRequest httpRequest) {
// ...
}
Is there some mechanism to log the incoming "raw" request body for this particular endpoint?

Related

Spring security request body

I'm using spring boot with spring security
I have rest controller
#RequestMapping("/foo")
public String foo(#RequestBody Foo foo) {
return foo.getBar();
}
And I've added Spring security to this endpoint
.mvcMatchers("/foo").access("#securityChecker.check(#foo)")
Now I have this security checker
#Service
public class SecurityChecker {
public boolean check(Foo foo) {
return foo != null;
}
}
the problem is that Foo is always null.
I'm guessing that it's because Jackson's filter is after Security one. Is there any way to get request body object without injecting HttpRequest object to "check" method and parsing request body? I would like to maybe have security checks after parsing JSON from the request's body.
Here is quick example of what I'm trying to do:
https://github.com/kedrigen/spring-security-request-body
You are missing #RequestBody (docs):
#PostMapping("/foo") // has to be post mapping
public String foo(#RequestBody Foo foo) {
return foo.getBar();
}
This annotation is used to have the request body read and deserialized into an Object through an HttpMessageConverter.
You are also missing #EnableWebSecurity:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { ... }
But the problem, in general, is you cannot simply do what you are trying to do.
foo that you expect in your controller has nothing to do with security and security context, so this "#securityChecker.check(#foo)" won't work.
Consider getting familiar with Referring to Beans in Web Security Expressions documentation.
Example from the docs:
public class WebSecurity {
public boolean check(Authentication authentication, HttpServletRequest request) {
...
}
}
http
.authorizeRequests(authorize -> authorize
.antMatchers("/user/**").access("#webSecurity.check(authentication,request)")
...
)
In a nutshell: this works, because Spring is aware of what authentication and request are and they exist in the context. But foo means nothing to Spring :)

Returning value from Apache Camel route to Spring Boot controller

I am calling a camel route from a Spring Boot controller. The camel route calls a REST service which returns a string value and I am trying to return that value from the camel route to the controller. Below is the Spring Boot controller:
#RestController
#RequestMapping("/demo/client")
public class DemoClientController {
#Autowired private ProducerTemplate template;
#GetMapping("/sayHello")
public String sayHello() throws Exception {
String response = template.requestBody("direct:sayHelloGet", null, String.class);
return response;
}
}
And below is my camel route:
#Component
public class MyRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:sayHelloGet")
.log("Route reached")
.setHeader(Exchange.HTTP_METHOD, simple("GET"))
.to("http://localhost:8080/demo/sayHello")
.log("${body}");
}
}
In the route, the log is printing the return value from the REST service but that String is not returned to the controller. Can anyone please suggest how to return the value to the Spring Boot controller?
The Spring Boot version I am using is 2.2.5 and Apache Camel version is 3.0.1.
See this FAQ
https://camel.apache.org/manual/latest/faq/why-is-my-message-body-empty.html
The response from http is streaming based and therefore only readable once, and then its read via the log and "empty" as the response. So either
do not log
enable stream caching
convert the response from http to a string (not streaming and re-readable safe)

Spring Data Rest - How to receive Headers in #RepositoryEventHandler

I'm using the latest Spring Data Rest and I'm handling the event "before create". The requirement I have is to capture also the HTTP Headers submitted to the POST endpoint for the model "Client". However, the interface for the RepositoryEventHandler does not expose that.
#Component
#RepositoryEventHandler
public class ClientEventHandler {
#Autowired
private ClientService clientService;
#HandleBeforeCreate
public void handleClientSave(Client client) {
...
...
}
}
How can we handle events and capture the HTTP Headers? I'd like to have access to the parameter like Spring MVC that uses the #RequestHeader HttpHeaders headers.
You can simply autowire the request to a field of your EventHandler
#Component
#RepositoryEventHandler
public class ClientEventHandler {
private HttpServletRequest request;
public ClientEventHandler(HttpServletRequest request) {
this.request = request;
}
#HandleBeforeCreate
public void handleClientSave(Client client) {
System.out.println("handling events like a pro");
Enumeration<String> names = request.getHeaderNames();
while (names.hasMoreElements())
System.out.println(names.nextElement());
}
}
In the code given I used Constructor Injection, which I think is the cleanest, but Field or Setter injection should work just as well.
I actually found the solution on stackoverflow: Spring: how do I inject an HttpServletRequest into a request-scoped bean?
Oh, and I just noticed #Marc proposed this in thecomments ... but I actually tried it :)

Spring 4 RestController - How to return jaxb object with ResponseEntity

I am using Spring #RESTController for my REST webservice. instead of returning the object of ModelAndView I am trying to return the object of ResponseEntity object in my rest method. for the Strgin type of response it is working ut when I am building ResponseEntity with a Jaxbobject it is giving me HTTP error 406
#RestController
#RequestMapping(value="/service")
public class MyController {
public #ResponseBody ResponseEntity<String> getDashBoardData() throws JAXBException {
// Some Operation
return new ResponseEntity<String>(myStringXML, responseHeaders, HttpStatus.OK);
}
}
Below is not working
#RestController
#RequestMapping(value="/service")
public class MyController {
public #ResponseBody ResponseEntity<MyJaxbClass> getDashBoardData() throws JAXBException {
// Some Operation
return new ResponseEntity<MyJaxbClass>(MyJaxbClassObject, HttpStatus.OK);
}
}
The #RestController annotation already implies the #ResponseBody annotation for all request handling methods, that is one of its purposes (it saves you from putting all those annotations there). So you can/should remove it.
Processing the return value of the method is done by a 'HandlerMethodReturnValueHandlerand the specific one which should handle this delegates to aHttpMessageConverter. It selects a specificHttpMessageConverterbased on the requested/supported response types for the current request and the support response types from theHandlerMethodReturnValueHandler`.
In general when using #EnableWebMvc or <mvc:annotation-driven /> everything should be setup automatically. The automatic setup does some detection on which libs are available (jaxb, json etc).
Based on the response code (406) you either have manually configured something wrong on the server side or the client doesn't support xml as a response type.

How can I log the JSON response of Spring 3 controllers with #ResponseBody in a HandlerInterceptorAdapter?

I have controllers that return JSON to the client. The controllers methods are marked using mvc annotation such as:
#RequestMapping("/delete.me")
public #ResponseBody Map<String, Object> delete(HttpServletRequest request, #RequestParam("ids[]") Integer[] ids) {
Spring knows to return JSON since Jackson is on the class path and the client is requesting a JSON response. I would like to log the response of these requests and all other controllers. In the past I have used an interceptor to do this. However, I got the response body from the ModelAndView. How can I get the response body in the inteceptor now that I'm using #ResponseBody? Specifically, how can I get the response body in this method?
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
You can log everything by using CustomizableTraceInterceptor
you can either set it in your application context xml config and use AOP: (log level Trace)
<bean id="customizableTraceInterceptor"
class="org.springframework.aop.interceptor.CustomizableTraceInterceptor">
<property name="exitMessage" value="Leaving $[methodName](): $[returnValue]" />
</bean>
or you can completly customize it by implementing it in Java and use the method setExitMessage():
public class TraceInterceptor extends CustomizableTraceInterceptor {
private Logger log = LoggerFactory.getLogger("blabla");
#Override
protected void writeToLog(Log logger, String message, Throwable ex) {
//Write debug info when exception is thrown
if (ex != null) {
log.debug(message, ex);
}
....
}
#Override
protected boolean isInterceptorEnabled(MethodInvocation invocation, Log logger) {
return true;
}
#Override
public void setExitMessage(String exitMessage) {
.... //Use PlaceHolders
}
}
and use the placeholders such as '$[returnValue]'. You can find the complete list in the spring api documentation.
EDIT: Also, if you want to get the value of your #ResponseBody in another interceptor, I think it's not possible until version > 3.1.1. Check this issue: https://jira.springsource.org/browse/SPR-9226

Resources