I'm trying to send a request to an HTTP endpoint using Camel Jetty or Camel HTTP component. I was wondering if it's possible to convert the request and response to HttpServletRequest and HttpServletResponse objects.
I have the following route:
from("direct://processing/callEndpoint")
.to("jetty:http://www.google.com")
.to("bean:processHttpResponse")
I'm using Guice for dependency injection and "bean:processHttpResponse" is the following:
public class HttpResponseBean {
public void processResponse(Exchange exchange) {
HttpServletRequest request = exchange.getIn().getBody(HttpServletRequest.class);
HttpServletResponse response = exchange.getOut().getBody(HttpServletResponse.class);
}
}
Both request and response objects are null. When debugging/inspecting the Exchange, the In Message is a byte array (byte[]) and the Out Message is null. Not sure how to proceed..
Refer to the Camel Jetty goto the Consumer Example.
Try to call your bean like this.
from("jetty:http://localhost:{{port}}/myapp/myservice").bean(HttpResponseBean,"processResponse");
public class HttpResponseBean {
public void processResponse(Exchange exchange) {
// we have access to the HttpServletRequest here and we can grab it if we need it
HttpServletRequest req = exchange.getIn().getBody(HttpServletRequest.class);
}
}
You should implement a processor like this one:
public class MyBean implements Processor {
public void process(Exchange exchange) throws Exception {
HttpServletRequest request = exchange.getIn().getBody(HttpServletRequest.class);
HttpServletResponse response = exchange.getOut().getBody(HttpServletResponse.class);
}
}
If you have a #Configuration Spring context like this
#Configuration
class ApplicationConfiguration {
#Bean
public MyBean myBean() {
return new MyBean();
}
}
you should be able to refer to it in the process statement like this .process("bean:myBean").
If you are building your route using a RouteBuilder, please use a SpringCamelContext in order to let Camel access the beans.
Related
I'm currently trying to inject and read out the HttpRequest in Quarkus 1.13 but without any success. I'm using RESTEasy-Reactive for my endpoint.
This is how I'm currently including it
#Path("/users/{id}")
class UserController(
#Inject val service: UserService,
#Context val httpRequest: io.vertx.core.http.HttpServerRequest,
)
...
The build process succeeds but when I try to access a property like httpRequest.absoluteURI() I am getting an NPE
java.lang.NullPointerException: Cannot invoke "org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext.serverRequest()" because the return value of "org.jboss.resteasy.reactive.server.core.CurrentRequestManager.get()" is null
at io.quarkus.resteasy.reactive.server.runtime.QuarkusContextProducers.httpServerRequest(QuarkusContextProducers.java:26)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusContextProducers_Subclass.httpServerRequest$$superaccessor3(QuarkusContextProducers_Subclass.zig:451)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusContextProducers_Subclass$$function$$3.apply(QuarkusContextProducers_Subclass$$function$$3.zig:29)
...
I also tried other classes like io.vertx.mutiny.core.http.HttpServerRequest or java.net.http.HttpRequest but still without success. Injecting it with #Inject didn't even build. I'm missing the HttpServletRequest class :/
Anybody got an idea?
You have a few options:
Using HttpFilter: https://javaee.github.io/javaee-spec/javadocs/javax/servlet/http/HttpFilter.html
#WebFilter(urlPatterns = "/*")
public class FilterEverything extends HttpFilter {
#Override
protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
//Do something with HttpServletRequest
}
}
Using ContainerRequestFilter: https://docs.oracle.com/javaee/7/api/javax/ws/rs/container/ContainerRequestFilter.html
As Quarkus Documentation Showcases:
#Provider
public class LoggingFilter implements ContainerRequestFilter {
private static final Logger LOG = Logger.getLogger(LoggingFilter.class);
#Context
UriInfo info;
#Context
HttpServerRequest request;
#Override
public void filter(ContainerRequestContext context) {
//Do whatever you want
}
}
As part of the method signature:
#GET
#Path("/someEndPoint")
#Produces("application/json")
public JsonObject getData(#PathParam("owner") String owner, #Context HttpServletRequest request) {
//Do something here
}
I added an async endpoint to a existing spring-mvc application:
#RestController
public class MyController {
#PostMapping("/")
public Mono<String> post(Object body) {
return Mono.just("test");
//webClient.retrieve().bodyToMono(String.class);
}
}
I want to create a global interceptor/filter that will log the request body payload. But how can I get access to it?
I tried adding a HandlerInterceptorAdapter, but the payload is always empty:
static class LoggingInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper(request);
byte[] buf = wrapper.getContentAsByteArray();
System.out.println(buf);
System.out.println(buf.length);
return true;
}
}
Maybe the payload is not yet present in the request, or has already been read. So how can I access the body in this async case?
Unfortunately in Webflux you cannot use HandlerInterceptorAdapter because it came from web mvc module and works only with the servlets.
I found a good article with solutions.
P.S. You must to remove spring-mvc dependencies if going to use reactive endpoins.
How do I log a JSON or XML request in a database or log file before processing in Spring boot using #RequestBody annotation?
Using which class can I perform this?
Or any link would be helpful.
You can use filter (CommonsRequestLoggingFilter class) approach or you can use below code with custom implementation
#Component
public class AppRequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) {
HttpServletRequest requestCacheWrapperObject = new ContentCachingRequestWrapper(request);
//your implementation
//sample method you can use: requestCacheWrapperObject.getParameterMap(); requestCacheWrapperObject.getContentAsByteArray();
return true;
}
#Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
//your implementation
}
}
#Configuration
public class AppMVCConfig implements WebMvcConfigurer {
#Autowired
private AppRequestInterceptor appRequestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(appRequestInterceptor)
.addPathPatterns("/**");
}
}
In order to log request payloads, you can use Spring provided filter CommonsRequestLoggingFilter.
Add following bean into your Spring-Boot config and change log level of org.springframework.web.filter package to DEBUG.
#Bean
public CommonsRequestLoggingFilter requestLoggingFilter() {
CommonsRequestLoggingFilter rlFilter = new CommonsRequestLoggingFilter();
rlFilter.setIncludePayload(true);
return rlFilter;
}
Also, Spring Boot provides Actuator Endpoint (/actuator/httptrace) for HTTP request logging out of the box. Check the below link for more details on this:
Spring Boot Actuator
I've setup a Spring HandlerInterceptor to add an attribute to the HttpServletRequest to be able to read it from the Controller, sadly this does not seem to work which seems strange to me. Am I doing things wrong? Any idea how to transmit the data from the Interceptor to the Controller?
Here is the simplified code of the two impacted classes
public class RequestInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
request.setAttribute("my-role", "superman");
}
[...]
}
#RestController
#RequestMapping("Test")
public class TestController {
public final Logger logger = LoggerFactory.getLogger(getClass());
#RequestMapping(value = "something")
public void something(HttpServletRequest request) {
logger.info(request.getAttribute("my-role"));
}
[...]
}
The request.getAttribute("my-role") returns null... but does return the excepted value if I read it in the postHandle of the HandlerInterceptor, I feel like I'm missing something...
EDIT : I found out that going thru the session with "request.getSession().setAttribute" works as a charm, still i do not understand why the request itself does not work in this use case.
Can you try with session instead of request like below.
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
...
HttpSession session = request.getSession();
session.setAttribute("attributeName", objectYouWantToPassToHandler);
....
}
In your handler handleRequest method:
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
....
HttpSession session = request.getSession();
objectYouWantToPassToHandler objectYouWantToPassToHandler = session.getAttribute("attributeName");
....
}
I'm trying to retrieve the body of a request in a JAX-RS ExceptionMapper. Here is my code so far:
#Provider #Componenet
public class BaseExceptionMapper implements ExceptionMapper<Exception> {
#Context private HttpServletRequest request;
#Override
public Response toResponse(Exception ex) {
// Trying to retrieve request body for logging throws an error
String requestBody = IOUtils.toString(request.getInputStream());
}
}
So my dilemma is I can't get the request body for logging because the servlet API wont allow you to call request.getInputStream() / request.getReader() more than once for a request (and JAX-RS Is obviously calling it to parse the request). Does anyone know if there is a way to do what I'm trying to do?
This question is a bit older, but still the answer may help others. My Example also depends on Commons-Io.
You can create a ContainerRequestFilter and use TeeInputStream to proxy/copy the original InputStream:
#Provider
#Priority(Priorities.ENTITY_CODER)
public class CustomRequestWrapperFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
ByteArrayOutputStream proxyOutputStream = new ByteArrayOutputStream();
requestContext.setEntityStream(new TeeInputStream(requestContext.getEntityStream(), proxyOutputStream));
requestContext.setProperty("ENTITY_STREAM_COPY", proxyOutputStream);
}
}
And use #Inject with javax.inject.Provider in your ExceptionMapper to get the ContainerRequest injected.
The ExceptionMapper would look like this:
#Provider
public class BaseExceptionMapper implements ExceptionMapper<Exception> {
#Inject
private javax.inject.Provider<ContainerRequest> containerRequestProvider;
#Override
public Response toResponse(Exception exception) {
ByteArrayOutputStream bos = (ByteArrayOutputStream) containerRequestProvider
.get().getProperty("ENTITY_STREAM_COPY");
String requestBody = bos.toString();
...
}
}
When I have also used the #Component annotation my ExceptionMapper was not used. I think that #Provider is sufficient.
One possible solution is to use a servlet filter and wrap the request, which allows you to intercept read calls to the request input stream. Example pseudo-code (depends on commons-io):
import org.apache.commons.io.output.StringBuilderWriter;
import org.apache.commons.io.input.TeeInputStream;
class MyHttpRequest extends HttpServletRequestWrapper {
private StringBuilderWriter myString = new StringBuilderWriter();
private InputStream myIn;
public MyHttpRequest(HttpServletRequest request) {
super(request);
myIn = new TeeInputStream(request.getInputStream(), myString);
}
#Override public ServletInputStream getInputStream()
throws java.io.IOException {
// this will need an extra wrapper to compile
return myIn;
}
public String getRequestBody() {
return myString.toString();
}
}
Filter:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
MyHttpRequest wrapper = new MyHttpRequest((HttpServletRequest) request);
chain.doFilter(wrapper, response, chain);
}
Mapper:
#Context private HttpServletRequest request;
#Override public Response toResponse(Exception ex) {
String body = "";
if (this.request instanceof MyHttpRequest) {
body = ((MyHttpRequest)request).getRequestBody()
}
}
You'll need a wrapper class for ServletInputStream, and you can find an example implementation here: Modify HttpServletRequest body
I know this is an old question but I found a workaround that I think it's nice to share.
With the following code you should be able to get the ContainerRequestContext inside the ExceptionMapper, then you can read the body, query params, headers, etc.
#Provider
public class CustomExceptionMapper implements ExceptionMapper<CustomException> {
#Context
private ResourceContext resourceContext;
#Override
public Response toResponse(CustomException e) {
ContainerRequestContext requestContext =
resourceContext.getResource(ContainerRequestContext.class);
}
}
Hope it can help