Robust Validation in the Enterprise Stack

Java Spring Boot remains the backbone of enterprise web development. When building large-scale systems (CRMs, banking portals, or healthcare apps), input validation is critical. While Bean Validation (`@Email`) handles basic syntax, it cannot stop a user from registering with a non-existent email address.

This guide demonstrates how to build a `EmailVerificationService` in Spring Boot that connects to EmailVerifierAPI.com. We will use the modern `java.net.http.HttpClient` available in Java 11+ for non-blocking I/O.

Dependencies

Ensure you have `jackson-databind` in your `pom.xml` or `build.gradle` to parse the JSON response. Most Spring Web starters include this by default.

The Service Implementation

We will create a service that encapsulates the API logic. It builds the request, handles the network call, and maps the JSON response to a Java POJO.

package com.example.demo.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

@Service
public class EmailVerificationService {

    private static final String API_KEY = "YOUR_API_KEY"; // Ideally from application.properties
    private static final String API_URL = "https://www.emailverifierapi.com/v2/verify";
    
    private final HttpClient httpClient;
    private final ObjectMapper objectMapper;

    public EmailVerificationService(ObjectMapper objectMapper) {
        this.httpClient = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(5))
                .build();
        this.objectMapper = objectMapper;
    }

    public boolean isEmailDeliverable(String email) {
        try {
            // Build the URI with query parameters
            String uri = API_URL + "?api_key=" + API_KEY + "&email=" + email;

            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(uri))
                    .GET()
                    .build();

            HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() == 200) {
                JsonNode root = objectMapper.readTree(response.body());
                
                // Extract fields
                String status = root.path("status").asText();
                boolean isDisposable = root.path("disposable").asBoolean();
                
                // Business Logic: 
                // Return true only if Valid AND Not Disposable
                return "valid".equalsIgnoreCase(status) && !isDisposable;
            } else {
                // Handle API errors (4xx, 5xx) - Log and Fail Open or Closed
                System.err.println("API Error: " + response.statusCode());
                return false; 
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

Integrating into a Controller

Now you can inject this service into your REST Controller to validate incoming requests.

@PostMapping("/register")
public ResponseEntity<String> registerUser(@RequestBody UserDto user) {
    if (!emailVerificationService.isEmailDeliverable(user.getEmail())) {
        return ResponseEntity.badRequest().body("Please provide a valid, permanent email address.");
    }
    // Proceed with registration...
    return ResponseEntity.ok("User registered.");
}

Handling "Risky" and "Unknown"

The code above implements a strict check (only "valid"). However, in some enterprise contexts, you may encounter "catch-all" domains (which return `risky` or `unknown`).

For a more nuanced approach, you should inspect the `score` field. A score > 0.7 usually indicates a safe-to-send address even if the specific status is uncertain due to server security settings. You can map the JSON response to a dedicated `EmailVerificationResult` class to pass all this data back to your controller for complex decision-making.

Performance Considerations

While this example uses synchronous `httpClient.send`, for high-throughput applications, you should use `httpClient.sendAsync` to prevent blocking your Servlet threads. This allows your Spring Boot application to handle thousands of concurrent verifications without exhausting the thread pool.