GraphQL + Springboot + Http Headers - java

I'm using these graphql dependencies:
"com.graphql-java-kickstart:graphql-spring-boot-starter:12.0.0",
"com.graphql-java-kickstart:graphql-java-tools:12.0.0",
And how can I get httpRequestHeaders from the DataFetchingEnvironment class. I see DataFetchingEnvironment.getContext() is deprecated is there any other alternative?
Current logic:
GraphQLServletContext servletContext = env.getContext(); //deprecated
GraphQLContext qlContext = env.getGraphQlContext(); // No httpRequest
var httpRequest = servletContext.getHttpServletRequest();

You can do:
env.getGraphQlContext().get(HttpServletRequest.class).getHeader("header")

As of 12.0.0, environment.getGraphQLContext() is not supported. This lack of support is referenced in the project's github (https://github.com/graphql-java-kickstart/graphql-spring-boot/issues/808)
As of version 13.0.0, you can access the HttpServletRequest by using
HttpServletRequest httpServletRequest = environment.getGraphQlContext().get(HttpServletRequest.class);
If you need to use version 12.0.0, my suggestion would be to use a OncePerRequestFilter and store the header information you need in a ThreadLocal value. Just remember to clear that thread local value after the request has been processed. This will only work if you turn off "async mode" that the GraphQL library introduced in 11.0.0, as "async mode" processes the request filters and graphql resolvers in different threads.

Upgrade to 13.0.0 (or 14.0.0) and call
HttpServletRequest httpServletRequest = env.getGraphQlContext().get(HttpServletRequest.class);
This will give you and HttpServletRequest, from where you can call
private static Map<String, String> getHeadersFromRequest(HttpServletRequest httpServletRequest) {
var mapOfHeaders = new HashMap<String, String>();
Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
mapOfHeaders.put(headerName, httpServletRequest.getHeader(headerName));
}
}
return mapOfHeaders;
}
to return a list map of headers.

Related

Get request header in spring boot

How do I get the header and body of the current request from an application which called my Springboot application? I need to extract this information. Unfortunately this does not work. I tried to get the current request with this code sample (https://stackoverflow.com/a/26323545/5762515):
public static HttpServletRequest getCurrentHttpRequest(){
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
return request;
}
throw new IllegalArgumentException("Request must not be null!");
}
And then I tried to get the body
ContentCachingRequestWrapper requestWrapper = (ContentCachingRequestWrapper) currentRequest;
String requestBody = new String(requestWrapper.getContentAsByteArray());
Can someone tell me what im doing wrong?
Thanks in advance
#RestController
public class SampleController {
#PostMapping("/RestEndpoint")
public ResponseEntity<?> sampleEndpoint(#RequestHeader Map<String, String> headers,#RequestBody Map<String,String> body) {
//Do something with header / body
return null;
}
}
If the application's are communicating through a rest endpoint I believe this would be the simplest solution. In spring you can add RequestHeader and RequestBody annotations to method arguments to have them setup to be used.
Of course you can map RequestBody directly to some POJO instead of using a map but just as an example.
Let me know if this is what you were looking for !
#TryHard, You're using spring boot then following way is more preferable for you,
#RestController
public class SampleController {
#RequestMapping("/get-header-data")
public ResponseEntity<?> sampleEndpoint(HttpServletRequest request) {
// request object comes with various in-built methods use as per your requirement.
request.getHeader("<key>");
}
}
you can get header with your code but need apply some changes.
private String getRequest() throws Exception {
RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
if (attribs != null) {
HttpServletRequest request = ((ServletRequestAttributes) attribs).getRequest();
return request ;
}
throw new IllegalArgumentException("Request must not be null!");
}
after you can extract header info from request. For example if you want get Accept-Encoding
String headerEncoding = getRequest().getHeader("Accept-Encoding");
obliviusly you don't use this approce if not necessary.
If you want exract the body NOT use this solution

Spring #RestController how to get header at class level [duplicate]

I am new to web programming in general, especially in Java, so I just learned what a header and body is.
I'm writing RESTful services using Spring MVC. I am able to create simple services with the #RequestMapping in my controllers. I need help understanding how to get HTTP header information from a request that comes to my method in my REST service controller. I would like to parse out the header and get some attributes from it.
Could you explain how I go about getting that information?
When you annotate a parameter with #RequestHeader, the parameter retrieves the header information. So you can just do something like this:
#RequestHeader("Accept")
to get the Accept header.
So from the documentation:
#RequestMapping("/displayHeaderInfo.do")
public void displayHeaderInfo(#RequestHeader("Accept-Encoding") String encoding,
#RequestHeader("Keep-Alive") long keepAlive) {
}
The Accept-Encoding and Keep-Alive header values are provided in the encoding and keepAlive parameters respectively.
And no worries. We are all noobs with something.
You can use the #RequestHeader annotation with HttpHeaders method parameter to gain access to all request headers:
#RequestMapping(value = "/restURL")
public String serveRest(#RequestBody String body, #RequestHeader HttpHeaders headers) {
// Use headers to get the information about all the request headers
long contentLength = headers.getContentLength();
// ...
StreamSource source = new StreamSource(new StringReader(body));
YourObject obj = (YourObject) jaxb2Mashaller.unmarshal(source);
// ...
}
My solution in Header parameters with example is user="test" is:
#RequestMapping(value = "/restURL")
public String serveRest(#RequestBody String body, #RequestHeader HttpHeaders headers){
System.out.println(headers.get("user"));
}
You can use HttpEntity to read both Body and Headers.
#RequestMapping(value = "/restURL")
public String serveRest(HttpEntity<String> httpEntity){
MultiValueMap<String, String> headers =
httpEntity.getHeaders();
Iterator<Map.Entry<String, List<String>>> s =
headers.entrySet().iterator();
while(s.hasNext()) {
Map.Entry<String, List<String>> obj = s.next();
String key = obj.getKey();
List<String> value = obj.getValue();
}
String body = httpEntity.getBody();
}

Adding Headers to Zuul when re-directing

I am trying to use Zuul to redirect calls to a downstream system somewhere else.
In the re-direct, I need to add in a Header with necessary data for the api receiving the redirection to process. I can't seem to get the downstream system to detect this data. Attached is my code.
I am using Zuul from Edgware.SR3, Spring Boot 1.5.12
Zuul Filter
#Component
public class RouteFilter extends ZuulFilter{
#Override
public Object run() {
//Testing to add header
context.getRequest().getParameterMap().put("api", new String[]{"api"});
context.getResponse().setHeader("api", api);
context.addZuulResponseHeader("api", "api");
context.addZuulRequestHeader("api", "api");
context.setSendZuulResponse(false);
context.put(FORWARD_TO_KEY, redirect_urls.get(key));
context.setResponseStatusCode(HttpStatus.SC_TEMPORARY_REDIRECT);
context.getResponse().sendRedirect(redirect_urls.get(key));
return null;
}
}
Redirected Service Code
#RequestMapping(value = "/forward")
public ResponseEntity<String> forwardToMe(#RequestHeader(required = true, name = "api")String api){
return new ResponseEntity<String>("Hi",HttpStatus.OK);
}
Error Received in Postman
{
"timestamp": 1524737817729,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.bind.ServletRequestBindingException",
"message": "Missing request header 'api' for method parameter of type String",
"path": "/forward" }
I guess you use a Route Filter, maybe you can try with a Pre Filter.
Adding a custom header can be done with something like this : context.addZuulRequestHeader("Authorization", "Basic " + credentials);.
For the redirection part, you can check this thread
A little late my response but works fine
As referred in the official documentation Cookies and Sensitive Headers
The sensitiveHeaders are a blacklist, and the default is not empty. Consequently, to make Zuul send all headers (except the ignored ones), you must explicitly set it to the empty list. Doing so is necessary if you want to pass cookie or authorization headers to your back end. The following example shows how to use sensitiveHeaders:
zuul:
routes:
entry:
path: /users/**
strip-prefix: false
service-id: users-service
sensitive-headers:
- Cookie,Set-Cookie
This implemented example can also help you
In case if anyone still facing this issue,
In Zuul Proxy add the header to RequestContext as below:
RequestContext ctx = RequestContext.getCurrentContext();
ctx.addZuulRequestHeader("param", "value");
And then in the respective microservices write a custom filter and extract the value as below
#Component
public class MyFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String headerParam = request.getHeaders("param").nextElement();
logger.info("headerParam: "+headerParam);
filterChain.doFilter(request, response);
}
}
I update my comment here just in case if anyone is still facing this problem.
I found this problem recently and resolved by adding the following configuration in my application.yml
application.yml
...
zuul:
sensitive-headers:
- Cookie,Set-Cookie
...
Reference Link below:
https://cloud.spring.io/spring-cloud-static/Dalston.SR5/multi/multi__router_and_filter_zuul.html
RequestContext ctx = RequestContext.getCurrentContext();
String auth = "useeerrr" + ":" + "passsss";
ctx.addZuulRequestHeader("Authorization", "Basic " +
Base64Variants.MIME_NO_LINEFEEDS.encode(auth.getBytes(StandardCharsets.US_ASCII)));
ctx.addZuulRequestHeader("X-USERNAME-HEADER","xxx");
Map<String, List<String>> newParameterMap = new HashMap<>();
Map<String, String[]> parameterMap = ctx.getRequest().getParameterMap();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
newParameterMap.put(key, Arrays.asList(values));
}
String authenticatedKey = "authenticated";
String authenticatedValue = "true";
newParameterMap.put(authenticatedKey,Arrays.asList(authenticatedValue));
ctx.setRequestQueryParams(newParameterMap);
HttpServletRequest request = ctx.getRequest();
logger.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
return null;

How to emulate HttpServletRequest, HttpServletResponse and ServletContext?

I've a method that uses HttpServletRequest, HttpServletResponse and ServletContext.
Here's my code:
public String processarModelo(MyObject ou, Map<String, Object> attrs,
Map<String, Object> params) throws Exception {
ServletContext sc = com.opensymphony.webwork.ServletActionContext.getServletContext();
HttpServletResponse r = com.opensymphony.webwork.ServletActionContext.getResponse();
MyHttpRequest rw = new MyHttpRequest(com.opensymphony.webwork.ServletActionContext.getRequest());
rw.clearAttributes();
for (String s : attrs.keySet()) {
rw.setAttribute(s, attrs.get(s));
}
Map<String, String> p = rw.getParameterMap();
p.clear();
for (String s : params.keySet()) {
p.put(s, (String) params.get(s));
}
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Writer w = new OutputStreamWriter(bout);
SwallowingHttpServletResponse r2 = new SwallowingHttpServletResponse(r, w, "iso-8859-1");
javax.servlet.RequestDispatcher dispatcher = sc.getRequestDispatcher("/paginas/expediente/processa_modelo.jsp");
dispatcher.include(rw, r2);
w.flush();
String s = bout.toString();
return s;
}
It's working when I call it from my browser.
This week I've built a webservice that must call the same method. Inside the webservice method I have however no ServletContext, HttpServletResponse and HttpServletRequest at hands.
How can I emulate them to render my JSP and get the generated HTML?
Well, searching around about #Context annotation, i found this one for webservice:
#Resource
private WebServiceContext context;
With this, i can get everything i need with this:
HttpServletRequest request = (HttpServletRequest)context.getMessageContext().get(MessageContext.SERVLET_REQUEST);
ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT);
HttpServletResponse response = (HttpServletResponse) context.getMessageContext().get(MessageContext.SERVLET_RESPONSE);
Thanks everybody for helping!!
Are you sure you want your web service to call this? It will get redirected to some other page....
It looks like a design issue to me. You should extract the actual functionality from here and place it in some common code. Then call it in such a way that the web service request and servlet request will have enough information to call the functionality.
Also one letter variable names are discouraged ;)
If you want to make a call to the web page to store the HTML, you should make an HTTP request in the webservice method itself and store the data. You can use something like HTTPClient (http://hc.apache.org/httpcomponents-client-ga/) to do this.
Trying to build the HTTPRequest yourself isnt a good way to go.

How to log properly http requests with Spring MVC

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.

Categories

Resources