How to access the RequestBody in a Spring WebFlux #ExceptionHandler? - java

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!

Related

How to short-circuit from Spring WebGraphQlInterceptor without executing controller handler?

I have a Spring WebFlux application and am trying to write a WebGraphQlInterceptor to enforce authorization. The authorization requires access to HTTP headers and GraphQL variables, both of which are easily accessible from a WebGraphQlInterceptor. However, if the request fails authorization, I do not want to execute the controller handler and instead exit early with an error response. I have the custom error response working OK, but I cannot figure out how to bypass the controller -- it seems like I'm required to proceed down the original chain, execute the controller, and only then return the error response.
I'm hoping there's an easy solution I'm missing. The documentation seems sparse on this topic with very few examples online. The WebGraphQlInterceptor documentation lists some methods like apply() that sound like they might be helpful for altering the chain, but it's not clear how to use them. The interceptor interface requires a WebGraphQlResponse to be returned, and I can't find a way to return it without continuing down the original chain with chain.next(request) below:
#Override
public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
return chain.next(request).map(response -> {
// Custom logic...
});
}
I also tried constructing my own custom WebGraphQlResponse from scratch to return from the interceptor, but this felt like a hack and a lot of unnecessary overhead.
I don't want the controller to be executed at all if authorization fails. Is this even possible with the WebGraphQlInterceptor? And what would the simplest implementation look like?
P.S. The WebFilter doesn't help me here because I need easy access to GraphQL variables, which isn't possible with WebFilters.

Spring Webflux: Controller return Mono<ResponseEntity<MyPojo>> vs Mono<MyPojo>

Question regarding return types I have seen with Spring Webflux.
In many examples, like online tutorials, the rest web controller of a Spring Webflux project will return a Mono<MyPojo>:
public Mono<MyPojo> monoPojo(String parameter) {
return WebClient.create("http://...").get().retrieve().bodyToMono(MyPojo.class)
.map(oneMyPojo -> unregisterRepository.insert(oneMyPojo));
}
But I am also bumping into projects where it returns Mono<ResponseEntity<MyPojo>>:
public Mono<ResponseEntity<MyPojo>> monoResponseEntityPojo(String parameter) {
return WebClient.create("http://...").get().retrieve().bodyToMono(MyPojo.class)
.map(oneMyPojo -> unregisterRepository.insert(oneMyPojo))
.map(ResponseEntity::ok);
}
What are the benefits of Mono<ResponseEntity<MyPojo>> over Mono<MyPojo>?
Lets clear some things up
A ResponseEntity<T> is from the org.springframework.httppackage while a ServerResponse is from the org.springframework.web.reactive.function.server package.
This should as a start should give you a hint of when to use what, and where.
But in short, you can use webflux in 2 ways, either by using old fashioned #RestController annotations, with annotated functions for each path. This a sort of "backwards compatibility mode" between regular servlet spring web, and webflux async event driven programming.
ResponseEntities are returned from old spring-web while if you instead opt in to use functional enpoints that exist in webflux, you need to return ServerResponses.
If you look in the code for the classes you will se that they work some parts the same, but other parts differently, especially how they store the body and serialize the bodies.
Handler functions and Filter functions in webflux only work with ServerResponses.
Now to your question, returning Mono<ResponseEntity<T> or Mono<T>.
Well it all comes down to how lazy you are.
If you return a Mono<T> the framework will try to figure out what type of content you have in the Mono, and then create a ResponseEntity accordingly. So if you are serializing it into json, it will set the content-type for you, and set the status usually to 200 OK
While if you wish, you can build your ResponseEntity completely custom, and return any status code, with any body, and any headers etc. etc.
So what it all comes down to is how lazy are you, how much do you want the framework to do for you, and how much to do you want to be explicit, and and type out everything by yourself, or customize.
Me, im lazy, i just return something that works.

Spring boot #PreAuthorize (is it possible to modify the input to the method in #PreAuthorize then pass into the method)

I have a method annotated with #PreAuthorize(...) with some logic that goes away and queries an API for some information about what the user can view. However, I have this endpoint that I need to add this #PreAuthorize annotation into which receives in a more "complex" object which I want to transform slightly (the object contains an array that is some cases I want to add/remove data from).
#PostMapping("/search")
#PreAuthorize("#Service.isAuth(#searchParam)")
public ResponseEntity<Response> search(SearchParams searchParam) {
return service.getSearchResult(searchParam);
}
Is there a way I can modify searchParam inside the #PreAuthorize annotation then have it passed into the method body, I know that this is probably is not the correct way to do this and maybe isn't something that #PreAuthorize wasn't designed for but is there any way of doing this even with a different type of annotation. Obviously worst case I can move the logic into the method body but I would prefer to use an annotation-based solution like #PreAuthorize offers if possible. Thanks for any help even links to other relevant things would be useful I've not found much on google related to this.
I think the best solution is to make a handler/interceptor and then annotate it with #PreAuthorize. So I think you are in the right track but you need to make sure that you modify your code to implement the HandlerMapping interface to create the interceptor and then override the prehandle method. After you need to annotate it with #PreAuthorize programatically. The last thing will be to use a wrapper to modify the HttpWrapper, it cannot be done manually. Here links to the relevant resources in order:
Creating a Handler/Interceptor: https://www.baeldung.com/spring-mvc-handlerinterceptor
Using PreAuthorise in the interceptor: How can I require that all request handlers in my Spring application have #PreAuthorize
To modify the HttpServlet request you will need a wrapper: How to modify HttpServletRequest body in java?
Have a try, hopefully that works.
Snippet of code taken from second link uses a programatic PreAuthorize rather than annotation:
public class PreAuthorizeChecker implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
PreAuthorize annotation = AnnotationUtils.findAnnotation(hm.getMethod(), PreAuthorize.class);
//TODO use the technique shown on the third link to wrap and modify the HttpServletRequest
if (annotation == null) {
// prevent access to method wihout security restrictions
throw new RuntimeException("Rights are not defined for this handler");
}
}
return true;
}
.....

A real-world controller example with Spring 5: Web Reactive

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

Is there a way to access HttpRequest type inside a #Controller method

I have tried to find the answer to this, but I cannot seem to find what I am looking for. So I apologize if this question already exists.
PROBLEM:
I want to be able to access the request type of a request inside of a generic method within my Controller.
DESCRIPTION:
Using Spring ROO and Spring MVC, I have developed a small web service that will respond with certain tidbits from a database when queried. In one of my controller classes, I have some methods that handle some variety of GET, PUT, POST, etc., for the URIs that are mapped within the #RequestMapping parameter.
For example:
#RequestMapping(method = RequestMethod.Get, value = "/foo/bar")
#ResponseBody
public ResponseEntity<String> getFooBar() {
// stuff
}
If a request is made to the web service that it is not currently mapped, a 405 error is returned (which is correct), but I want to return more information along with a 405 response. Maybe respond with something like:
"I know you tried to execute a [some method], but this path only handles [list of proper methods]."
So I wrote a short method that only has the RequestMapping:
#RequestMapping(value = "/foo/bar")
I have found that the method with this mapping will catch all unhandled request types. But I am having trouble accessing the information of the request, specifically the type, from within the method.
QUESTION:
A. How can I access the request type from within the method? OR
B. Is this the right approach? What would be the right approach?
EDIT
ANSWER:
I added a HttpServletRequestobject to the method parameters. I was able to access the method type from that.
I tried using HttpRequest, but it didn't seem to like that much.
Thanks all!
You can add a method parameter of HttpServletRequest, but I think you'd be better off continuing to reply with 405. A client should then make an HTTP OPTIONS call (see How to handle HTTP OPTIONS with Spring MVC?) and you can return the list of allowed methods there.
A. you can access request if you mentioned it as parameter in controller method
public ... getFooBar(HttpRequest request) {
...
}
B. you do not need to add any other description as the 405 status is descriptive.
In answer to "A", just add "HttpRequest req" as an additional argument to your controller methods. Spring will automatically inject a reference to the request, and you can play with headers to your heart's content.
In answer to "B" - "What would be the right approach", how about this?
In order to return that 405, Spring has raised a MethodArgumentNotValidException. You can provide custom handling for this like so:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public MyMethodArgumentMessage handleMathodArgumentNotValidException(
MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
MyMethodArgumentMessage myMessage =
new MyMethodArgumentMessage(result.getFieldErrors());
return myMessage;
}
You should take a look at the #ExceptionHandler annotation. This lets you add methods such as the following to your controller. You can define your own exceptions and appropriate custom handlers for them. I use it to return well-structured XML and JSON from REST services. Although for it to work, you need to throw specific exceptions from your controller methods.
A good walk-through of using this was provided by Petri Kainulkainen in his blog:
http://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-adding-validation-to-a-rest-api/

Categories

Resources