Skip to content

Request validation

Kappa can validate your incoming HTTP requests against your OpenAPI descriptions. Malformed requests won't reach the spring controllers, hence the bad request will fail early. Still the HTTP client will receive a programmer-readable error description about what went wrong.

Installation

<dependency>
  <groupId>com.github.erosb</groupId>
  <artifactId>kappa-spring</artifactId>
  <version>2.0.2</version>
</dependency>
  testImplementation("com.github.erosb:kappa-spring:2.0.2")

Enable OpenAPI-based HTTP request validation

openapi/pets-api.yaml :

openapi: "3.1.0"
info:
  title: "Pets API"
  version: 0.0.1
paths:
  /api/pets:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePetRequest"
components:
  schemas:
    CreatePetRequest:
      type: object
      additionalProperties: false
      required:
        - name
        - owner
      properties:
        name:
          $ref: "#/components/schemas/Name"
        owner:
          $ref: "./common-types.yaml#/UserIdentifier"
        birthDate:
          type: string
          format: date
    Name:
      type: string
      minLength: 1

KappaSpringBootExampleApplication.java:

@SpringBootApplication
@EnableKappaRequestValidation // (1)
public class KappaSpringBootExampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(KappaSpringBootExampleApplication.class, args);
    }

    @Bean
    public KappaSpringConfiguration kappaConfig() {
        var kappaConfig = new KappaSpringConfiguration();
        var mapping = new LinkedHashMap<String, String>();
        mapping.put("/**", "/openapi/pets-api.yaml");
        kappaConfig.setOpenapiDescriptions(mapping);
        return kappaConfig;
    }
}
  1. Sets up a servlet filter for validating incoming HTTP requests
record UserIdentifier(String id) {
}

record CreatePetRequest(String name, UserIdentifier owner, LocalDate birthDate) {
}

@RestController
@RequestMapping("/api/pets")
public class PetController {

    @PostMapping
    void createPet(@RequestBody CreatePetRequest requestBody) {
        System.out.println("requestBody = " + requestBody);
    }
}

Try it out!

If you start KappaSpringBootExampleApplication and send a request with curl (or your preferred HTTP client), the request will also be validated:

curl -XPOST http://localhost:8080/api/pets \
  -H 'content-type: application/json' \
  --data '{"name": null,"type":"cat","owner":{"id": -5},"birthDate":"20230708"}'

the above command will print the following output:

{
  "errors" : [ {
    "dataLocation" : "$request.body#/type (line 1, position 22)",
    "schemaLocation" : "openapi/pets-api.yaml#/components/schemas/CreatePetRequest/additionalProperties",
    "dynamicPath" : "#/$ref/additionalProperties/false",
    "message" : "false schema always fails"
  }, {
    "dataLocation" : "$request.body#/name (line 1, position 10)",
    "schemaLocation" : "openapi/pets-api.yaml#/components/schemas/Name/type",
    "dynamicPath" : "#/$ref/properties/name/$ref/type",
    "message" : "expected type: string, actual: null"
  }, {
    "dataLocation" : "$request.body#/owner/id (line 1, position 43)",
    "schemaLocation" : "openapi/common-types.yaml#/Id",
    "dynamicPath" : "#/$ref/properties/owner/$ref/properties/id/$ref/minimum",
    "message" : "-5 is lower than minimum 0"
  }, {
    "dataLocation" : "$request.body#/birthDate (line 1, position 59)",
    "schemaLocation" : "openapi/pets-api.yaml#/components/schemas/CreatePetRequest/properties/birthDate/format",
    "dynamicPath" : "#/$ref/properties/birthDate/format",
    "message" : "instance does not match format 'date'"
  } ]
}

These json schema validation errors tell us the following problems with the json payload:

  • the "type" field sent in the request is not recognized by the service
  • the "name" should be a string, never null, like in our request
  • the "owner.id" property should be non-negative, so -5 is invalid
  • the "birthDate" also does not match the expected date format

Managing undocumented endpoints

Sometimes you need to exclude certain requests from OpenAPI-based validation. This is common for:

  • Non-REST endpoints (like static resources or health checks)
  • Endpoints that don't have OpenAPI documentation yet
  • Third-party integrations (like Swagger UI)

You can configure these exclusions using the ignoredPathPatterns property in KappaSpringConfiguration.

How it works

When an incoming HTTP request is processed:

  1. First, Kappa checks if the request path matches any pattern in openapiDescriptions
  2. If no match is found, Kappa checks if the path matches any pattern in ignoredPathPatterns
  3. If matched in ignoredPathPatterns, the request bypasses validation and proceeds normally
  4. If not matched anywhere, Kappa returns a validation error

Example - configuring Swagger UI

Given you already have OpenAPI descriptions for all your endpoints, it is easy to make those available via swagger UI. We just have to make sure that the swagger UI related HTTP requests aren't rejected by Kappa due to OpenAPI the validation.

Let's go through it step by step:

Maven:

  <dependency>
      <groupId>org.springdoc</groupId>
      <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
      <version>2.8.9</version>
  </dependency>

Gradle:

implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9")

application.yaml:

springdoc:
  swagger-ui:
    urls:
      - name: Pets API
        url: /openapi/pets-api.yaml
  @Bean
    public KappaSpringConfiguration kappaConfig() {
        var kappaConfig = new KappaSpringConfiguration();
        var mapping = new LinkedHashMap<String, String>();
        mapping.put("/api/**", "/static/openapi/pets-api.yaml"); // (1)
        kappaConfig.setOpenapiDescriptions(mapping);
        kappaConfig.setIgnoredPathPatterns(
          "/swagger-ui/**", // (2)
          "/v3/api-docs/**", // (3)
          "/openapi/**" // (4)
        );
        return kappaConfig;
    }
  1. Make sure the openapi descriptions are under src/main/resources/static (this is required for springdoc to find the files)
  2. Enable requests for loading Swagger UI client
  3. Enable loading swagger UI configuration
  4. Enable loading our OpenAPI descriptions

Try it out!

The Swagger UI setup is already included in the kappa-examples repo. Let's just clone and try it locally:

  • git clone https://github.com/erosb/kappa-examples
  • cd kappa-examples/kappa-spring-boot-examples
  • ./gradlew bootRun
  • open your browser at http://localhost:8080/swagger-ui/index.html