Java interceptor to process the REST API response code - java

I have a requirement to intercept the REST API responses for out application and do something with them. One very obvious approach to achieve this is to define an annotation and interceptor at method level (so that it can be applied to the REST API methods). However, I could not find a way to extract/intercept the response code for API responses. I am very new to Java EE world, so might be missing something here, but didn't find anything on internet search either. Our application is based on standard JavaEE with CXF.
I have seen some examples with code similar to the following, but not sure how I can get the API response from that. Any help would be appreciated.
#AroundInvoke
public Object around(InvocationContext context) throws Exception {......

Suppose you are using the standard JEE solution, from the doc
#AroundInvoke
public Object logInvocation(InvocationContext ctx) throws Exception {
String class = ctx.getMethod().getDeclaringClass().getName();
String method = ctx.getMethod().getName();
Logger.global.entering(class, method, ctx.getParameters());
try {
Object result = ctx.proceed();
Logger.global.exiting(class, method, result);
return result;
}
catch (Exception e) {
Logger.global.throwing(class, method, e);
throw e;
}
}
The Object result = ctx.proceed(); is the result for you.

JAX-RS server filters
Use a ContainerResponseFilter from the JAX-RS API 2.0 to intercept responses or server side:
#Provider
public class CustomResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
// Intercept the HTTP response and get the status code
int status = responseContext.getStatus()
}
}
The JAX-RS 2.0 API also provide the ContainerRequestFilter to intercept requests:
#Provider
public class CustomRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Intercept the HTTP request
}
}
To bind the filters to resource classes and/or methods, use name binding annotations.
Registering server filters in Apache CXF
According to the Apache CXF documentation, the filters must be registered in the cxf.xml configuration file. This file must be in the classpath of your application.
Here's an example extracted from the documentation of what your CXF configuration file can be like when registering a filter:
<beans>
<jaxrs:server id="customerService" address="/">
<jaxrs:serviceBeans>
<bean class="org.CustomerService" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="authorizationFilter" />
</jaxrs:providers>
<bean id="authorizationFilter" class="com.bar.providers.AuthorizationFilter">
<!-- authorization bean properties -->
</bean>
</jaxrs:server>
</beans>
For more details, check the CXF documentation about configuration.

Although interceptors are a great way to do cross-cutting concerns for your application services, you are talking about a web application here. You are not looking at intercepting service request, you are looking at intercepting http requests.
Web applications use the servlet spec, and for such generic stuff you'd use #Webfilters.
These webfilters can get the entire request and response, including the status code, as they work on the transport level, and not on the application level.
Be mindful that the servlet spec is picky: you cannot easily read requests and responses without duplicating them, and it's a bit messy code. Google a bit for it (log http request, log http response) and you should find plenty of code to create a filter which can do that.

Hi you didn't mention what technology stack. If you needed something for client side you could use Spring and Spring's ClientHttpRequestInterceptor.
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
/**
* response.getStatusCode();
*/
return response;
}

Related

Logging a request header before Spring Security filter chain

I want to log the contents of a given incoming request header as early as possible.
I know about approaches like CommonsRequestLoggingFilter or a logging HandlerInterceptor, however these seem to only log after Spring has executed a lot of other code, such as the Spring Security filter chain.
I want to log before Spring has done any of that, as early as possible based on a single requirement: the log message needs to be able to extract a header from the HTTP request.
Is there a way to do this?
I have found a way to do this using the embedded Tomcat. Since this receives the request before Spring does, you can capture the entire dispatched request from here.
public class CustomLoggerValve extends ValveBase {
private static final Logger logger = LoggerFactory.getLogger(CustomLoggerValve.class);
#Override
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
MDC.put("requestId", UUID.randomUUID().toString());
logger.info("Received request");
getNext().invoke(request, response);
} finally {
MDC.remove("requestId");
}
}
}
Since I'm using Spring without Spring Boot, I can just add this to my Tomcat directly:
Tomcat tomcat = // ... your tomcat setup
tomcat.getService().getContainer().getPipeline().addValve(new CustomLoggerValve());
I haven't tried, but it looks like you could add this quite easily in Spring Boot too.
Presumably a similar approach would work with embedded Jetty/other JVM web servers.

In spring boot 2 whats the most efficient way to log body?

I used to log my requests in filters in JAX-RS using this
#Provider
public class RequestLoggingFilter implements ContainerRequestFilter {
#Context
private HttpServletRequest servletRequest;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
BufferedInputStream stream = new
BufferedInputStream(requestContext.getEntityStream());
String payload = IOUtils.toString(stream, "UTF-8");
requestContext.setEntityStream(IOUtils.toInputStream(payload, "UTF-8"));
}
}
I am trying do something similar in spring boot 2 and I have tried using filters, Interceptors and also actuator. I lost the body as soon as I read it. In spring boot 2 the actuator's trace does not support logging of the body so is there any other way to log the request and response body without losing it?
You might find everything you need there: http://www.baeldung.com/spring-http-logging
Spring provides a built-in solution to log payloads. We can use
ready-made filters by plugging into Spring application using
configuration.
AbstractRequestLoggingFilter is a filter which provides basic
functions of logging. Subclasses should override the beforeRequest()
and afterRequest() methods to perform the actual logging around the
request.
Spring framework provides three concrete implementation classes which
can be used to log the incoming request. These three classes are:
CommonsRequestLoggingFilter Log4jNestedDiagnosticContextFilter
(deprecated) ServletContextRequestLoggingFilter Now, let’s move on to
the CommonsRequestLoggingFilter and configure it to capture incoming
request for logging.

Camel With Cxf and Routing

I was doing some research on Camel - CXf integration and am confused about the below scenario.
So i implemented a Rest Endpoint
#Path("/authenticate")
public interface Sample {
#GET
#Path("/handshake")
#Produces(MediaType.TEXT_PLAIN)
public Response handshake();
#POST
#Path("/login")
#Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
#Produces(MediaType.APPLICATION_JSON)
public Response login(LoginRequest request) throws JsonGenerationException, JsonMappingException, IOException;
}
And the implementation as below
public class SampleImpl implements Sample{
#Context
private HttpHeaders headers;
#Autowired
CamelContext context;
public Response handshake()
{
System.out.println("HandShake Executed Successfully");
return Response.status(Status.OK).entity("This is a Message after Routing").build();
}
public Response login(LoginRequest request) throws JsonGenerationException, JsonMappingException, IOException {
System.out.println("The Rquest objecr is Received "+request);
return Response.status(Status.OK).entity(mapper.writeValueAsString(request)).build();
}
}
The Route
<camel:from uri="cxfrs:bean:SampleRestEndPoint?bindingStyle=SimpleConsumer"></camel:from>
routes it into the implementation. But since the implementation returns a response object am confused how to build the routes around this.
Once the call comes into the implementation how can I execute the
other routes and sent a response back?.In this case the implementation returns a custom object.
How are the other routes attached to a CXF route?.
Should my CXF Implemenation always return a void type?. As i see
that, to get access to Exchange object camel need the return type to
be void
Do I completely ignore the implementation and go with the "to" steps
and modify it in exchange body for the required response?.
Any pointers will be appreciated.
Dude, take a look at this - http://bushorn.com/camel-cxf-geocoder-example/
The above example is not REST though, but usage of CXF with Camel route is same.
I will do these mandatory steps:
Avoid beans/custom classes - try to use the camel framework capabilities.
Use XML - Spring/Blueprint DSL
Please look at the following thread.
Apache Camel and web services
I have successfully implemented web service consumption using camel and Apache CXF. If you have doubts, I can help you out.
Thanks,
Gautham
#GauthamHonnavara - that is an implementation of a JS webservice with an assosiated processor however it doesnt assosiate any direct route to the endpoint.Also my question was specific to JAX-RS where you cannot generate a service class from wsdl.
Assume this use case that u need a customer to invoke the endpoint and then go through say another 5 steps, reach out to another webservice etc and then sent a response back. The above implementation sents a response back in the webservice implementation bean itself.
So to avoid this create a simple interface with the producer consumer etc, just like in my question and then make each method void if you want to inject the Exchange( if needed. ) and use below configuration
<!-- Created the CXF End Point For the Calls to Come IN -->
<cxf:rsServer id="RestEndPoint" address="/rest"
serviceClass="com.poc.camel.cxf.service.incoming.xxx"
loggingFeatureEnabled="true" loggingSizeLimit="20">
<cxf:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" >
<!-- <constructor-arg ref="customObjectMapper" type="org.codehaus.jackson.map.ObjectMapper"/> -->
</bean>
</cxf:providers>
</cxf:rsServer>
Trick is to use the service class tag. If the interface is provided there then it doesn't need a concrete implementation from CXF.
Hope this helps. Let me know

How to use Jersey's internal routing mechanism to extract a class/method reference?

I have a Jersey 1.8 application running. Jersey is running as a Servlet.
I need to write a servlet filter that given a plain request/response, is able to figure out which REST resource/method will respond to the request and extract values from annotations.
For example, imagine I have the following resource:
#Path("/foo")
#MyAnnotation("hello")
public class FooResource {
#GET
#Path("/bar")
#MyOtherAnnotation("world")
public Response bar(){
...
}
}
When a request GET /foo/bar comes in, I need my servlet filter to be able to extract the values "hello" and "world" from MyAnnotation and MyOtherAnnotation before Jersey's own servlet processes the request.
This filter logic should be able to work for all requests and all resources registered.
Is there a way to access Jersey's internal routing mechanism to obtain a class/method reference where Jersey will dispatch the request?
I'm open to other suggestions as well, but ideally nothing like trying to hack my own routing mechanism by reading the #Path annotations myself.
#Provider
#Priority(Priorities.AUTHORIZATION)
public class MyFilter implements ContainerRequestFilter
#Context // request scoped proxy
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
if (resourceInfo.getResourceClass().isAnnotationPresent(MyAnnotationion.class) ||
resourceInfo.getResourceMethod().isAnnotationPresent(MyOtherAnnotation.class)) {
to register the filter use
bind(AuthFilter.class).to(ContainerRequestFilter.class).in(Singleton.class);

Setting response header using interceptor?

I'm writing jax-rs end points. For some set of end points (existing code), I want to set an extra response header which was actually generated in #AroundInvoke interceptor and set to HttpServletRequest attribute. In #AroundInvoke I'm able to access HttpServletRequest using #Inject. But it seems I cannot access HttpServletResponse in the same interceptor itself.
It seems I can do with PostProcessorInterceptor but again I'm confused with the following doc.
The org.jboss.resteasy.spi.interception.PostProcessInterceptor runs after the JAX-RS method was invoked but before MessageBodyWriters are invoked. They can only be used on the server side. Use them if you need to set a response header when there might not be any MessageBodyWriter invoked.
I'm using resteasy, jackson. If I use PostProcessorInterceptor can I inject HttpServletResponse? Or Can I set new http header there some how?
Any code example/direction would be appreciated.
With JaxRS 2 (which comes with javaEE 7) you can use a ContainerResponseFilter see also
public class PoweredByResponseFilter implements ContainerResponseFilter {
#Inject
HttpServletRequest request;
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
String name = "X-My-Header";
String value = "";// some data from request
responseContext.getHeaders().add(name, value);
}
}

Categories