Normalize response body in spring boot - spring-boot

I do have some entity class (code without annotations for simplified example)
class User {
public String id;
public String name;
}
Now I want to output this via an API, but I want to structure my response in a special format, like
{
"data": {
"id": 1,
"name": "mars3142"
}, // user object or another entity or list...
"meta": ...,
"error": ...
}
The meta and/or error data should only be visible in special situations (like RuntimeExceptions). Where is the best place to transform my entity results into the normalized response? Do I need to write a filter for that? Does anybody has a sample code for that?

I would suggest to implement something this:
public abstract class BaseResponse {
// Meta data
// Consider defining fields here needed for happy-path and error-responses
// Contains common tracking fields, e.g. correlationId, requestId
}
public class ErrorResponse extends BaseResponse {
// Error Fields
}
public class Response extends ErrorResponse {
// Entity-object in your case
}
I guess you can build your response like setting response from DAO to above suggested structure in controller layer. For error-responses (in case of RuntimeExceptions), they're standardly build and returned in #ControllerAdvice or other.
Some patterns of exception handling are explained in Error Handling for REST with Spring | Baeldung.

Regarding your 2 questions:
Design: The proper place for this response-mapping depends on the scope (all responses or just some) and existing components in your application's response layer.
Patterns and Web-Framework concepts: I would not use the response-filters or -interceptors of your web-framework. Those should be used for cross-cutting concerns, or for chained processes (e.g. security, authorization, enrichment, sanitation).
Instead I would use the web-frameworks concepts and components that are responsible for response-representations, like ResponseEntity (HTTP-response representation, ControllerAdvice (error-handling), HttpMessageConverter.
There are 3 ways you could "wrap" your objects into uniform JSON-response models:
Annotate class with the custom #JsonRootName as data and in special cases add meta and/or error attributes (through e.g. embedding into a wrapper or using a mixin)
A JSON custom serializer that could extend from BeanSerializer which wraps this and any class uniformly in your given outer structure
Modify Spring's MappingJackson2HttpMessageConverter to wrap any returned response object into the predefined JSON-structure
You could iterate from the simplest (1.) to the most complex (3.). Some iteration code (like 2.) can be reused in the next (3.).
1. Use a Wrapper Class
The first is rather a simple start where you can implement the "normalization" within controller-methods. You could for example put the object (serialized as data) into the "empty" meta-structure (wrapper-class) with an empty JsonNode, and meta or error properties.
2. Define a Custom Serializer
The second is pretty flexible and can be tested well in isolation (not even depending on Spring). It would allow to implement the complete object-wrapping in one place.
3. Customize Spring's HTTP Message Converter
The third is similar to the second but requires some knowledge about Spring's message-converters and allows you to transform each response-object to a specific JSON-response using Jackson's ObjectMapper.
Sample code can be found online, e.g. at Baeldung's Jackson or Spring tutorials, Springframework Guru articles.

I used the solution from https://stackoverflow.com/a/72355056/708157 and transformed it a little bit.
Now my classes are that way
public class BaseResponse<T> {
boolean success;
T data;
Error error;
}
public class Error {
...
}
And every api response is now ResponseEntity<BaseResponse<XYZ>>. This way, I can setup my default structure and my classes are lose coupled, because I can use every class for T within my BaseResponse.

Related

Spring inject component into non-spring managed interface/abstract class and its subclasses

TLDR: I need an interface/abstract class and all classes implementing it to have access to a Spring managed bean. Can Spring inject a bean into an interface/abstract-class and its subclasses simply via #Autowired ?
I am working on an API built with Spring Webflux + Cloud Gateway that depending on the cookie JWT authorized party, identifies the User's policy group and assign an Attribute ENUM "InterfaceID" to the ServerWebExchange via exchange.getAttribute().put("InterfaceID",InterfaceID.A) after the JWT is validated, and currently uses "InterfaceID" to represent the different groups of users/different interface the user entered from.
JWTValidationFilter.java [Current]
switch(JWTValidator.validate(jwt).get("AZP")){
//if user is from company A or its partners
case "a":
case "aa":
exchange.getAttribute().put(InterfaceID.COMPANY_A_ACCESS);
break;
case "b":
exchange.getAttribute().put(InterfaceID.NORMAL_ACCESS);
...
}
For certain API endpoints (say /api/getSessionDocument), different "InterfaceID" fetches data from different DB/apis, as well as have different permission checking on top of that.
RequestController.java [Current]
#Autowired
APICallerUtil apiCallerUtil;
switch(exchange.getAttribute.get(InterfaceID)){
case "NORMAL_ACCESS":
apiCallerUtil.getDataFromApiA();
break;
case "COMPANY_A_ACCESS":
// call api B but check for permission from api D first
...
}
The endpoint's controller now has another switch statement, and to many code analyzers this have been a code smell. I have been trying to refactor this entire bit of code to use polymorphism to handle the different "getSessionDocument" flows, but i run into issues regarding the injection of util classes that calls specific APIs.
APICallerUtil.java class, exisiting class from the project, would prefer not to refactor this.
#Component
public class APICallerUtil{
#Value("${some uri to some API}") //different by environment and therefore cant be static final
private String uri1;
#Value("${some auth to some API}") //confidential
private String uri1AuthHeader;
//...
public JSONObject getDataFromApiA(String somekey){ //cant be static since uri1 is not static
//Some code that uses uri1 and apache httpclient
return data;
}
...
}
IBaseAccess.java
interface IBaseAccess{
default Mono<JSONObject> getSesssionDocument(ServerWebExchange e){return Mono.error("not implemented");}
}
RequestController.java [new]
#Autowired
APICallerUtil apiCallerUtil;
return exchange.getAttribute.get(InterfaceID).getSessionDocument(exchange);
NormalAccess.java
public class NormalAccess implements IBaseAccess{
//can i autowire APICallerUtil here?
//use constructor to pass the Util class reference here?
Mono<JSONObject> getSesssionDocument(ServerWebExchange e){
//need to call ApiA here
//need to call ApiC here
}
}
NormalAccess needs to call APICaller.getDataFromApiA(), but it needs a reference to the Spring managed instance of APICaller. What would be the "correct" way to pass the reference/autowire API caller into NormalAccess, or even better IBaseAccess (so that the implementing classes can use the Util bean)?
JWTValidationFilter.java [new]
switch(JWTValidator.validate(jwt).get("AZP")){
//if user is from company A or its partners
case "a":
case "aa":
exchange.getAttribute().put("InterfaceID",new CompanyAAccess(/*pass the util class here?*/));
break;
case "b":
exchange.getAttribute().put("InterfaceID",new NormalAccess(/*pass the util class here?*/));
...
}
I have tried several methods, but either I lack the knowledge on the specific Spring feature, or that method is deeemed a bad design choice by some, including:
Making the methods and fields in APICallerUtil static, via suggestions from Spring: How to inject a value to static field? and Assigning private static final field member using spring injection , then the Access classes can call the static methods.
Creating a contructor for IBaseAccess that consumes the APICallerUtil reference and store it inside. The JWTfilter would hold an autowired APICallerUtil and pass it in when the attribute is assigned.
Create a static class that provides the application context and Access classes use applicationContext.getBean("APICallerUtil"); to obtain the bean.
Use the #Configurable annotation? I could not find much documentation on how this works for interfaces/abstract-class.
I understand that there might not exist an absolute answer for this question, but regardless I'd like suggestion/feedback on which of these approaches are viable/good. Especailly concerning whether the APIUtil class should be static or not.

Use CRNK without repository

We've standardized on using JSON:API for our REST endpoints, however; not all of our data revolves around repositories and it seems that CRNK requires repositories in order to work.
Is that correct?
Example
I wrote a very simple Spring Boot 2.1.9 example that has a single controller and included CRNK in it, but when I get into my controller I do not get the expected JSON:API output.
Please keep in mind, I am just starting to look at CRNK and this is just a simple "hello world" type of application that I am testing with
Here is my example
package com.example.crnkdemo;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping("/test/v1.0")
public class Controller {
#GetMapping(value = "/{country}", produces = "application/vnd.api+json")
public Country test1(#PathVariable String country, #RequestParam(name = "filter[region]", required = false) String filter) {
return new Country(country, filter);
}
}
Country is just a dummy class I created which is:
package com.example.crnkdemo;
import io.crnk.core.resource.annotations.JsonApiId;
import io.crnk.core.resource.annotations.JsonApiResource;
#JsonApiResource(type = "country")
#AllArgsConstructor
#Data
public class Country {
#JsonApiId
private String country;
private String region;
Results
But when I use the following URL http://localhost:8080/test/v1.0/US?filter[region]=northeast I get
{
"country": "US",
"region":"northeast"
}
I would have expected the JSON API type of result
{
"data": {
"type": "country",
"id": "US",
"attributes": {
"region": "northeast"
}
}
Thanks!
I ran into similar issue and the problem was that I got io.crnk:crnk-format-plain-json in my dependencies (simply copied from an example app) which changes the way how the responses look like (non-JSON-API). So first have a look into your maven/gradle configuration.
"not all of our data revolves around repositories"
you may also have a look at http://www.crnk.io/releases/stable/documentation/#_architecture where the architecture of resource-oriented framework like Crnk and JSON:API are discussed in more detail. In principle one can model everything as repository. Applications usually follow two kinds of patterns: CRUD ones and "actions". CRUD is simple: GET, POST, PATCH, DELETE objects. A repository is a perfect match for that. In contrast, people have a harder time when it comes to "actions". But this can be modelled as well as CRUD. For example, POSTing an AddressChange resource may trigger a server to start modifying the address(es) of some objects. This may happend immediately or take a longer time. Subsequent GET requests for the POSTed resources will reveal the current status of the action. And a DELETE request can cancel the request.
Crnk itself is not in need for Controllers as Spring MVC is. This kind of "lower-level plumbing" is taken care by Crnk itself because JSON:API specifies how a REST layer is supposed to look like. So there is no need to write custom code to specify urls patterns, parameters, etc. as in the MVC example above. Instead one can implement a repository:
public class TaskRepositoryImpl extends ResourceRepositoryBase<Task, Long> {
private ConcurrentHashMap<Long, Task> tasks = new Concurrent...
public TaskRepositoryImpl() {
super(Task.class);
}
#Override
public <S extends Task> S create(S entity) {
map.put(entity.getId(), entity);
return entity;
}
#Override
public ResourceList<Task> findAll(QuerySpec querySpec) {
return querySpec.apply(tasks.values());
}
...
}
There are also many built-in defult repository implementatons like for in-memory, JPA, security to cover the most frequent use cases.
with crnk, no need of writing controllers, manager classes. By default the controllers are defined.
Once we define the resources, we can access it by http://server_name:portno/crnk-path-prefix-property/defined_resourcename & the method type
Eg. In our case, resource is country, let's say server is running in localhost:8081 and crnk-path-prefix is /api/v1, then the url is http://localhost:8081/api/v1/country & set method type is GET, it will give the desired output. Remember to set content-type as application/vnd.api+json.
For POST, same url and set method type as POST, pass the data object
For PATCH, same url along with id attribute appended to the url and set method type as PATCH & pass the data object

Spring data rest and business rules validation

I want to seek a best practice for applying business rules when working with spring data rest.
Lets consider following scenario:
I have a Customer and Order in #OneToMany relationship.
I have a business rule saying that Customer needs to have verified flag set to be able to make orders
So I need to make sure that whenever someone POSTs to /orders the Customer making the call is verified.
I'm considering using beforeSave Validators autowiring other service/repositories into the Validator and check whatever needs to be checked.
Is there better way of achieving the same?
There are several ways to solve this. As far as my knowledge goes:
Usage of spring security annotations like #PreAuthorize. The intended use of these annotations is however for security purposes and you are mentioning business rules. I would use these for user authorization rules Spring data rest security chapter
The use of validators as you mentioned yourself. Spring data rest Validators
Use spring data rest events Spring data rest events. You can create global event handlers, however here you need to determine the entity type. I would go with Annotated event handlers to perform business logic Spring data rest annotated event handler
So just for the sake of world piece I'm adding my solution. Went with #2.
The documentation is pretty clear on how to proceed so just sharing few tips which may save you time.
You need to assign validators manually, auto-discovery doesn't work
Manually spelling event type is error prone, some helper Enum could be handy.
Like:
/**
* "beforeSave" gets called on PATCH/PUT methods
* "beforeCreate" on POST
* "beforeDelete" on DELETE
*/
enum Event {
ON_CREATE("beforeCreate"), ON_UPDATE("beforeSave"),
ON_DELETE("beforeDelete");
private String name;
Event(String name) {
this.name = name;
}
}
...
private static void addValidatorForEvents(ValidatingRepositoryEventListener eventListener, Validator validator, Event... events) {
Arrays.asList(events).forEach(event -> eventListener.addValidator(event.name, validator));
}
One out of the box solution you can use to solve your Business rules related problems, is using Spring AOP. What you can do, is define an Annotation (say #X) and place that annotation on top of your POST call.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface X{}
Next what you need to do is, create an aspect, and run your custom validation logic in this aspect as follows,
#Aspect
#Component
public class CustomAspect {
//You can autowire beans here
#Around("#annotation(qualified name of X)")
public Object customMethod(ProceedingJoinPoint joinPoint) throws Throwable {
flag = customLogic();
if (flag){
return joinPoint.proceed(); //return if logic passes, otherwise
}else{
throw new BusinessRuleException("Business rule violated");
}
}
private boolean customLogic(){
//your custom logic goes here
}
}
And finally apply this annotation on top of any method in controller layer like:
#X
#RequestMapping(method = RequestMethod.POST, value = "do-something")
public void callSomething(HttpServletRequest request) throws Exception {
// your business logic goes here
}
Only thing to note above is that you need to pass HttpServletRequest request explicitly to your controller method in order to AOP aspect get the same context for manipulation of user session related attributes like session_id, etc.
Above solution will help you add business rules on top of your Business Logic and help you with all kinds of pre validations you want to build in your web application. It is a pretty handy application of Spring AOP. Do reach out in case of any

How to auto generate response fields that do not have POJO

We have a service that simply returns the json document on a GET request. Since we do not have the POJO for the response "model", it appears we won't be able to use the auto response fields generation "goodness".
One option for us is to create the Pojos (quite large, about 50 attributes) and a corresponding controller that uses the pojos. This is awkward as we now have to maintain the model and corresponding controller just so we can auto generate the model.
Any ideas on how we can still leverage some auto generation of the response fields would be greatly appreciated.
Here's the controller I'm referring to:
#RestController
#RequestMapping("/api")
public class ProductController {
#Autowired
ProductService productService;
#RequestMapping(value = { "/products/{ids}" }, method = { RequestMethod.GET },
produces = "application/json", headers={"accept=application/json"})
#Timed
#ExceptionMetered
#LogExecutionTime
public String getProductDetails(#PathVariable("id") String id) {
return productService.getProductDetails(id);
}
At the moment I see no way of leveraging the auto generation without putting additional effort into it. Spring Auto REST Docs works by inspecting POJOs with a Jackson visitor (static introspection without runtime information) and there is currently no way of deriving the JSON fields from a string (would be dynamic at runtime). Thus, I only see two options:
The approach that you already described: Creating the corresponding POJO and using it.
Using Spring REST Docs for the corresponding test and manually document each field in the test. Might be the better option here if you do not want to alter the production code.

URI-specific ExceptionMapper in JAX-RS

I'm using Jersey, and Guice as my IOC-container. I'd like to know if it is possible to associate an ExceptionMapper with a specific URI. The reason for this is that I want to map the same exception differently based on what URI was visited. For example, suppose I've got the following two exception mappers for my custom exception:
public class MyExceptionMapperForFirstURI implements
ExceptionMapper<MyException> {..return response based on first URI..}
public class MyExceptionMapperForSecondURI implements
ExceptionMapper<MyException> {..return response based on second URI..}
As far as I understand you bind an ExceptionMapper in your ServletModule as follows:
public class MyModule extends ServletModule {
#Override
public void configureServlets() {
super.configureServlets();
bind(MyCustomExceptionMapper.class);
}
}
How would I go about binding MyExceptionMapperForFirstURI and MyExceptionMapperForSecondURI so that they get associated with the correct URIs. Is this possible, and if possible: is this the correct way to do this?
This is quite late answer ;-) but you can always inject the UriInfo and branch on that. So,
#Context
UriInfo uriInfo;
.....
if (matchesA(uriInfo.getAbsolutePath())) {
// do something
}
Not sure how the URI's of your app look like, but if it is possible to split your app into two servlets or filters, then you can do it like that - i.e. have one servlet/filter serve one set of resources and include the first mapper and have the other servlet/filter serve the other set of resources and include the other mapper.
If these are custom exceptions, you can also pass Request as an argument to the exception and have just a single mapper - decide on the response based in the request uri in the mapper.

Resources