We have REST services exposed via Spring MVC. We use a HandlerExceptionResolver to log exceptions. We currently log the following:
The exception and its stack trace
The URL
The request headers
It would make debugging easier if we could also log the JSON post data as well. Any suggestions on how to get this?
Add this to the class representing the configuration for the application:
import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.filter.AbstractRequestLoggingFilter;
....
#Bean
public Filter loggingFilter(){
AbstractRequestLoggingFilter f = new AbstractRequestLoggingFilter() {
#Override
protected void beforeRequest(HttpServletRequest request, String message) {
System.out.println("beforeRequest: " +message);
}
#Override
protected void afterRequest(HttpServletRequest request, String message) {
System.out.println("afterRequest: " +message);
}
};
f.setIncludeClientInfo(true);
f.setIncludePayload(true);
f.setIncludeQueryString(true);
f.setBeforeMessagePrefix("BEFORE REQUEST [");
f.setAfterMessagePrefix("AFTER REQUEST [");
f.setAfterMessageSuffix("]\n");
return f;
}
you may have to comment out
f.setIncludePayload(true);
You need a filter that would save request body when it's being read and provide the saved data to your exception logger later.
Spring contains AbstractRequestLoggingFilter that does the similar thing. Though it's not directly suitable for your problem, you can use it as a reference to implement your own filter.
There is no easy way to log the payload of the request/response. You can use a java web filter to intercept all the requests and responses and read the JSON data from the stream. But there is one problem, when you will read data from the stream the actual data will be exhausted from stream.
Therefore, you have to implement the wrapper of actual request and response object. Only the copied version of request response will be logged. We have implemented similar solution like follows and it satisfied our requirement:
http://www.wetfeetblog.com/servlet-filer-to-log-request-and-response-details-and-payload/431
http://angelborroy.wordpress.com/2009/03/04/dump-request-and-response-using-javaxservletfilter/
Related
I am trying to make an API with Jetty Server, and I have this simple GET request:
#GET
public String helloWorld(){
return "Hello world";
}
In order to make a POST request, I assume that one must save the input to the Jetty server. I have tried to research for quite a while, but found nothing.
I imagine something like this:
#POST
public void Save(String stringToSave) {
// Save to DB?
}
You could likely google this but let me give you a quick overview. A Servlet is a chunk of code that is normally run during an HTTP action - GET, POST, etc. It is the original technology of the JavaEE world, having been released in the late 1990's.
A simple Java servlet, using modern annotations, would look something like:
#WebServlet(name = "SampleServlet", urlPatterns = "/sampleServlet")
public class SampleServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// called when an HTTP POST is sent
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// called when an HTTP GET is sent
}
}
The important parts to note are that the class extends HttpServlet and that you have to write code to pull data out of the request and push it into the response. This isn't bad to do but it does have to be done.
JAX-RS is a newer standard, aimed simplifying the creation of REST services. It too is a chunk of code that runs during an HTTP interaction.
A simple example of this would be:
#Path("/sampleService")
public class SampleService{
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#POST
#Path("/v1/hello")
public Response sayHello( SomeObject someobject ) {
The code here is both simpler and a bit more complex. The use of annotations helps determine the path that the service exists on a URL (in this case /sampleService/v1/hello), the HTTP method, and the Content-Type for both the request and response. Additionally, if the SomeObject object is defined correctly, the JAX-RS framework will automatically deserialize the incoming JSON or XML payload into an object for you.
The Response object contains the HTTP response code (perhaps a teapot) and a response body. In this example, the body will be automatically serialized back to the requestor in a way that matches the Accept header of the HTTP request (i.e., JSON for an application/json Accept header and XML for application/xml).
Note that while not directly related the JAX-RS framework takes advantage of the Servlet framework. Indeed in JAX-RS you can access the HttpServletRequest and HttpServletResponse object in your methods.
Which way is "better"? In general I would recommend using JAX-RS where possible as it is the newer standard and is a bit easier to implement. However, if you do any work in the JavaEE world you're very likely to run into Servlet code so it's important to understand it too.
Note that both Servlets and JAX-RS require an application server of some sort. Jetty is one of those. Another very common one is Tomcat. The application server sets up the environment for your code and listens for incoming HTTP messages. When it gets one it looks to see if it knows how to handle the URL and routes to the appropriate place. In the servlet world the server routes solely on the URL. In the JAX-RS world the server routes on the URL and, if specified by the #Consumes annotation, the HTTP Content-Type header too.
There is much more but let's start there and see if it answers what you're after.
I need to be able to modify the HTTP response body of the response that I am getting when someone hits my Service API. I tried using ConatinerResponseFilter to modify the body, but I believe it will only modify the headers and not the response body. Can someone tell me how I can modify the HTTP response body ,message and the status?
It could be achieved with a WriterInterceptor:
#Provider
public class CustomWriterInterceptor implements WriterInterceptor {
#Override
public void aroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
OutputStream outputStream = context.getOutputStream();
// Manipulate the HTTP entity using the OutputStream
context.setOutputStream(outputStream);
context.proceed();
}
}
In this answer you will find an example of how to modify a JSON sent in the request payload using Jackson (the same idea can be used to manipulate response payload).
The trick is to use a wrapper because body when read as a stream becomes in accessible
Modify HttpServletRequest body
Check this or just check online for modify body in filter
Note :if u are doing a web service then using frameworks like CXF makes it easy modify
I have a web service method as follow (deployed on WebLogic 12.2.1), which I can receive the JSON request body in the POJO object "requestParameters":
#POST
#SessionChecker
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("LogIn")
public Response logIn(#Context HttpServletRequest request, Parameters requestParameters) {
....
}
I have a filter that I want to intercept the request before the above web service method is called.
#Provider
#SessionChecker
public class CheckSessionFilter implements ContainerRequestFilter {
#Context
private HttpServletRequest servletRequest;
#Override
public void filter(ContainerRequestContext requestContext) throws WebApplicationException {
....
}
}
In the filter() method, how do I get the JSON message body into the POJO object of type Parameters? I just need to get one attribute from the JSON message. After the filter is done, the JSON message should pass on to the web service method without change.
Thanks in advance.
Here's the problem. When your filter is hit, the request stream (InputStream) hasn't been read yet. So if you try to read it, then Jersey will not be able to read it, as a stream can only be read once, so it will be empty.
Jersey actually offers a solution to this. The ContainerRequestContext, is actually an instance of Jersey specific ContainerRequest. If you look at the linked API, you will find a bufferEntity() method. This allows us to read the entity, and Jersey will be able to read it again. So your first step is to make that call
#Override
public void filter(ContainerRequestContext requestContext)
ContainerRequest cr = (ContainerRequest) requestContext;
cr.bufferEntity();
}
Now you can get the entity. If you look at the API for ContainerRequest, there are also methods to readEntity(..). If you are familiar with the JAX-RS Client API, you may have before used Response#readEntity(...class) to read the response entity. The ContainerRequest#readEntity(..) works pretty much the same way.
So if you know what the JSON format is supposed to be, and you have the POJO, you could do
POJO pojo = cr.readEntity(POJO.class);
Otherwise, if the format will change from request to request, you could extract the data as a map
Map<String, Object> json = cr.readEntity(new GenericType<Map<String, Object>>(){});
UPDATE
If you are using one JAX-RS APIs, and not Jersey specific APIs, then the above is not doable. You will instead need to read the stream to get the JSON, and set the stream back, so that Jersey can read it. If might look something like
InputStream entityIn = requestContext.getEntityStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// write `entityIn` to `baos`
byte[] bytes = baos.toByteArray();
POJO pojo = new ObjectMapper().readValue(bytes, POJO.class);
// do something with POJO
requestContext.setEntityStream(new ByteArrayInputStream(bytes));
Of course you will need to some JSON deserializer to do this. I just used Jackson in the example.
It's not as elegant as the first example, but you don't have much option if you are strictly sticking the JAX-RS APIs. If you can I would suggest just adding the Jersey dependencies to your project as provided (compile-time) so that you can use the APIs, since you are using Jersey with WebLogic anyway.
As I have noticed, in my CXF JaxRS service, if I throw an exception in say READ phase (IN interceptor) and do not provide any default FaultOutInterceptor, the XMLFaultOutInterceptor takes care of building the response as it should be returned (which is always an XML).
Now, I would like to be able to return a response in the format in which the service was requested : JSON or XML or otherwise.
I found something like this on the web:
public class JsonFaultOutHandlerInterceptor extends JAXRSOutInterceptor
{
public JsonFaultOutHandlerInterceptor() {
getBefore().add(LoggingOutInterceptor.class.getName());
}
#Override
public void handleMessage(Message message) {
...
message.getInterceptorChain().abort();
}
}
I have configured it in the outInterceptor, should I conditionally abort (if the request type was application/Json) or not abort(if the request type was application/xml) the interceptor chain? (I'm not sure if the request type information is already available. Also, somehow, aborting the chain doesn't seem very correct)
Had the response reached the JAXRS filters, using ExceptionMapper<T> I would have beautifully handled the response. But when the exception occurs in the INinterceptor, I am a little lost.
What would be a good way to be able to define a FaultOutInterceptor?
so lets say, i am listening on URL v1/shop/{PATH_PARAM}/status.xml. Whenever, a request is made at v1/shop/some_value/status.xml It will be redirected to my method.
But lets say, a user does a mistake in this path ? Jersey would by itself send an response of "Bad Request" or "Un implemented" method or something like that. Is there a way that instead jersey sending this response, I can handle those reponse ? lets say i will create a new function which will listen ot everything except for those which i are implemented for business case. So any request, which is not properly structured, or some unsupported media request would go to this method, so that i can send a more resonable response to the user
You could make a Servlet Filter, configure it to intercept all URLs matching your Web Services root URL (or even the root URL of the web server where the web service is deployed), have it pass the request on to the web service and then when the response arrives at the filter, if it's some kind of web service error you can change the response to whatever you want, make it redirect to some custom error page, etc.
I just did this using an "Exception Mapper". Here is a tutorial on how to do it with resteasy: https://community.jboss.org/wiki/RESTEasyExceptionHandlingWithExceptionMapper?_sscc=t
A colleague told me there is something analogous for Jersey.
Here is the code I used to make sure that I don't get empty content, because sometimes I forget, and I'm sure others will forget, to look at the headers / HTTP status.
import org.jboss.resteasy.spi.Failure;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Provider
public class PipelineMgrExceptionMapper implements ExceptionMapper<Exception> {
#Override
public Response toResponse(Exception e) {
if (e instanceof Failure) {
Failure f = (Failure) e;
return Response.fromResponse(f.getResponse()).entity(f.getMessage()).type(MediaType.TEXT_PLAIN).build();
} else {
return Response.status(500).entity(e.getMessage()).type(MediaType.TEXT_PLAIN).build();
}
}
}