You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Corda-Spring-Webserver中实现查询过滤与控制器请求过滤?

Hey there! Let's break down how to handle both query filtering for Corda states and request filtering in your Corda-Spring-Webserver controllers—two common needs when building on this stack. I'll walk you through practical, code-first examples for both scenarios.

1. Implementing Query Filtering for Corda States

When you need to filter Corda vault states (like fetching only states owned by a specific party, or with a certain value), you'll leverage Corda's VaultQueryCriteria along with Spring controller parameters to build dynamic filters.

Basic Filtering Example

Suppose you have an IOUState with fields lender, borrower, and value. Here's how to expose a Spring endpoint that lets clients filter by lender or value:

@RestController
@RequestMapping("/api/iou")
class IOUController(private val rpcOps: CordaRPCOps) {

    @GetMapping("/filter")
    fun getFilteredIOUs(
        @RequestParam(required = false) lender: String?,
        @RequestParam(required = false) minValue: Int?
    ): ResponseEntity<List<IOUState>> {
        // Start with a base criteria (e.g., only unconsumed states)
        var criteria = VaultQueryCriteria(Vault.StateStatus.UNCONSUMED)

        // Add lender filter if parameter is provided
        lender?.let {
            val lenderParty = rpcOps.wellKnownPartyFromX500Name(CordaX500Name.parse(it))
                ?: return ResponseEntity.badRequest().body(emptyList())
            criteria = criteria.and(VaultQueryCriteria(VaultQueryCriteria.VaultCustomQueryCriteria(
                Builder.equal(IOUState::lender.name(), lenderParty)
            )))
        }

        // Add minimum value filter if parameter is provided
        minValue?.let {
            criteria = criteria.and(VaultQueryCriteria(VaultQueryCriteria.VaultCustomQueryCriteria(
                Builder.greaterThanOrEqual(IOUState::value.name(), it)
            )))
        }

        // Execute the query
        val results = rpcOps.vaultQueryBy<IOUState>(criteria).states.map { it.state.data }
        return ResponseEntity.ok(results)
    }
}

Adding Pagination & Sorting

For larger datasets, combine filtering with Corda's PageSpecification and Sort:

@GetMapping("/filter-with-pagination")
fun getFilteredIOUsWithPagination(
    @RequestParam(required = false) borrower: String?,
    @RequestParam(defaultValue = "0") page: Int,
    @RequestParam(defaultValue = "10") size: Int,
    @RequestParam(defaultValue = "value") sortBy: String
): ResponseEntity<List<IOUState>> {
    var criteria = VaultQueryCriteria(Vault.StateStatus.UNCONSUMED)

    borrower?.let {
        val borrowerParty = rpcOps.wellKnownPartyFromX500Name(CordaX500Name.parse(it))
            ?: return ResponseEntity.badRequest().body(emptyList())
        criteria = criteria.and(VaultQueryCriteria(VaultQueryCriteria.VaultCustomQueryCriteria(
            Builder.equal(IOUState::borrower.name(), borrowerParty)
        )))
    }

    val pageSpec = PageSpecification(page, size)
    val sort = Sort(Sort.Direction.ASC, sortBy)
    val results = rpcOps.vaultQueryBy<IOUState>(criteria, pageSpec, sort).states.map { it.state.data }
    return ResponseEntity.ok(results)
}
2. Request Filtering for Spring Controllers

This is about intercepting incoming requests to the controller (e.g., validating API keys, checking user permissions, sanitizing inputs). Here are three practical approaches:

Approach 1: Spring HandlerInterceptor

Create a custom interceptor to filter requests before they reach your controller methods:

  1. Implement the Interceptor:
@Component
class RequestValidationInterceptor : HandlerInterceptor {
    override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
        // Example: Validate API key from headers
        val apiKey = request.getHeader("X-API-Key")
        if (apiKey.isNullOrBlank() || !isValidApiKey(apiKey)) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid or missing API key")
            return false
        }

        // Example: Sanitize query parameters
        val unsafeParam = request.getParameter("filter")
        if (!unsafeParam.isNullOrBlank() && containsMaliciousContent(unsafeParam)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid filter parameter")
            return false
        }

        // Allow the request to proceed
        return true
    }

    private fun isValidApiKey(apiKey: String): Boolean {
        // Add your API key validation logic here (e.g., check against a database)
        return apiKey == "your-secure-api-key"
    }

    private fun containsMaliciousContent(content: String): Boolean {
        // Add input sanitization logic (e.g., check for SQL injection patterns)
        return content.contains("DROP TABLE") || content.contains("' OR '1'='1")
    }
}
  1. Register the Interceptor:
@Configuration
class WebConfig : WebMvcConfigurer {
    @Autowired
    private lateinit var requestValidationInterceptor: RequestValidationInterceptor

    override fun addInterceptors(registry: InterceptorRegistry) {
        // Apply to all /api endpoints
        registry.addInterceptor(requestValidationInterceptor)
            .addPathPatterns("/api/**")
            .excludePathPatterns("/api/public/**") // Exclude public endpoints if needed
    }
}

Approach 2: Spring Security @PreAuthorize (for Permission Filtering)

If you're using Spring Security, use annotations to filter requests based on user roles or permissions directly in your controller:

@RestController
@RequestMapping("/api/iou")
class IOUController(private val rpcOps: CordaRPCOps) {

    // Only users with ROLE_ADMIN can access this endpoint
    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/admin/all")
    fun getAllIOUsForAdmin(): ResponseEntity<List<IOUState>> {
        val results = rpcOps.vaultQueryBy<IOUState>().states.map { it.state.data }
        return ResponseEntity.ok(results)
    }

    // Users must have permission to view the specific lender's IOUs
    @PreAuthorize("hasPermission(#lender, 'VIEW_IOU')")
    @GetMapping("/admin/filter-by-lender")
    fun getIOUsByLenderForAdmin(@RequestParam lender: String): ResponseEntity<List<IOUState>> {
        val lenderParty = rpcOps.wellKnownPartyFromX500Name(CordaX500Name.parse(lender))
            ?: return ResponseEntity.badRequest().body(emptyList())
        val criteria = VaultQueryCriteria(VaultQueryCriteria.VaultCustomQueryCriteria(
            Builder.equal(IOUState::lender.name(), lenderParty)
        ))
        val results = rpcOps.vaultQueryBy<IOUState>(criteria).states.map { it.state.data }
        return ResponseEntity.ok(results)
    }
}

Approach 3: Custom Annotation + AOP

Create a custom annotation to mark methods that need filtering, then use AOP to enforce the logic:

  1. Define the Annotation:
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class ValidateRequest
  1. Implement the AOP Aspect:
@Aspect
@Component
class RequestValidationAspect {
    @Before("@annotation(com.yourpackage.ValidateRequest)")
    fun validateRequest(joinPoint: JoinPoint) {
        // Get the request object from the method arguments
        val request = joinPoint.args.firstOrNull { it is HttpServletRequest } as? HttpServletRequest
            ?: throw IllegalArgumentException("No HttpServletRequest found in method arguments")

        // Add your custom validation logic here
        val userId = request.getHeader("X-User-ID")
        if (userId.isNullOrBlank()) {
            throw RuntimeException("User ID header is required")
        }
    }
}
  1. Use the Annotation in Your Controller:
@RestController
@RequestMapping("/api/iou")
class IOUController(private val rpcOps: CordaRPCOps) {

    @ValidateRequest
    @GetMapping("/user")
    fun getIOUsForUser(@RequestHeader("X-User-ID") userId: String): ResponseEntity<List<IOUState>> {
        val userParty = rpcOps.wellKnownPartyFromX500Name(CordaX500Name.parse(userId))
            ?: return ResponseEntity.badRequest().body(emptyList())
        val criteria = VaultQueryCriteria(VaultQueryCriteria.VaultCustomQueryCriteria(
            Builder.equal(IOUState::borrower.name(), userParty)
        ))
        val results = rpcOps.vaultQueryBy<IOUState>(criteria).states.map { it.state.data }
        return ResponseEntity.ok(results)
    }
}

内容的提问来源于stack exchange,提问作者Владимир Евсин

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:04:28