Common module is standard request and response format for Web. Currently Common Module support :
- Standard web response
- Standard paging and sorting request
- Default error handler
<dependency>
<groupId>com.blibli.oss</groupId>
<artifactId>blibli-backend-framework-common</artifactId>
</dependency>
Blibli backend response will always use class Response<T>
.
/**
* Standard Blibli Web Response
*/
public class Response<T> {
/**
* Code , usually same as HTTP Code
*/
private Integer code;
/**
* Status, usually same as HTTP status
*/
private String status;
/**
* Response data
*/
private T data;
/**
* Paging information, if response is paginate data
*/
private Paging paging;
/**
* Error information, if request is not valid
*/
private Map<String, List<String>> errors;
/**
* Metadata information
*/
private Map<String, Object> metadata;
}
We can use ResponseHelper
to construct Response<T>
object.
@RestController
public class CustomerController {
@GetMapping(value = "/api/customers/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<Response<Customer>> findById(@PathVariable("id") String id) {
Mono<Customer> customerMono = service.findById(id);
return customerMono.map(customer -> {
return ResponseHelper.ok(customer);
}).subscribeOn(Schedulers.elastic());
}
}
For Paging, we can use PagingRequest
class for web request, and Paging
class for web response.
We also already implement PagingRequestArgumentResolver
to support argument injection on controller,
so we don't need to parse paging request manually.
@RestController
public class CustomerController {
@GetMapping(
value = "/api/customers",
produces = MediaType.APPLICATION_JSON_VALUE
)
public Mono<Response<List<Customer>>> list(PagingRequest pagingRequest) {
return customerService.find(pagingRequest)
.map(response -> {
Paging paging = PagingHelper.toPaging(pagingRequest, response.getTotal());
return ResponseHelper.ok(response.getCustomers(), paging);
})
.subscribeOn(commandScheduler);
}
}
In the url we can use standard paging :
GET /api/customers?page=1&item_per_page=100
GET /api/customers?page=2&item_per_page=50
GET /api/customers?page=2
We can also configure default paging properties :
blibli.backend.common.paging.default-page=1
blibli.backend.common.paging.default-item-per-page=50
blibli.backend.common.paging.max-item-per-page=1000
In the PagingRequest
we also can give sorting information, and it will automatically injected to PagingRequest
.
GET /api/customers?page=2&sort_by=id:asc,first_name:asc,created_at:desc
GET /api/customers?page=2&sort_by=id,first_name,created_at:desc
We can also configured default sorting properties
blibli.backend.common.paging.default-sort-direction=asc
Common module also contain default error handler to create standard error response.
We can use CommonErrorController
class to create standard error controller.
@Slf4j
@RestControllerAdvice
public class ErrorController implements CommonErrorController, MessageSourceAware {
@Getter
@Setter
private MessageSource messageSource;
@Override
public Logger getLogger() {
return log;
}
}
If we include Swagger Module, we can also make Paging & Sorting feature to be included on Swagger OpenAPI Spec.
We only need to add annotation @PagingRequestInQuery
@RestController
public class CustomerController {
@PagingRequestInQuery
@GetMapping(
value = "/api/customers",
produces = MediaType.APPLICATION_JSON_VALUE
)
public Mono<Response<List<Customer>>> list(PagingRequest pagingRequest) {
// some logic
}
}
Sometimes we want to send metadata information for UI in validation message. We can do this with add @MetaDatas on validation field
public class SampleRequest {
@NotBlank(message = "NotBlank")
private String name;
@MetaDatas(
@MetaData(key = "min", value = "1"),
@MetaData(key = "max", value = "100")
)
@Min(1)
@Max(100)
private Integer age;
When get validation error, response will automatically contains metadata
{
"code" : 400,
"status" : "BAD_REQUEST",
"errors" : {
"age" : [
"TooLarge"
]
},
"metadata" : {
"errors" : {
"age" : {
"min" : "1",
"max" : "100"
}
}
}
}