I want to be involved in a reactive programming world with Spring. As I realised, it gives me a choice between two different paradigms: the annotation-based (with well-known to us #Controller, #RequestMapping) and the reactive one (which is intended to resolve an "Annotation Hell").
My problem is a lack of understanding how a typical reactive controller will look like. There are three conceptual interfaces, which I can use in my controller class:
HandlerFunction<T> (1) - I define a method for each specific ServerRequest
which returns a concrete HandlerFunction<T> instance, then register these methods with a router. Right?
RouterFunction (2) and FilterFunction (3) - Is there a specific place where all RequestPredicates with corresponding HandlerFunctions should be placed? Or can I do it separately in each controller (as I used to do with the annotation approach)? If so, how then to notify a global handler (router, if any?) to apply this router part from this controller?
It's how I see a reactive controller "template" by now:
public class Controller {
// handlers
private HandlerFunction<ServerResponse> handleA() {
return request -> ok().body(fromObject("a"));
}
// router
public RouterFunction<?> getRouter() {
return route(GET("/a"), handleA()).and(
route(GET("/b"), handleB()));
}
// filter
public RouterFunction<?> getFilter() {
return route(GET("/c"), handleC()).filter((request, next) -> next.handle(request));
}
}
And, finally, how to say that it is a controller, without marking it with the annotation?
I've read the Spring reference and all posts related to this issue on the official blog. There is a plenty of samples, but all of them are pulled out of context (IMHO) and I can't assemble them into a full picture.
I would appreciate if you could provide a real-world example and good practices of how to organise interactions between these functions.
This is not a real world example, but so far Is how I view some kind of organization on this:
https://github.com/LearningByExample/reactive-ms-example
As far as I concerned:
RouterFunction is the closest analogue to #Controller (#RequestMapping precisely) in terms of new Spring approach:
Incoming requests are routed to handler functions with a
RouterFunction (i.e. Function>). A router function evaluates to a
handler function if it matches; otherwise it returns an empty result.
The RouterFunction has a similar purpose as a #RequestMapping
annotation. However, there is an important distinction: with the
annotation your route is limited to what can be expressed through the
annotation values, and the processing of those is not trivial to
override; with router functions the processing code is right in front
of you: you can override or replace it quite easily.
Then instead of Spring Boot SpringApplication.run in main method your run server manually by :
// route is your route function
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
HttpServlet servlet = new ServletHttpHandlerAdapter(httpHandler);
Tomcat server = new Tomcat();
Context rootContext = server.addContext("",
System.getProperty("java.io.tmpdir"));
Tomcat.addServlet(rootContext, "servlet", servlet);
rootContext.addServletMapping("/", "servlet");
tomcatServer.start();
There are both reactive and non-reactive approach. It's illustrated on Spring github
Related
Is there a way to get access to the RequestBody (preferably in it's mapped form) in an #ExceptionHandler method using Spring WebFlux, with the default Reactor Netty?
Consider the following example:
#RestController
class TestRestController {
#PostMapping("/test")
Mono<TestBody> testPost(#RequestBody TestBody testBody) {
return Mono.error(new NullPointerException());
}
#ExceptionHandler(NullPointerException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
Mono<Void> handleNullPointerException(ServerWebExchange serverWebExchange) {
return Mono.empty();
}
}
At runtime additional instances of certain types can be injected into the #ExceptionHandler's method signature, as shown in the above example with ServerWebExchange. But the docs clearly state that it doesn't support request body arguments (see the note in this section).
Using the Servlet stack, you can inject the RequestContext as shown here. Is there an equivalent or similar approach for the WebFlux stack?
Yes, there is a way, but not a really good looking one. We had kind of the same problem, but in our case we've wanted to have access on the reactive context, since we've stubbornly chose to use the MDC in this reactive paradigm, so the MDC rely on that context.
After lots of back-and-forth with the Exception handlers and controller advice, it was pretty obvious that the reactive context could not be accessed there.
So we've extends the AbstractErrorWebExceptionHandler . Here there is a handle(ServerWebExchange exchange, Throwable throwable) method that will be called when you're application has an error. The good part is that there is the ServerWebExchange and you can access the context like: exchange.getAttributes().get(MDC_CONTEXT) or the body like: exchange.getRequest().getBody().
So that resolved our MDC problem, but the mapping of the errors had to be handled manually unfortunately. I've remember that we've invested a lot of time, and at that time, that was the best solution. Cheers!
I have started a few days ago to learn about fault tolerance solutions in microservices. I have some microservices in my ecosystem and they are now interconnected with Eureka service lookup. I used FeignClient to call from one to another. As I heard and read, that Hystrix is getting into maintenance, I wondered if I could use Resilience4J in Feign instead of Hystrix. Well, at least not from annotation level right now as it seems. I found a great Feign.Builder adapter to add resilience4j fault tolerance features above a FeignClient as a decorator (https://github.com/resilience4j/resilience4j/tree/master/resilience4j-feign) so I wanted to use it.
So I used this, added together the features and added the default encoder, decoder, etc. items into the feign builder. Turns out I have to finish of course my code with a .target call which creates my client proxy and I could not really do this with Eureka in a good way:
The first constructor, which takes the class type and the URL is hardcoded, so if I add an eureka next server query into this parameter, it is just a hardcoded url for one of the instances, this is not load balanced. Some kinda workaround could be that I create prototype-scope or similar short lived scoped beans of this client and always get the "next url" for the call. This adds lots of burden to use the clients in every class I make. At least as I saw it. Maybe I could add some kind of singleton helper bean around the prototyping, but again this is not a good design as I see
I thought maybe I could create an EurekaTarget from the Target interface, but of course none of the methods indicate any "end of lifecycle" things, not even the apply method. I thought maybe that is one point which is called before doing a service call, but I saw multiple calls towards it so I had to change the url for all calls.
Do you know any better solution to do this migration?
I guess you are using Spring Boot?
The next version v1.0.0 of Resilience4j will support the #FeignClient annotation.
There was a PR which added the functionality -> https://github.com/resilience4j/resilience4j/pull/579
You can then use it as follows:
#FeignClient(name = DUMMY_FEIGN_CLIENT_NAME)
#CircuitBreaker(name = DUMMY_FEIGN_CLIENT_NAME)
public interface DummyFeignClient {
String DUMMY_FEIGN_CLIENT_NAME = "dummyFeignClient";
#GetMapping(path = "/api/{param}")
void doSomething(#PathVariable(name = "param") String param);
}
I have seen in many cases the .handle("someBean", "someMethod") EIP method, playing vast role in the integration flows. I can understand that it is just a Service Activator in the former XML Config, but I need some clarification on how to create this bean and what does the someMethod return. Also, in which cases do I have to use .handle(...)? Maybe a completed example using Java DSL should work for me.
As noticed correctly the .handle("someBean", "someMethod") is fully equal to the <int:service-activator ref="someBean" method="someMethod"/>: https://docs.spring.io/spring-integration/reference/html/messaging-endpoints-chapter.html#service-activator-namespace.
That means that you should have someBean definition for service invocation in the someMethod. For example you need to perform simple logic to convert payload of the incoming message to the upper case and return the result:
class MyService {
public String someMethod(String payload) {
return payload.toUpperCase();
}
}
The return of this method becomes as a payload of the outbound message to the next EIP endpoint in your IntegrationFlow definition.
Everything what you see in the Reference Manual is fully true for Java DSL. In particular all the rules for the <service-activator> or #ServiceActivator are applied for this .handle().
I was trying to understand the caching that happens at the client side.
Unfortunately I am unable to find any resources that can help me out.
I have employee model objects which are fairly small in size.
Once a use a GET request to obtain an employee object, I want it to be cached at the client side
Now when the request comes again to obtain the same employee, I want to see if the actual object has been modified, if not, then serve from the client cache else return the modified object also adding it to the cache.
I am using Spring boot to create a REST endpoint.
What I have been able to figure out is that cache-control would be used some how, but I am not sure how the objects would be added here in spring.
Any help here is much appreciated!!!!
Thanks,
Amar
HTTP caching is not an easy topic. There are different ways to do it, and you should probably start by familiarizing yourself with the mechanisms, this seems to be a good starting resource: HTTP caching
Then, you will probably identify some common usage patterns you will want to reuse. One way to do that is to create custom annotations and write an interceptor that reacts on them.
For example, you could write such an annotation:
#Inherited
#Retention(RUNTIME)
#Target({METHOD, TYPE})
public #interface CacheFor {
long amount();
TimeUnit unit() default TimeUnit.SECONDS;
}
and use it on controller methods like this:
#CacheFor(amount=10, unit = MINUTES)
#RequestMapping(bla bla)
public FooBar serveMyData(){
// code here
}
and in your interceptor, you will need to look at the handler method, check whether it has this annotation, and if it does, set the appropriate headers.
Let's say that I have a #Controller with two methods that do related but different things. I want to expose them on the same URL endpoint but have different functionality depending on the security context. Now I know that I could have a single mapped request method and dispatch to different utility functions within my class, but could I wire it so that Spring MVC will handle the dispatch for me based on what comes in?
As a strawman example, there's code that dispatches between ROLE_ADMIN and ROLE_USER, but my actual use case is a good chunk more complicated:
#Controller
public class Controller {
#RequestMapping("/api/thing")
#PreAuthorize("hasRole('ROLE_ADMIN')")
public String doAnAdministrativeThing() {
... (admin-priviledge stuff goes here)
}
#RequestMapping("/api/thing")
#PreAuthorize("hasRole('ROLE_USER')")
public String doADifferentThing() {
... (normal-priviledged stuff goes here)
}
}
To preempt any rabbit holes:
No, I might not have any choice about having the two functions be on different URLs or having different parameters or any of the "normal" #RequestMapping bits
The security context differences are complex but can be processed within a #PreAuthorize expression
There are actually more than two different contexts to dispatch on
I think that this will not work, global method security has absolutely nothing to do with MVC layer.
Moreover it's officially recommended to use method security only for the service layer
Generally we would recommend applying method security at the service
layer rather than on individual web controllers.
P.S. generally I try to avoid using AOP with Spring controllers, and declare AOP methods only in the root context(and not in the servlet context), because proxying controller instance can lead to some confusing behavior, and forces the users to know Spring AOP specific things, like difference between JDK proxy and CGLIB proxy, how annotations are processed, etc.
This will work with some extensions. See the reference to GitHub mohchi / spring-security-request-mapping in #andy's answer to Can Spring Security use #PreAuthorize on Spring controllers methods?