I've seen this, however I am having this issue now.
If I change my controller to return a ResponseEntity<?> the body comes through fine, but if it returns DeferredResult<ResponseEntity<?>> then I get my headers and status code but no response body.
Any help will be greatly appreciated
Use of DeferredResult is not simply a matter of changing the controller's response type. A controller which returns a DeferredResult will likely delegate handling of the request to a thread pool (or some async mechanism) and whatever actually performs the request must set the response on the DeferredResult. Something like this:
In the controller:
DeferredResult deferredResult = new DeferredResult<>();
// pass deferredResult into a runnable task (or something similar)
// which is scheduled for execution via a thread pool (or something similar)
return deferredResult;
In the task which executes the request on behalf of the controller:
// do some work
// ...
deferredResult.setResponse(responseBody);
Related
I have started using WebClient(org.springframework.web.reactive.function.client.WebClient) for calling rest services. I have 2 classes say Utility.java and ServiceImpl.java.
ServiceImpl.java is where I use WebClient. A post call I am making looks like -
ClientResponse response = webClient.post()
.uri(path)
.body(Mono.just(inputDTO),InputDTO.class)
.exchange()
.block();
(ClientResponse above is org.springframework.web.reactive.function.client.ClientResponse)
(I am using exchange instaed of retrive because I want headers as well as status code)
Now trying to convert this response into some DTO - ResponseDTO.
Mono<ResponseEntity<ResponseDTO>> mono = response.toEntity(ResponseDTO.class);
ResponseEntity<ResponseDTO> resEntity = mono.block();
ResponseDTO myObj = resEntity.getBody();
So myObj is an object of ResponseDTO class.
The issue is - when I perform the conversion of 'response into ResponseDTO.java' in my utility class, I get myObj = null. But if I do it in my ServiceImpl.java (just after calling post API), it returns the proper body (ResponseDTO object).
The same issue occurs even if I perform the conversion and post call operation in two different methods in the ServiceImpl.java.
Do I need to configure something here?
I figured out what was the issue.
After calling REST api, body in the response if flushed out after I read it from the response for the first time. I had a Sysout statement in service implementation class where I was reading the body content.
Recommendation: Read the body content only once and store it in a variable. Use it wherever required.
Using POSTMANto call an endpoint for example:
http://localhost/v1
Checking Request Method in the code using:
context.getRequest().getMethod()
No matter what I change request method in postman request, I always get the first request method I select let say GET.
When i change request to
http://localhost/v1/
It works and start sending correct request methods i select in POSTMAN
http://localhost/v1
I am expecting that when I call:
http://localhost/v1
or
http://localhost/v1/
It should behave same way.
Any explanation to point to the right direction would be appreciated
you need to redirect /v1 to /v1/ try like
#RequestMapping(value="/v1")
public ModelAndView redirectToPage() {
return new ModelAndView("redirect:/v1/");
}
On the retrieve code below, how to get Person instead of Mono or how to get Person from Mono, please ?
23.2.3 Request and Response Body Conversion
The response body can be one of the following:
Account — serialize without blocking the given Account; implies a synchronous, non-blocking controller method.
1.7.1. Retrieve
WebClient client = WebClient.create("http://example.org");
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Person.class);
Once you have a Mono<Person> instance available, you have two choices:
compose that reactive type (i.e. use operators available on that type) and use it to save that data in a datastore, serve it as a HTTP response body, etc
or call Person person = result.block() on it, which blocks. So you should not do that in a reactive application because this might completely block the few threads available to your application.
I have a controller method like this:
#RequestMapping("/hello")
public String hello(UserForm user) {
return "hello";
}
It receives some request parameters in the UserForm command object. But I have not written any code to add the object to the Model. Still, in the view hello.jsp, I'm able to access the data, like this:
Hello, ${userForm.name}!
Does it mean that Spring MVC adds command objects to the Model automatically?
You don't need #ModelAttribute just to use a Bean as a parameter.
You'll need to use #ModelAttribute or model.addAttribute() to load default data into your model - for example from a database.
Most of the Spring controllers in the real world accept a lot of different types of parameters - Path variables, URL parameters, request headers, request body and sometimes even the entire HTTP Request object. This provides a flexible mechanism to create APIs. Spring is really good at parsing these parameters in to Java types as long as there is an ObjectMapper (like Jackson) configured to take care of the de-serialization.
The RequestMappingHandlerAdapter makes sure the arguments of the method are resolved from the HttpServletRequest.
Spring model data created prior to (or during) the handler method
execution gets copied to the HttpServletRequest before the next view
is rendered.
By now, Spring has processed the HTTP request and it creates the ModelAndView object from the method’s return value. Also, note that you are not required to return a ModelAndView instance from a controller method. You may return a view name, or a ResponseEntity or a POJO that will be converted to a JSON response etc.
ServletInvocableHandlerMethod invocableMethod
= createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(
this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(
this.returnValueHandlers);
}
The returnValueHandlers object is a composite of HandlerMethodReturnValueHandler objects. There are also a lot of different value handlers that can process the result of your method to create ModelAndViewobject expected by the adapter.
Then, it has to render the HTML page that the user will see in the browser. It does that based on the model and the selected view encapsulated in the ModelAndView object.
Now, at this stage, the view gets access to the userForm (as in your example above) from the request scope.
My existing code is like:
String myController(#PathVariable someId, ModelMap map){
....
return "myViewName";
}
Now I want to set a cookie in some cases, so I need to get hold of a HttpServletResponse obj. Can I just add such a response obj to the list of params and operate on it in the controller?
If so, I wonder how my own response is kind of reconciled with the response generated by the JSP that resolves the "myViewName".
Yes.
#RequestMapping
public String myController(#PathVariable someId, ModelMap map, HttpServletResponse response) {
// Do what you need to do on the response, like set a cookie
return "myViewName";
}
Regarding your other question : "how my own response is kind of reconciled with the response generated by the JSP that resolves the "myViewName"."
When you return a view say "myViewName", it will be resolved to a particular resource (JSP View or JSON View or any other view). Once that view resource is obtained depending on what you return, that view does the rendering on to the response. This response object is the same that was passed to the controller function (myController). So say if you set some cookie/headers on the response in the controller function, the response that is being used by the view to do the rendering will also have the same properties.
In case you want to handle the actual rendering/response yourself, you can always get the outputstream of the response and write to it and close the stream. Then the view that you return is just ignored as the dispatcher will check that the response is already handled and will just do post handle stuff.
Hope that clears up for anyone looking for the dispatcher logic behind it.