tl;dr:
How do I get the ServletResponse during ServletRequestListener.requestDestroyed?
Short Version
In JavaEE, I want to know when:
when a request starts
and when a request ends
and be able to inspect the request and response objects.
Long Version
In the ASP.NET world, if you want to know when a request starts and ends, you write an IHttpModule:
public class ExampleModuleForThisQuestion : IHttpModule
{
}
And then register your "module" in the web XML configuration file:
web.config:
<system.webServer>
<modules>
<add name="DoesntMatter" type="ExampleModuleForThisQuestion "/>
</modules>
</system.webServer>
Inside your module, you can register callback handlers for:
BeginRequest event
EndRequest event
The web server infrastructure then calls you Init method. That is your opportunity to register that you want to receive notifications when a request starts, and when a request ends:
public class ExampleModuleForThisQuestion : IHttpModule
{
public void Init(HttpApplication application)
{
application.BeginRequest += new EventHandler(beginRequest); //register the "BeginRequet" event
application.EndRequest += new EventHandler(endRequest); //register the "EndRequest" event
}
}
And now we have our callbacks when a request starts:
private void beginRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
//Record the time the request started
application.Context.Items["RequestStartTime"] = DateTime.Now;
//We can even access the Request and Response objects
application.ContenxtLog(application.Context.Request.Headers["User-Agent"]);
}
And we have our callback when a request ends:
private void endRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
//We can even access the Request and Response objects
//Get the response status code (e.g. 418 I'm a teapot)
int statusCode = application.Context.Response.StatusCode;
//Get the request method (e.g. GET, POST, BREW)
String method = application.context.Request.RequestType;
//Get the path from the request (e.g. /ViewCustomer)
String path = application.context.Request.AppRelativeCurrentExecutionFilePath'
//Get when the request started - that we recorded during "Begin Request"
DateTime requestStartTime = (DateTime)application.Context.Items["RequestStartTime"];
//And we can modify the response
if ((DateTime.Now - requestStartTime).TotalSeconds = 17)
application.Context.Response.StatusCode = 451;
}
The Java Almost-Equivalent is ServletRequestListener
In Java, apparently the corresponding technique is to create and object that implements the ServletRequestListener interface:
#WebListener
public class ExampleListenerForThisQuestion
implements javax.servlet.ServletRequestListener {
}
and register our listener with the application server by including it in our web XML configuration file:
web.xml
<listener>
<listener-class>ExampleListenerForThisQuestion</listener-class>
</listener>
Now we can implement the requestInitialized and requestDestroyed methods to get when a request starts and ends:
public class ExampleListenerForThisQuestion
implements javax.servlet.ServletRequestListener {
#Override
public void requestInitialized(ServletRequestEvent sre) {
ServletRequest sr = sre.getServletRequest();
sr.setAttribute("requestStartTicks", getCurrentTickCount());
HttpServletRequest request = (HttpServletRequest) sr;
// e.g. "PUT /Customers/1234"
System.out.printf("%s %s\r\n", request.getMethod());
}
#Override
public void requestDestroyed(ServletRequestEvent sre) {
ServletRequest sr = sre.getServletRequest();
long requestStartTicks = (long)sr.getAttribute("requestStartTicks");
HttpServletResponse response = (HttpServletRequest)...nothing, because i don't know how...
// e.g. "226 IM Used"
System.out.printf("%d %s\r\n", response.getStatus(), response.getStatusDescription());
}
}
But how do we get the response?
Now that I'm notified when the response ends, I need the result of that request:
I need the HTTP status code (e.g., 424)
I need the HTTP status description (e.g., Failed Dependency)
I need to inspect response headers
I need to modify response headers
You notice the line in my code above:
HttpServletResponse response = (HttpServletRequest)...nothing, because i don't know how...
How can I get hold of the response?
You can create a Filter instead of a listener.
Filters allow you to create wrappers around request processing. See the documentation on that topic.
For HTTP, you can use HTTPFilter. This could look like the following:
#WebFilter("/*")//or via deployment descriptor
public class YourFilter extends HttpFilter{ //or just Filter for general (non-HTTP) processing
#Override
public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {//for generic filters, use ServletRequest/ServletResponse instead
//before request processing
chain.doFilter(req, res);//calls other filters and processes request
//after request processing
//you can use res here
}
}
If you do not call chain.doFilter, other filters and the servlet will not be executed.
If you prefer declaring the filter in your deployment descriptor (web.xml), you can do that as well:
<filter>
<filter-name>yourFilter</filter-name>
<filter-class>your.FilterClass</filter-class>
</filter>
<filter-mapping>
<filter-name>yourFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Related
Working on a http proxy servlet which is used to forward the request to another remote server.
NOTE: Cannot change the URL Pattern
Snippet of web.xml
<servlet>
<servlet-name>SomeServlet</servlet-name>
<servlet-class>SomeServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SomeServlet</servlet-name>
<url-pattern>/pattern1/*</url-pattern>
</servlet-mapping>
SomeServlet.Java
Validates the requests to check for specific content in request body and if matches forward to remote server. (Working as expected)
but if the condition fails, request processing should continue as is.
what I have now,
public class proxyServlet extends HttpServlet {
/* it has all the init other configs*/
#Overrirde
protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws ServletException, IOException {
/* If true, returns which endpoint for remote server else returns null */
String endPoint = checkForConditions(servletRequest);
if(endPoint == null) {
return; // this is not letting the app to continue with flow.
} else {
HttpRequest newR = new BasicHttpRequest(method, endPoint);
doExecute(servletRequest, newR)
}
}
}
I cannot create a new request and forward since server will again catch the pattern and do it in a infinite loop.
What is the best way to accomplish this? Any suggestions are greatly appreciated.
UPDATE-Solved: As per the comment from #balusc, Used servlet-filter and on condition use a HTTPClient to send POST request to remote server else chain.doFilter(...)
I need to pass some parameters from javascript client to the Java endpoint in Tomcat.
With Tomcat you can annote your java class with #ServerEndpoint(value="/websocket") and then this class will be the endpoint for your websocket.
I.e. every time when a javascript calls new WebSocket("ws://localhost/websocket") your annoted class will be instantiated.
Now I want to pass a short string parameter (or int, does not really matter) from the javascript client side to my annoted class preferably during instantiation (i.e. in the constructor).
How shall I do this?
I have thought of putting it into the url, but then I would need to do something else instead of the annotation thus it does not feel too safe.
you could pass it as a header and Java tomcat endpoint which is exposing web socket has to read the header and set it to user properties map of endpoint config
Example
public class WsHandShakeInterceptor extends ServerEndpointConfig.Configurator {
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
super.modifyHandshake(sec, request, response);
setUserHeadersAsProps(sec, request);
}
private void setUserHeadersAsProps(ServerEndpointConfig sec, HandshakeRequest request) {
Map<String, List<String>> headers = request.getHeaders();
if (headers != null) {
List<String> userNameHeader = headers.get(HeadersConst.OIDC_CLAIM_USERNAME);
if (userNameHeader != null && !userNameHeader.isEmpty()) {
sec.getUserProperties().put(HeadersConst.X_FORWARDED_USER, userNameHeader.get(0));
}
}
}
}
How do I properly close a websocket and and provide a clean, informative response to the client when an internal error occurs on my server? In my current case, the client must provide a parameter when it connects, and I am trying to handle incorrect or missing parameters received by OnOpen.
This example suggests I can just throw an exception in OnOpen, which will ultimately call OnError where I can close with a reason and message. It kinda works, but the client only receives an EOF, 1006, CLOSE_ABNORMAL.
Also, because I have found no other discussion, I can't tell what might be best practice.
I'm using the JSR-356 spec, as follows:
#ClientEndpoint
#ServerEndpoint(value="/ws/events/")
public class WebSocketEvents
{
private javax.websocket.Session session;
private long token;
#OnOpen
public void onWebSocketConnect(javax.websocket.Session session) throws BadRequestException
{
logger.info("WebSocket connection attempt: " + session);
this.session = session;
// this throws BadRequestException if null or invalid long
// with short detail message, e.g., "Missing parameter: token"
token = HTTP.getRequiredLongParameter(session, "token");
}
#OnMessage
public void onWebSocketText(String message)
{
logger.info("Received text message: " + message);
}
#OnClose
public void onWebSocketClose(CloseReason reason)
{
logger.info("WebSocket Closed: " + reason);
}
#OnError
public void onWebSocketError(Throwable t)
{
logger.info("WebSocket Error: ");
logger.debug(t, t);
if (!session.isOpen())
{
logger.info("Throwable in closed websocket:" + t, t);
return;
}
CloseCode reason = t instanceof BadRequestException ? CloseReason.CloseCodes.PROTOCOL_ERROR : CloseReason.CloseCodes.UNEXPECTED_CONDITION;
try
{
session.close(new CloseReason(reason, t.getMessage()));
}
catch (IOException e)
{
logger.warn(e, e);
}
}
}
Edit: The exception throwing per linked example seems weird, so now I am catching exception within OnOpen and immediately doing
session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "some text"));
Edit: This turned out to be correct, though a separate bug disguised it for a while.
Edit2: Clarification: HTTP is my own static utility class. HTTP.getRequiredLongParameter() gets query parameters from the client's initial request by using
session.getRequestParameterMap().get(name)
and does further processing.
To develop the points I mentioned, your problem about "how to handle a required parameter", I can see following options. First of all, let's consider the endpoint:
#ServerEndpoint(value = "/websocket/myendpoint",
configuration = MyWebsocketConfiguration.class)
public class MyEndpoint{
// #OnOpen, #OnClose, #OnMessage, #OnError...
}
Filtering
The first contact between a client and a server is a HTTP request. You can filter it with a filter to prevent the websocket handshake from happening. A filter can either block a request or let it through:
import javax.servlet.Filter;
public class MyEndpointFilter implements Filter{
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// nothing for this example
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// if the connection URL is /websocket/myendpoint?parameter=value
// feel free to dig in what you can get from ServletRequest
String myToken = request.getParameter("token");
// if the parameter is mandatory
if (myToken == null){
// you can return an HTTP error code like:
((HttpServletResponse) response).setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
// if the parameter must match an expected value
if (!isValid(myToken)){
// process the error like above, you can
// use the 403 HTTP status code for instance
return;
}
// this part is very important: the filter allows
// the request to keep going: all green and good to go!
chain.doFilter(request, response);
}
#Override
public void destroy() {
//nothing for this example
}
private boolean isValid(String token){
// how your token is checked? put it here
}
}
If you are using a filter, you must add it in your web.xml:
<web-app ...>
<!-- you declare the filter here -->
<filter>
<filter-name>myWebsocketFilter</filter-name>
<filter-class>com.mypackage.MyEndpointFilter </filter-class>
<async-supported>true</async-supported>
</filter>
<!-- then you map your filter to an url pattern. In websocket
case, it must match the serverendpoint value -->
<filter-mapping>
<filter-name>myWebsocketFilter</filter-name>
<url-pattern>/websocket/myendpoint</url-pattern>
</filter-mapping>
</web-app>
the async-supported was suggested by BalusC in my question to support asynchronous message sending.
TL,DR
if you need to manipulate GET parameters provided by the client at the connection time, Filter can be a solution if you are satisfied with a pure HTTP answer (403 status code and so on)
Configurator
As you may have noticed, I have added configuration = MyWebsocketConfiguration.class. Such class looks like:
public class MyWebsocketConfigurationextends ServerEndpointConfig.Configurator {
// as the name suggests, we operate here at the handshake level
// so we can start talking in websocket vocabulary
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
// much like ServletRequest, the HandshakeRequest contains
// all the information provided by the client at connection time
// a common usage is:
Map<String, List<String>> parameters = request.getParameterMap();
// this is not a Map<String, String> to handle situation like
// URL = /websocket/myendpoint?token=value1&token=value2
// then the key "token" is bound to the list {"value1", "value2"}
sec.getUserProperties().put("myFetchedToken", parameters.get("token"));
}
}
Okay, great, how is this different from a filter? The big difference is that you're adding here some information in the user properties during the handshake. That means that the #OnOpen can have access to this information:
#ServerEndpoint(value = "/websocket/myendpoint",
configuration = MyWebsocketConfiguration.class)
public class MyEndpoint{
// you can fetch the information added during the
// handshake via the EndpointConfig
#OnOpen
public void onOpen(Session session, EndpointConfig config){
List<String> token = (List<String>) config.getUserProperties().get("myFetchedToken");
// now you can manipulate the token:
if(token.isEmpty()){
// for example:
session.close(new CloseReasons(CloseReason.CloseCodes.CANNOT_ACCEPT, "the token is mandatory!");
}
}
// #OnClose, #OnMessage, #OnError...
}
TL;DR
You want to manipulate some parameters but process the possible error in a websocket way? Create your own configuration.
Try/catch
I also mentioned the try/catch option:
#ServerEndpoint(value = "/websocket/myendpoint")
public class MyEndpoint{
#OnOpen
public void onOpen(Session session, EndpointConfig config){
// by catching the exception and handling yourself
// here, the #OnError will never be called.
try{
Long token = HTTP.getRequiredLongParameter(session, "token");
// process your token
}
catch(BadRequestException e){
// as you suggested:
session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "some text"));
}
}
// #OnClose, #OnMessage, #OnError...
}
Hope this help
I believe I should have placed...
session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "some text"));
...right where the error occurs, within #OnOpen(). (For generic errors, use CloseCodes.UNEXPECTED_CONDITION.)
The client receives:
onClose(1003, some text)
This is, of course, the obvious answer. I think I was misled, by the example cited, into throwing an exception from #OnOpen(). As Remy Lebeau suggested, the socket was probably closed by this, blocking any further handling by me in #OnError(). (Some other bug may have obscured the evidence that was discussed.)
I'm working on my first web project using tomcat, jsp, servlets and log4j and I have a demo of using Command design pattern, which I'm interested in. I have one Controller which accepts doGet and doPost methods and then proccesses requests to CommandContainer which finds appropriate Command, executes it, gets path to resource and forwards the client to it.
public abstract class Command implements Serializable {
private static final long serialVersionUID = 8879403039606311780L;
public abstract String execute(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException;
}
CommandContainer which manages Commands:
public class CommandContainer {
private static final Logger LOG = Logger.getLogger(CommandContainer.class);
private static Map<String, Command> commands = new TreeMap<String, Command>();
static {
// common commands
commands.put("login", new LoginCommand());
commands.put("logout", new LogoutCommand());
commands.put("viewSettings", new ViewSettingsCommand());
commands.put("noCommand", new NoCommand());
// client commands
commands.put("listMenu", new ListMenuCommand());
// admin commands
commands.put("listOrders", new ListOrdersCommand());
LOG.debug("Command container was successfully initialized");
LOG.trace("Number of commands --> " + commands.size());
}
public static Command get(String commandName) {
if (commandName == null || !commands.containsKey(commandName)) {
LOG.trace("Command not found, name --> " + commandName);
return commands.get("noCommand");
}
return commands.get(commandName);
}
The only Controller I have:
public class Controller extends HttpServlet {
private static final long serialVersionUID = 2423353715955164816L;
private static final Logger LOG = Logger.getLogger(Controller.class);
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
process(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
process(request, response);
}
private void process(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
LOG.debug("Controller starts");
// extract command name from the request
String commandName = request.getParameter("command");
LOG.trace("Request parameter: command --> " + commandName);
// obtain command object by its name
Command command = CommandContainer.get(commandName);
LOG.trace("Obtained command --> " + command);
// execute command and get forward address
String forward = command.execute(request, response);
LOG.trace("Forward address --> " + forward);
LOG.debug("Controller finished, now go to forward address --> " + forward);
// if the forward address is not null go to the address
if (forward != null) {
RequestDispatcher disp = request.getRequestDispatcher(forward);
disp.forward(request, response);
}
}
}
I'am using Controller in jsp in the next way:
...
<form id="login_form" action="controller" method="post">
<input type="hidden" name="command" value="login"/>
...
</form>
And web.xml file:
<servlet>
<servlet-name>Controller</servlet-name>
<servlet-class>com.mycompany.web.Controller</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Controller</servlet-name>
<url-pattern>/controller</url-pattern>
</servlet-mapping>
I don't understand how to implement Post-Redirect-Get pattern with Command pattern, because every time request comes to controller it uses process() method and seems that it doesnt matter GET or POST is used in JSP. And then would you help understand the need of use Command pattern ? What if I will use multiple servlets like LoginServlet, LogoutServlet, ViewSettingsServlet instead of one Controller - is it would be a bad idea because then I need to hardcode them in jsp forms as actions ? All this questions just confusing me, cos I'm a starter, so please jelp me understand all this.
Well, currently, your command returns a String: the name of the JSP to forward to. If I understand correctly, you also want to be able to redirect instead of forwarding. So you need to tel the servlet that the returned value if not a view name to forward to, but a URL to redirect to.
There are various ways to do that. You could for example return an object containing the type of action to do (FORWARD or REDIRECT), and the view name or URL. Or you could return a String like redirect:/foo/bar, which means that /foo/bar is a URL to redirect to, and not a view name.
But the best solution would probably to avoid reinventing the wheel, and use an existing MVC framework rather than implementing one yourself: Spring MVC, Stripes, Struts, etc. all provide much more than what you have there, and in a much better way. In particular, using a request parameter to choose the command is not a very good choice. Using the path is a much better idea.
You could also simply use multiple servlets, which would be better than your current solution. You would lose the front controller though, which typically contains code that is common to all commands: internationalization, security checks, etc.
Hello I've been trying to figure out generic way to log http requests in my application, so far no luck, here is how I handle the logging right now i.e:
#RequestMapping(value="register", method = RequestMethod.POST)
#ResponseBody
public String register(#RequestParam(value="param1",required=false) String param1, #RequestParam("param2") String param2, #RequestParam("param3") String param3, HttpServletRequest request){
long start = System.currentTimeMillis();
logger.info("!--REQUEST START--!");
logger.info("Request URL: " + request.getRequestURL().toString());
List<String> requestParameterNames = Collections.list((Enumeration<String>)request.getParameterNames());
logger.info("Parameter number: " + requestParameterNames.size());
for (String parameterName : requestParameterNames){
logger.info("Parameter name: " + parameterName + " - Parameter value: " + request.getParameter(parameterName));
}
//Some processing logic, call to the various services/methods with different parameters, response is always String(Json)
String response = service.callSomeServiceMethods(param1,param2,param3);
logger.info("Response is: " + response);
long end = System.currentTimeMillis();
logger.info("Requested completed in: " + (end-start) + "ms");
logger.info("!--REQUEST END--!");
return response;
}
So what I do right now for different controllers/methods is copy everything from beginning of the inside of the method until the processing logic which differs from method to method and then copy everything from below of that as showed in above template.
It is kind of messy, and there is a lot of code repetition(which I don't like). But I need to log everything.
Does anyone have more experience with this kinds of logging, can anyone shed some light on this?
EDIT: Also, see #membersound's comment on this answer, which improves this answer.
Spring supports this. See CommonsRequestLoggingFilter. If using Spring Boot, just register a bean of that type and Boot will apply it to the filter chain. Like:
#Bean
public Filter logFilter() {
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setMaxPayloadLength(5120);
return filter;
}
Also, this logging filter requires the log level be set to DEBUG. E.g. do this in a logback.xml with:
<logger name="org.springframework.web.filter.CommonsRequestLoggingFilter" level="DEBUG"/>
Use an interceptor:
extend HandlerInterceptorAdapter and override preHandle
define it with <mvc:interceptors> in dispatcher-servlet.xml
It will run for every request.
The main issue with reading request is that as soon as the input stream is consumed its gone whoof... and cannot be read again. So the input stream has to be cached. Instead of writing your own classes for caching (which can be found at several places on web), Spring provides a couple of useful classes i.e. ContentCachingRequestWrapper and ContentCachingResponseWrapper. These classes can be utilized very effectively, for example, in filters for logging purposes.
Define a filter in web.xml:
<filter>
<filter-name>loggingFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>loggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Since the filter is declared as DelegatingFilterProxy, it can be declared as a bean using #Component or #Bean annotations. In the loggingFilter's doFilter method, wrap the request and response with spring provided classes before passing it to the filter chain:
HttpServletRequest requestToCache = new ContentCachingRequestWrapper(request);
HttpServletResponse responseToCache = new ContentCachingResponseWrapper(response);
chain.doFilter(requestToCache, responseToCache);
String requestData = getRequestData(requestToCache);
String responseData = getResponseData(responseToCache);
The input stream will be cached in the wrapped request as soon as the input stream is consumed after chain.doFilter(). Then it can be accessed as below:
public static String getRequestData(final HttpServletRequest request) throws UnsupportedEncodingException {
String payload = null;
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
}
}
return payload;
}
However, things are a bit different for response. Since the response was also wrapped before passing it to the filter chain, it will also be cached to the output stream as soon as it is written on its way back. But since the output stream will also be consumed so you have to copy the response back to the output stream using wrapper.copyBodyToResponse(). See below:
public static String getResponseData(final HttpServletResponse response) throws IOException {
String payload = null;
ContentCachingResponseWrapper wrapper =
WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
wrapper.copyBodyToResponse();
}
}
return payload;
}
Hope it helps!
Here's a small library I wrote you can use: spring-mvc-logger
I made it available via maven central:
<dependency>
<groupId>com.github.isrsal</groupId>
<artifactId>spring-mvc-logger</artifactId>
<version>0.2</version>
</dependency>
Adding to what #B.Ali has answered. If you are using this in a spring asynchronous request (serlvet 3.0 or greater) handling scenario, then the following code is what worked for me.
public class OncePerRequestLoggingFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
boolean isFirstRequest = !isAsyncDispatch(request);
HttpServletRequest requestToUse = request;
HttpServletResponse responseToUse = response;
// The below check is critical and if not there, then the request/response gets corrupted.
// Probably because in async case the filter is invoked multiple times.
if (isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) {
requestToUse = new ContentCachingRequestWrapper(request);
}
if (isFirstRequest && !(response instanceof ContentCachingResponseWrapper)) {
responseToUse = new ContentCachingResponseWrapper(response);
}
filterChain.doFilter(requestToUse, responseToUse);
if (!isAsyncStarted(request)) {
ContentCachingResponseWrapper responseWrapper =
WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
responseWrapper.copyBodyToResponse(); // IMPORTANT to copy it back to response
}
}
#Override
protected boolean shouldNotFilterAsyncDispatch() {
return false; // IMPORTANT this is true by default and wont work in async scenario.
}
}
As any tech answer ... it depends ..
on the tech stack you are using and what your requirements are.
for example the more generic you want to make your logging, the further upfront you would want to do it. in your case, you are logging only requests which are logging enabled and being handled in the spring context. So you could be "missing" other requests.
I would look at the container or the web server you are using to run your app. That will remove this dependency on Spring. Plus containers provide you the flexibility of plugging in a logging provider and then configuring the format of the log outside code.
For example, if you are using Apache Web server, use Apache web server logging to log all HTTP requests in the access logging layer. But be careful, some of the logging options have performance penalties. Log only what you seriously need for an access pattern monitoring perspective.
If you are using tomcat, then tomcat also will allow you to log stuff. Search for Access Valve in the tomcat documentation for the tomcat you are using. That will open up a world of possibilities.
More extensive logging should be the domain of the exception strategy ie the kind of detail you want to see when a problem occurs in the system.