{"id":35640618,"url":"https://github.com/xently/header-validator-starter","last_synced_at":"2026-01-13T23:02:52.400Z","repository":{"id":329106840,"uuid":"1118146625","full_name":"xently/header-validator-starter","owner":"xently","description":"Spring Boot autoconfiguration starters that validate incoming HTTP request headers","archived":false,"fork":false,"pushed_at":"2026-01-07T09:59:04.000Z","size":147,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-13T19:50:14.061Z","etag":null,"topics":["autoconfiguration","flux","headers","mvc","requests","spring-boot","starter","web"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xently.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-17T10:27:01.000Z","updated_at":"2026-01-07T09:57:23.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/xently/header-validator-starter","commit_stats":null,"previous_names":["xently/header-validator-starter"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/xently/header-validator-starter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xently%2Fheader-validator-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xently%2Fheader-validator-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xently%2Fheader-validator-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xently%2Fheader-validator-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xently","download_url":"https://codeload.github.com/xently/header-validator-starter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xently%2Fheader-validator-starter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28405148,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T21:51:37.118Z","status":"ssl_error","status_checked_at":"2026-01-13T21:45:14.585Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autoconfiguration","flux","headers","mvc","requests","spring-boot","starter","web"],"created_at":"2026-01-05T11:13:45.252Z","updated_at":"2026-01-13T23:02:52.393Z","avatar_url":"https://github.com/xently.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Header Validator Starter\n\n## Overview\n\nThis repository contains Spring Boot autoconfiguration starters that validate incoming HTTP request headers for both\nSpring MVC (Servlet) and Spring WebFlux applications, plus demo applications showing how to use each variant.\n\nThe starters integrate at the framework level and expose configuration properties to declare which headers are required\nand how they should be validated. They also ship lightweight request/response filters and an exception handler that\nreturns a standardised error payload when validation fails.\n\nModules in this repo are separate Maven projects:\n\n- `header-validator-common`: shared, web‑stack agnostic logic\n- `header-validator-starter-web`: Servlet/MVC auto‑configuration starter (updated artefact for the web/servlet variant)\n- `header-validator-starter-webflux`: WebFlux auto‑configuration starter\n- `demo-web`: Spring MVC demo app consuming the Servlet starter\n- `demo-webflux`: Spring WebFlux demo app consuming the WebFlux starter\n\n## Stack\n\n- **Language:** Java 21\n- **Frameworks:** Spring Boot 3.5.9 (Servlet and WebFlux)\n- **Build tool:** Maven, with Maven Wrapper (`mvnw`, `mvnw.cmd`) in root\n- **Testing:** JUnit 5, Spring Boot Test, Rest Assured (demo)\n\n## Key features\n\n- Default header rules are provided and can be extended/overridden with configuration properties.\n- Pluggable validation via `HeaderValidator` (functional interface). Built-ins include:\n    - Regex-based validator for values like `X-MinorServiceVersion` and `X-CallBackURL`\n    - Epoch timestamp validator for `X-TimeStamp`\n- Error handling: Invalid or missing headers are collected; a 400 Bad Request is returned with a standardised body\n  (fields such as `messageCode=4000453`, `statusDescription=Failed`, and one error per offending header).\n- Filters:\n    - `RequestContextFilter` creates a request-scoped context with a conversation ID.\n\n## Requirements\n\n- Java 21 (JDK 21)\n- Maven 3.9+ (or use the included Maven Wrapper)\n\n## Getting started\n\n1) Build and install all modules locally (from the repository root):\n\n   Unix/macOS:\n\n    ```shell\n    ./mvnw clean install\n    ```\n\n   Windows PowerShell:\n\n    ```shell\n    ./mvnw.cmd clean install\n    ```\n\n   This installs the starters (`header-validator-starter-web` and `header-validator-starter-webflux`) to your\n   local Maven repository so the demos can consume them.\n\n2) Add the dependency (choose ONE per application):\n\n   Servlet/MVC application (Tomcat/Jetty/Undertow):\n\n   ```xml\n   \u003cdependency\u003e\n     \u003cgroupId\u003eke.co.xently\u003c/groupId\u003e\n     \u003cartifactId\u003eheader-validator-starter-web\u003c/artifactId\u003e\n     \u003cversion\u003e3.1.0\u003c/version\u003e\n   \u003c/dependency\u003e\n   ```\n\n   WebFlux application (Reactor Netty):\n\n   ```xml\n   \u003cdependency\u003e\n     \u003cgroupId\u003eke.co.xently\u003c/groupId\u003e\n     \u003cartifactId\u003eheader-validator-starter-webflux\u003c/artifactId\u003e\n     \u003cversion\u003e3.1.0\u003c/version\u003e\n   \u003c/dependency\u003e\n   ```\n\n   Important: Do not add both starters to the same application. Use the one that matches your web runtime.\n\n3) Run the demo application(s):\n\n   Unix/macOS:\n\n    ```shell\n    cd demo-web # OR demo-webflux\n    ./mvnw spring-boot:run\n    ```\n\n   Windows PowerShell:\n\n    ```shell\n    Set-Location demo-web # OR demo-webflux\n    ./mvnw.cmd spring-boot:run\n    ```\n\n   Then call the demo endpoint (requires headers; see below):\n\n    ```http request\n    GET http://localhost:8080/api/hello\n    ```\n\n   Sample call file: [request.http](request.http).\n\n## Configuration\n\nBoth starters expose the following configuration properties (Spring Boot relaxed binding applies):\n\n- `xently.api.headers.validation.headers` — a list of header rules. Each rule supports:\n    - `header-name` (string, required): The HTTP header name.\n    - `required` (boolean, default true): Whether the header must be present and non-empty.\n    - `validator` (string, optional): A `HeaderValidator` implementation. The value can be:\n        - a fully qualified class name (FQCN) with a public no-arg constructor, or\n        - a Spring bean name of a `HeaderValidator`.\n          The lookup order is controlled by `xently.api.headers.validator.source` (see below).\n\n- `xently.api.headers.validator.source` — optional, enum controlling how validator strings are resolved:\n    - `FQCN` — use FQCN only\n    - `BeanDefinition` — use Spring bean name only\n    - `FQCNB4BeanDefinition` — try FQCN, then fall back to bean name (default)\n    - `BeanDefinitionB4FQCN` — try bean name, then fall back to FQCN\n\n### Default headers\n\nIf you don’t configure anything, these headers are validated by default (can be extended/overridden):\n\n- X-FeatureCode (optional)\n- X-FeatureName (required)\n- X-ServiceCode (required)\n- X-ServiceName (required)\n- X-ServiceSubCategory (optional)\n- X-MinorServiceVersion (required) — regex `v?\\d+(\\.\\d+){0,2}` (case-insensitive)\n- X-ChannelCategory (required)\n- X-ChannelCode (required)\n- X-ChannelName (required)\n- X-RouteCode (optional)\n- X-TimeStamp (optional) — epoch seconds validated by `EpochTimestampValidator`\n- X-ServiceMode (optional)\n- X-SubscriberEvents (optional)\n- X-CallBackURL (optional) — must match HTTP(S) URL (simple regex)\n\nExample: adding/overriding headers (demo-web; for WebFlux, bean/package names will differ slightly)\n\n[demo-web/src/main/resources/application.properties](demo-web/src/main/resources/application.properties) shows how to:\n\n- Add new headers (required and optional)\n- Provide a custom validator by FQCN\n- Override a default header with a validator provided via Spring bean name\n\n### Snippet\n\n#### YAML\n\n```yaml\nxently:\n  api:\n    headers:\n      validator:\n        source: beandefinitionb4fqcn\n      validation:\n        headers:\n          - header-name: X-Additional-Required\n          - header-name: X-Additional-Optional\n            required: false\n          - header-name: X-Additional-Custom-Validator\n            required: false\n            validator: CustomValidator # Spring bean name\n          - header-name: X-Timestamp # Override the default X-TimeStamp header to accept ISO-8601 instead\n            required: false\n            validator: co.ke.xently.demoweb.validators.ISO8601TimestampHeaderValidator\n```\n\n#### Properties\n\n```properties\nxently.api.headers.validator.source=beandefinitionb4fqcn\nxently.api.headers.validation.headers.[0].header-name=X-Additional-Required\nxently.api.headers.validation.headers.[1].header-name=X-Additional-Optional\nxently.api.headers.validation.headers.[1].required=false\nxently.api.headers.validation.headers.[2].header-name=X-Additional-Custom-Validator\nxently.api.headers.validation.headers.[2].required=false\n# Spring bean name\nxently.api.headers.validation.headers.[2].validator=CustomValidator\n# Override the default X-TimeStamp header to accept ISO-8601 instead\nxently.api.headers.validation.headers.[3].header-name=X-Timestamp\nxently.api.headers.validation.headers.[3].required=false\nxently.api.headers.validation.headers.[3].validator=validators.co.ke.xently.demoweb.ISO8601TimestampHeaderValidator\n```\n\n#### Environment variables\n\nAll properties can be supplied via environment variables using Spring Boot’s relaxed binding rules. Examples:\n\n- `xently.api.headers.validator.source` → `XENTLY_API_HEADERS_VALIDATION_VALIDATOR_SOURCE`\n- `xently.api.headers.validation.headers.[0].header-name` → `XENTLY_API_HEADERS_VALIDATION_HEADERS_0__HEADER_NAME`\n\nNote the conversion rules: dots to underscores, list indexes like `[0]` to `_0_` (often written as `0__` to separate\nlevels), and kebab-case keys to upper snake case.\n\n### Custom validators\n\nImplement the [\n`HeaderValidator`](header-validator-common/src/main/java/co/ke/xently/common/headers/validators/HeaderValidator.java)\ninterface.\n\nYou can provide validators by:\n\n- **FQCN:** set `validator=com.example.MyValidator` (class must have a public no-arg constructor), or\n- **Bean name:** declare `@Component class MyValidator implements HeaderValidator` and set `validator=MyValidator`.\n\n## Payload conversion and error response customisation\n\nThe starters expose a simple extension point via the [\n`PayloadConverter`](header-validator-common/src/main/java/co/ke/xently/common/utils/converter/PayloadConverter.java)\ninterface to help you:\n\n- Customise the body returned when header validation fails.\n- Convert the deserialized request-body into a \"request payload\" type that can carry a `messageID`.\n\n### Overriding the default behavior\n\nTo override, define your own Spring bean of type `PayloadConverter` in your application. Because the default bean is\ncreated with `@ConditionalOnMissingBean`, your bean will take precedence automatically.\n\nMinimal example (works for both MVC and WebFlux apps):\n\n```java\nimport co.ke.xently.common.headers.exceptions.HeadersValidationException;\nimport co.ke.xently.common.utils.converter.PayloadConverter;\nimport co.ke.xently.common.utils.dto.Request;\nimport co.ke.xently.common.utils.dto.ResponsePayload;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.lang.NonNull;\n\n@Configuration\nclass CustomPayloadConverterConfig {\n    @Bean\n    PayloadConverter payloadConverter() {\n        return new PayloadConverter() {\n            @Override\n            public @NonNull Object convertToRequestPayload(@NonNull Object payload) {\n                // Optionally map your DTO to Request so the starters can extract messageID.\n                // If payload is already a Request, just return it.\n                return payload; // replace with mapping logic if needed\n            }\n\n            @Override\n            public @NonNull Object convertToHeaderValidationErrorResponse(\n                    @NonNull ResponsePayload\u003c?\u003e defaultBody,\n                    @NonNull HeadersValidationException exception) {\n                // Optionally transform the default error payload into a custom structure.\n                return defaultBody; // or return your own DTO/map\n            }\n        };\n    }\n}\n```\n\n## Default error response\n\nOn validation failure header validation throws `HeadersValidationException`. The starter’s error handlers convert it\nto a 400 response similar to:\n\n```json\n{\n  \"conversationID\": \"...\",\n  \"messageID\": \"...\",\n  \"messageCode\": \"4000453\",\n  \"messageDescription\": \"Invalid or missing request headers\",\n  \"statusCode\": \"0\",\n  \"statusDescription\": \"Failed\",\n  \"additionalData\": [],\n  \"errorInfo\": [\n    {\n      \"errorCode\": \"\u003cheader-name\u003e\",\n      \"errorDescription\": \"\u003cdescriptive-error-message\u003e\"\n    }\n  ]\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxently%2Fheader-validator-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxently%2Fheader-validator-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxently%2Fheader-validator-starter/lists"}