How to Extend BaseController for Custom Business Logic in ContiNew Admin
Extend BaseController and override the preHandle hook or add new methods to inject custom logic while inheriting standard CRUD and Sa-Token permission checks.
ContiNew Admin provides a robust foundation for REST APIs through its generic BaseController class. This article explains how to leverage inheritance and hook methods to add specialized endpoints or custom security rules without rewriting boilerplate code. By extending the base class located in continew-common, you inherit automatic CRUD operations, permission validation, and request signing bypass logic.
Understanding the BaseController Architecture
The BaseController in continew-common/src/main/java/top/continew/admin/common/base/controller/BaseController.java extends the framework's AbstractCrudController to provide a centralized entry point for RESTful operations. It implements a pre-handle hook that enforces Sa-Token permission checks, handles request signing bypass scenarios, and manages path-exclusion logic before executing any CRUD operation.
The class uses five generic type parameters to maintain type safety across layers:
- S – The service interface extending
BaseService<L, D, Q, C> - L – The list-type DTO used for pagination responses
- D – The detail-type DTO representing a single record
- Q – The query object encapsulating filter criteria
- C – The create/update request object for write operations
Concrete controllers declare which CRUD endpoints to expose using the @CrudRequestMapping annotation. This annotation triggers the generation of standard methods (list, get, create, update, delete, export) via the inherited AbstractCrudController implementation.
Extending BaseController for Custom Endpoints
To add business-specific functionality, create a controller class that extends BaseController with the appropriate generic parameters. You can define new request-mapping methods using standard Spring MVC annotations (@GetMapping, @PostMapping, etc.) alongside the automatically generated CRUD endpoints.
The parent class injects the service layer into the protected baseService field, giving you direct access to business logic without additional wiring.
package top.continew.admin.system.controller;
import top.continew.admin.common.base.controller.BaseController;
import top.continew.admin.system.model.req.user.ResetAttemptsReq;
import top.continew.admin.system.model.resp.user.UserResp;
import top.continew.admin.system.service.UserService;
import top.continew.admin.system.model.query.UserQuery;
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
import top.continew.starter.extension.crud.enums.Api;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
@RestController
@RequiredArgsConstructor
@CrudRequestMapping(value = "/system/user", api = {Api.PAGE, Api.LIST, Api.GET, Api.CREATE, Api.UPDATE, Api.BATCH_DELETE})
public class CustomUserController
extends BaseController<UserService, UserResp, UserResp, UserQuery, ResetAttemptsReq> {
@SaCheckPermission("system:user:resetAttempts")
@PostMapping("/{id}/reset-attempts")
public void resetLoginAttempts(@PathVariable Long id) {
// Directly call the service layer through the injected baseService
baseService.resetLoginAttempts(id);
}
}
Overriding the Pre-Handle Hook for Custom Security Logic
The preHandle method in BaseController serves as the primary extension point for customizing security and validation logic. This method receives the target method, its arguments, and the CRUD API type, allowing you to intercept requests before they reach the business layer.
Permission prefix generation follows a consistent pattern managed by CrudApiPermissionPrefixCache. The system extracts a resource name from the controller class (e.g., system:user) and constructs permission strings as prefix:apiName (e.g., system:user:create). Sign-request bypass occurs automatically when the request contains a valid Sa-Token signature parameter, skipping permission checks for trusted clients.
Override preHandle to implement special rules while optionally delegating to super.preHandle(...) to retain default behavior:
@Override
public void preHandle(CrudApi crudApi, Object[] args,
Method targetMethod, Class<?> targetClass) throws Exception {
// Skip permission checks for the custom "reset-attempts" endpoint
if ("resetLoginAttempts".equals(targetMethod.getName())) {
return;
}
// Retain the default permission checks for all other methods
super.preHandle(crudApi, args, targetMethod, targetClass);
}
Alternatively, apply the @SaIgnore annotation to individual methods or the entire class to completely bypass permission verification.
Managing CRUD API Exposure
Control which standard endpoints are available by configuring the api attribute in @CrudRequestMapping. Pass an array of Api enum values to selectively enable pagination (Api.PAGE), listing (Api.LIST), retrieval (Api.GET), creation (Api.CREATE), updates (Api.UPDATE), or batch deletion (Api.BATCH_DELETE).
Omitting an enum value from the array prevents AbstractCrudController from generating that endpoint, effectively hiding it from the API surface. This approach allows you to expose only the operations relevant to your domain while maintaining the underlying infrastructure.
Summary
- Extend
BaseController<S, L, D, Q, C>with concrete generic types to inherit CRUD operations, permission checks, and request signing logic. - Access the service layer through the protected
baseServicefield without additional dependency injection. - Add custom endpoints by defining standard Spring MVC mapping methods within the extended class.
- Override
preHandle(CrudApi, Object[], Method, Class<?>)to implement custom security rules or bypass default permission checks for specific methods. - Use
@CrudRequestMapping(api = {...})to declaratively control which standard CRUD endpoints are exposed. - Apply
@SaIgnoreto skip permission verification entirely, or rely on the automatic sign-request bypass for trusted clients.
Frequently Asked Questions
What is the difference between BaseController and AbstractCrudController?
BaseController extends AbstractCrudController and adds ContiNew Admin-specific security logic, including Sa-Token permission enforcement and request signing bypass. AbstractCrudController (part of the continew-starter-extension-crud dependency) provides the generic CRUD method implementations, while BaseController acts as the local customization layer for the admin system.
How does the permission prefix get generated for custom endpoints?
CrudApiPermissionPrefixCache extracts the resource identifier from the controller class name and mapping path (e.g., system:user). For custom endpoints, you must explicitly specify the full permission string in @SaCheckPermission (e.g., "system:user:resetAttempts"), as the automatic prefix generation only applies to standard CRUD operations.
Can I disable permission checks for specific methods?
Yes. Apply the @SaIgnore annotation to the method to skip all permission verification. Alternatively, override preHandle and return early for specific method names, or implement conditional logic that bypasses super.preHandle() while retaining it for other operations.
How do I access the service layer in my custom controller?
The parent class injects the service implementation into the protected baseService field. Since your controller declares the service type as the first generic parameter (S), baseService is already typed to your specific service interface (e.g., UserService), allowing direct invocation of business methods without additional @Autowired declarations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →