How do I create a Spring Boot endpoint that can accept multiple different types as RequestBody? - spring-boot

I have 2 messages like this...
{
"type":"MessageString",
"message": {
"value":"..."
}
}
and
{
"type":"MessageObject",
"message": {
"value": { ... }
}
}
Then I have a Spring Boot endpoint like...
#Controller
#RequestMapping("...")
public class MyController {
#PostMapping(
consumes = MediaType.APPLICATION_JSON_VALUE
)
#ResponseBody
public String processMessage(
#RequestBody (?) wrapper
){
...
}
}
I would like to know what kind of classes I can use to accept both types? I had it working like this....
class MessageWrapper<T extends MessageType>{
private T message;
}
But then I still have to declare the type in the response body like...
#RequestBody MessageWrapper<Object> wrapper
or
#RequestBody MessageWrapper<String> wrapper
So this wouldn't work because I can't have 2 response bodies.
I am trying to keep this as simple as possible so I would like to avoid making the #RequestBody a string then parsing it inside the controller.

Related

How to Handle FORM_URL_ENCODED to POJO with Quarkus?

I can't seem to find any information on how to have Quarkus convert incoming URL_FORM_ENCODED requests into a POJO.
The documentation says I can annotate each parameter in my receiving method with the #RestForm parameter. But, in the case where we have a lot of form params that would mean I have to add each parameter to my method signature.
I tried simply doing something like:
public class FormStuff {
#RestForm
public String title;
....More fields
}
and then in my receiving method I have:
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void save(FormStuff stuff) {
log.info("Got Stuff! {}", stuff);
}
However, when I call this from my webpage I always get a 415 Unsupported MediaType
BUT! If I change my signature to:
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void save(#RestForm title) {
log.info("Got Stuff! {}", title);
}
The request goes through and I see the title field print out.
Does Quarkus support converting Forms to POJO like it does for MultiPart forms?
Thanks!
You need to do the following:
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void save(#BeanParam FormStuff stuff) {
log.info("Got Stuff! {}", stuff);
}
#BeanParam is a JAX-RS standard annotation that users can use to gather multiple HTTP params into a POJO.

How to write #ApiResponse which may return a Class or a List of that class using OpenAPI 3 Swagger in Spring Boot

As written in documentation we can use anyOf with #Schema if we want to define multiple responses.
#ApiResponse(responseCode = "201", description = "OK",
content = #Content(schema = #Schema(anyOf = {Product.class, Activity.class})))
My controller returns either a Product or a List<Product>. I would like to specify this in my OpenAPI 3 documentation.
I would like to know if it's possible.
If Yes, then how?
If No, then is there any workaround?
I don't only want to specify List.class. I want to specify List<Product>.
P.S.:- Searching on Google didn't get me any results that I can use.
Ok, thats a tough one.
Basically if you really want to return a List of Objects or one Object, then you can create a basic interface like this
public interface Response {
}
And then you can create your Object, which implements the response
public class Hello implements Response {
private String message;
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
}
Finally we can create the List of our Object. For that we need to create a class, which extends the ArrayList and implements our Interface
public class HelloList extends ArrayList<Hello> implements Response {
}
After that we can just set our schema as implementation
#ApiResponse(responseCode = "200", description = "hello world", content = #Content(mediaType = "application/json", schema = #Schema(implementation = Response.class)))
On the Clientside you need to check the instance of the Response-Object, so that you can parse either the Object or the List
Response response = someCall();
if (response instanceof Hello) {
System.out.println(processHello((Hello) response);
}
if (response instanceof HelloList) {
System.out.println(processHelloList((HelloList) response);
}
This example works, but its very very complex und confusing. A better way to design your api, would be to return just the list. I don't see the benefit to seperate one object or the list of it.

Expose public field of POJO to FTL in Spring

I can't figure out how to send a POJO to my template in Spring Boot.
Here's my POJO and my controller:
class DebugTest {
public String field = "Wooowee";
public String toString() {
return "testie " + field;
}
}
#Controller
#RequestMapping("/debug")
public class WebDebugController {
#RequestMapping(value = "/ftl", method = RequestMethod.GET)
public ModelAndView ftlTestPage(Model model) {
DebugTest test = new DebugTest();
ModelAndView mnv = new ModelAndView("debug");
mnv.addObject("test", test);
return mnv;
}
}
Here's my template:
HERES THE TEST: ${test}$
HERES THE TEST FIELD: ${test.field}$
Here's the output (GET /debug/ftl):
HERES THE TEST: testie Wooowee$
HERES THE TEST FIELD: FreeMarker template error (DEBUG mode; use RETHROW in production!):
The following has evaluated to null or missing:
==> test.field [in template "debug.ftl" at line 3, column 25]
[Java stack trace]
The class itself (DebugTest) must be public too, as per the JavaBeans Specification. Also, fields by default aren't exposed. Defining getter methods is generally the best (with Lombok maybe), but if you want to go with fields, configure the ObjectWrapper as such. As you are using Spring Boot, I think that will be something like this in your application.properites:
spring.freemarker.settings.objectWrapper=DefaultObjectWrapper(2.3.28, exposeFields = true)

Spring MVC REST - standard request body wrapper

What is the best way to achieve a common REST request body unwrap effect with Spring MVC #RestController ?
In other words, assuming I have the following controller:
#RestController
public class MyController {
#RequestMapping(value = "/", method = POST)
public Object hello(#RequestBody MyDTO dto) {
...
}
}
I would like the actual post body to be:
{
"version": "1.0",
"payload": {
...
}
}
Which can be represented by the following class:
public class ApiRequestDTO<TPayload> {
private String version;
private TPayload payload;
...
// Getters and Setters...
...
}
So in this particular case the client would send an instance of ApiRequestDTO<MyDTO>.
I achieved the opposite (response body wrapper) using a #ControllerAdvice, but I notice that it won't work exactly for the request body. One possible approach I can see is to decorate all the relevant message converters. But I was wondering if there is a better approach?

Using custom JSON serialization for Spring WebSocket #MessageMapping/#SubscribeMapping

This works fine
In my Spring-based application, I have set up a HTTP-based REST endpoint. This endpoint "speaks" JSON:
#Controller
public class HttpRestController implements RestController {
#Override
#RequestMapping(value = "/users/{user}", method = RequestMethod.GET)
#ResponseBody
public getUser(#PathVariable User user) {
User jsonFriendlyUser = new JacksonAnnotatedUser(user);
return jsonFriendlyUser;
}
}
As these JSON payloads have to follow unusual naming conventions, I used annotations such as #JsonRootName and #JsonProperty to customize the serialized property names:
#JsonRootName("uussaaar")
public class JacksonAnnotatedUser implements User {
//...
public int getId() {
return id;
}
#JsonProperty("naammee")
public String getName() {
return name;
}
#JsonSerialize(using = FriendsJsonSerializer.class )
public Set<User> getFriends() {
return friends;
}
#JsonIgnore
public String getUnimportantProperty() {
return unimportantProperty;
}
}
With this custom JSON metadata, querying /users/123 via HTTP returns the following JSON payload:
{"uussaaar":
{
"id":123,
"naammee":"Charlie",
"friends": [456, 789]
}
}
The following doesn't work as expected
Now I am playing around with Spring's WebSocket support: I want to create a STOMP-based REST endpoint. Therefore i created a StompRestController like this:
#Controller
public class StompRestController implements RestController {
#Override
#SubscribeMapping("/users/{user}")
public getUser(#DestinationVariable User user) { // assuming this conversion works
User jsonFriendlyUser = new JacksonAnnotatedUser(user);
return jsonFriendlyUser;
}
I would have expected for #SubscribeMapping/#MessageMapping to follow the same JSON serialization behavior as #RequestMapping. But this is not the case. Instead, when querying this WebSocket/STOMP endpoint, #SubscribeMapping/#MessageMapping-annotated methods will result in sending a STOMP message to clients with a payload/body corresponding to the "normal" Jackson serialization rules, e.g.
{
"id":123,
"name":"Charlie"
"friends":[{argh recursion}, ...],
"unimportantProperty":"This property shall not be part of JSON serialization"
}
Therefore:
How can I have #SubscribeMapping/#MessageMapping-annotated methods obey custom #JsonXXX annotations for returned values?
Is there another way aside #JsonXXXfor doing such returned value serialization?

Resources