Spring Cloud Netflix: Whats happening in ZuulConfiguration with the ZuulServlet? - spring

Looking in ZuulConfiguration, I see the following:
#Bean
public ZuulController zuulController() {
return new ZuulController();
}
#Bean
public ServletRegistrationBean zuulServlet() {
return new ServletRegistrationBean(new ZuulServlet(),
this.zuulProperties.getServletPattern());
}
ZuulController wraps the ZuulServlet and manages its lifecycle as if it were a Spring Controller. What throws me is that the ZuulConfiguration class registers the servlet anyways using a ServletRegistrationBean. Perhaps I am thinking the wrong thing, but I would think that you would do one or the other. Could someone explain why both are necessary?
Using this configuration, is the ZuulServlet running as a true servlet (known by the embedded servlet container), a controller (which delegates to the servlet) or both?
Thanks,
Joshua

From this commit:
Allow streaming of multipart requests in Zuul proxy
It turns out that the suckiness of Zuul with multipart requests
comes almost entirely from the Multipart handling in Spring's
DispatcherServlet. This change makes the proxy routes available
on an alternative path /zuul/ (where
/zuul is the default value of zuul.servletPath). I have
tested those with 800MB file uploads using the main method in
the FormZuulServletProxyApplicationTests and the main
observation is that there is no OutOfMemory error (no-one tries
to download the complete request body). It works with Ribbon
and with the simple (HttpClient) filter. With Ribbon you
will need to set some timeouts if you want to upload files
as large as that, e.g. see application.yml in the tests:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
ribbon:
ConnectTimeout: 3000
ReadTimeout: 60000
You need to set "Transfer-Encoding: chunked" in the
incoming request. Chrome does not do this by default
apparently, but I was able to test with curl, e.g.
$ curl -v -H "Transfer-Encoding: chunked" \
-F "file=#mylarg.iso" \
localhost:9999/zuul/direct/file
The old proxy paths through the DispatcherServlet are still
available (for backwards compatibility and for convenience of
having the paths available at the root of the context path).

Related

Disable Feign logging on some API calls

I am using following configuration to enable logging for API calls made using feign
#Feign properties
feign:
client:
config:
default:
loggerLevel: full
My application is making calls to 3 APIs and feign is logging request and response JSON correctly for all the 3 APIs . I want to disable this logging for one of the API. Can you please let know on the necessary configuration. Thanx in advance
If you have 3 different feign clients for 3 APIs, then you can simply override logging level in this way (assuming that your feign client is called "feignClient2"):
#Feign properties
feign:
client:
config:
default:
loggerLevel: full
feignClient2:
loggerLevel: none
But if you have one feign client for 3 endpoints, then the task becomes more complicated. You can inherit feign logger class (or any of its children classes, f.e. Slf4jLogger, if you use it now) and override its logRequest and logAndRebufferResponse methods to not log anything for specific endpoint (you can get the required information from request and response method parameters respectively). Then add FeignLoggerFactory bean with your own logger:
#Bean
public FeignLoggerFactory feignLoggerFactory() {
return new DefaultFeignLoggerFactory(new CustomLogger());
}
It will override the default FeignLoggerFactory from FeignClientsConfiguration.
In summary, I would recommend you to use the first option (with separate feign client). But if you choose the second one, I could help you to do it if you provide the example of your code.

Spring Boot - Multiple context paths - have one redirect to the other?

We have a spring boot 2 app that is typically accessed behind a proxy server for all requests at, say, /blah/**.
We set up this app to use a context path called /blah to simplify this (so we no longer had to add /blah to all of our controller mappings, resource imports, etc)
server:
port: 9876
servlet:
context-path: /blah
This works fine, but there are some cases where people might access the app at root (/) that we'd like to handle.
It looks like I need to create a new servlet that listens to "/" and just redirects to "/blah"? What is the easiest way to set that up? I don't want any of my existing beans in this other context, just a simple controller or html file that performs a redirect.
This is what I think would be easiest way.
#RequestMapping("/*")
public void redirect(HttpServletResponse response) throws IOException{
response.sendRedirect("/blah");
}

Spring-Cloud Zuul breaks UTF-8 symbols in forwarded multipart request filename

this is first time for me on SO, so please be patient for my first question.
I think i have some kind of configuration problem, but after a day of experiments i'm stuck. Our application is based on Spring-Cloud [Brixton release]. We have configuration like this: Portal (web application serving angular-based web-ui), which has zuul proxy with single route configured to our gateway service, like so:
zuul:
ignoredServices: '*'
prefix: /api
routes:
api-proxy:
path: /**
serviceId: api-gateway
which has another Zuul configured and relays requests to inner bussiness logic services:
zuul:
ignoredServices: '*'
routes:
service1:
path: /service1/**
serviceId: service1
service2:
path: /service2/**
serviceId: service2
All this configuration is working with no problem.
The problem now that i am facing is with file upload multipart requests. To be more precise - those multipart requests, when file to be uploaded has non latin symbols (e.g. ąčęėįš) from UTF-8. When request reaches service which has to deal with #RequestPart MultipartFile file, then file.getOriginalFilename() returns questionmarks in the places of aforementioned symbols. Now, i have tried to directly upload such file to such controller, and filename comes without questionmarks, that is, not broken, which suggests, that some bad interpretation/parsing of multipart request occurs somewhere in Zuul filters, when proxy relays incomming request.
Maybe someone had similar experience with Zuul and can direct me some way to resolve this problem?
I just ran into the same issue myself, and created the following issue:
https://jira.spring.io/browse/SPR-15396
Hopefully this is getting configurable in Spring 4.3.8.
In the meantime, you have to create a bean of type FormBodyWrapperFilter (that overrides the one in ZuulConfiguration). In the constructor, you pass a copy of FormHttpMessageConverter, which extends from FormHttpMessageConverter, and you change the encoding used in FormHttpMessageConverter.MultipartHttpOutputMessage#getAsciiBytes(String) to UTF-8 (you might also want to delete any references to javax-mail, unless you have that on classpath). You need a pretty recent version of Spring Cloud Netflix to do this.
Example:
#Bean
FormBodyWrapperFilter formBodyWrapperFilter() {
return new FormBodyWrapperFilter(new MyFormHttpMessageConverter());
}
Then you create a copy of FormHttpMessageConverter, and change the following method:
private byte[] getAsciiBytes(String name) {
try {
// THIS IS THE ONLY MODIFICATION:
return name.getBytes("UTF-8");
} catch (UnsupportedEncodingException ex) {
// Should not happen - US-ASCII is always supported.
throw new IllegalStateException(ex);
}
}
Still having issues after modifying response even with newer versions
Using spring boot 2.3.8.RELEASE
Managed to fix it by forcing the following spring properties
server.servlet.encoding.force= true
server.servlet.encoding.charset= UTF-8

How to enable swagger in a jersey + spring-boot web application [duplicate]

I am getting a HTTP 404 error when trying to serve index.html ( located under main/resources/static) from a spring boot app. However if I remove the Jersey based JAX-RS class from the project, then http://localhost:8080/index.html works fine.
The following is main class
#SpringBootApplication
public class BootWebApplication {
public static void main(String[] args) {
SpringApplication.run(BootWebApplication.class, args);
}
}
I am not sure if I am missing something here.
Thanks
The problem is the default setting of the Jersey servlet path, which defaults to /*. This hogs up all the requests, including request to the default servlet for static content. So the request is going to Jersey looking for the static content, and when it can't find the resource within the Jersey application, it will send out a 404.
You have a couple options around this:
Configure Jerse runtime as a filter (instead of as a servlet by default). See this post for how you can do that. Also with this option, you need to configure one of the ServletProperties to forward the 404s to the servlet container. You can use the property that configures Jersey to forward all request which results in a Jersey resource not being found, or the property that allows you to configure a regex pattern for requests to foward.
You can simply change the Jersey servlet pattern to something else other than the default. The easiest way to do that is to annotate your ResourceConfig subclass with #ApplicationPath("/root-path"). Or you can configure it in your application.properties - spring.jersey.applicationPath.

Spring Data REST CORS - how to handle preflight OPTIONS request?

I'm using Spring Data REST to build a RESTful API. Until now my HTML GUI for this RESTful service was served from the same Tomcat and I had no problems wit Cross Origin requests.
Now I want to serve the static files from a different server. This means the API is on another domain/port. Browsers will send the OPTIONS request to get the Access-Control headers from the server. Unfortunately Spring Data REST does not handle those OPTIONS requests and even returns a HTTP 500.
I tried creating a custom controller that handles all OPTIONS requests
#Controller
#RequestMapping(value = "/**", method = RequestMethod.OPTIONS)
public class OptionsController {
#RequestMapping
public ResponseEntity options() {
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
Which worked for OPTIONS, but then all other requests (like GET) ceased to work.
OPTIONS requests are switched on via the dispatchOptionsRequest dispatcher servlet parameter.
tl;dr: currently Spring Data REST does not answer OPTIONS requests at all.
It might be worth opening a ticket in our JIRA.
Browsers will send the OPTIONS request to get the Access-Control headers from the server.
Is that specified somewhere? If so, it would be cool if the ticket description included a link to that spec.
A few comments regarding your approach for a workaround:
#RequestMapping on the controller method overrides the method attribute and expectedly now matches all HTTP methods, which is why you see all requests intercepted. So you need to define OPTIONS as HTTP method there, too (or maybe instead of in the class mapping).
You're not returning any Allow header which is the whole purpose of OPTIONS in the first place.
I wonder if the approach in general makes sense as it'll be hard to reason about the supported HTTP methods in general.
Just set the parameter dispatchOptionsRequest to true into the dispatcher to process the Options method calls, into the implementation of the WebApplicationInitializer.
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(applicationContext));
dispatcher.setInitParameter("dispatchOptionsRequest", "true");
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");

Resources