要实现一个 Spring Boot + SSE (Server-Sent Events) 服务端,通过调用 OpenAI API 获取数据并解析后返回给前端,你可以分为几个步骤来完成。具体来说,前端会通过 SSE 连接到 Spring Boot 后端,然后后端会调用 OpenAI API(比如 ChatGPT API),解析返回的数据,并通过 SSE 实时推送给前端。

步骤概述

  1. 搭建 Spring Boot 项目
    创建一个 Spring Boot 项目,添加相关依赖。
  2. 调用 OpenAI API
    使用 HTTP 客户端(如 RestTemplate 或 WebClient)向 OpenAI API 发起请求,获取返回的数据。
  3. 解析返回数据
    解析 OpenAI API 返回的 JSON 数据。
  4. 通过 SSE 向前端推送数据
    使用 Spring Boot 的 SseEmitter 实现 Server-Sent Events,将解析的数据推送到前端。
  5. 前端接收数据
    使用 JavaScript 的 EventSource 接收从后端推送的事件数据,并在页面上实时显示。

1. 创建 Spring Boot 项目

使用 Spring Initializr 或 IDE 创建一个新的 Spring Boot 项目,添加以下依赖:

  • Spring Web
  • Spring Boot DevTools
  • Jackson (默认包含)
  • Spring Boot Starter JSON

pom.xml 示例依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-json</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <!-- OpenAI API RestClient -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
</dependencies>

2. 配置 OpenAI API

你需要申请 OpenAI API 密钥并将其保存到项目的配置文件中。

在 application.properties 或 application.yml 中添加 OpenAI API 密钥:

openai.api.key=YOUR_OPENAI_API_KEY

3. 调用 OpenAI API

Spring 提供了 RestTemplate 或 WebClient 来进行 HTTP 请求,我们选择 WebClient(异步方式)来进行 OpenAI API 调用。

首先,配置 WebClient bean:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {

    @Bean
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }
}

4. 调用 OpenAI API 的服务

创建一个 OpenAIService 类来封装与 OpenAI API 的交互:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.http.MediaType;
import reactor.core.publisher.Mono;

@Service
public class OpenAIService {

    @Value("${openai.api.key}")
    private String apiKey;

    private final WebClient.Builder webClientBuilder;

    public OpenAIService(WebClient.Builder webClientBuilder) {
        this.webClientBuilder = webClientBuilder;
    }

    public Mono<String> callOpenAI(String prompt) {
        String apiUrl = "https://api.openai.com/v1/completions";

        return webClientBuilder.baseUrl(apiUrl)
                .defaultHeader("Authorization", "Bearer " + apiKey)
                .defaultHeader("Content-Type", "application/json")
                .post()
                .bodyValue("{\"model\":\"text-davinci-003\", \"prompt\":\"" + prompt + "\", \"max_tokens\":100}")
                .retrieve()
                .bodyToMono(String.class);
    }
}
  • callOpenAI() 方法会发起一个 POST 请求,传递用户的提示语(prompt)给 OpenAI API,并获取模型返回的结果。

5. 使用 SSE 实时推送数据

在控制器中,使用 SseEmitter 来实现 SSE 功能,将数据实时推送到前端。

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.publisher.Mono;

@RestController
public class OpenAIController {

    private final OpenAIService openAIService;

    public OpenAIController(OpenAIService openAIService) {
        this.openAIService = openAIService;
    }

    @GetMapping("/openai-sse")
    public SseEmitter streamOpenAIResponse(@RequestParam String prompt) {
        SseEmitter emitter = new SseEmitter();

        openAIService.callOpenAI(prompt)
                .doOnTerminate(emitter::complete)
                .subscribe(response -> {
                    try {
                        emitter.send(response, MediaType.APPLICATION_JSON);
                    } catch (Exception e) {
                        emitter.completeWithError(e);
                    }
                });

        return emitter;
    }
}
  • 通过 @GetMapping("/openai-sse"),客户端可以通过该端点访问数据流。
  • SseEmitter 用于将响应数据实时推送到前端。

6. 前端接收 SSE 数据

在前端,使用 EventSource API 来接收从后端推送的 SSE 数据。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OpenAI SSE Example</title>
</head>
<body>
    <h1>OpenAI Response:</h1>
    <div id="response"></div>

    <script type="text/javascript">
        const prompt = "Tell me a joke";  // Example prompt
        const eventSource = new EventSource(`/openai-sse?prompt=${encodeURIComponent(prompt)}`);

        eventSource.onmessage = function(event) {
            const newElement = document.createElement("div");
            newElement.textContent = `Response: ${event.data}`;
            document.getElementById("response").appendChild(newElement);
        };

        eventSource.onerror = function(event) {
            console.error("Error occurred:", event);
            eventSource.close();
        };
    </script>
</body>
</html>

7. 测试和调试

  • 启动 Spring Boot 应用,访问前端页面,前端页面会通过 SSE 接收到从 OpenAI API 返回的实时数据。
  • 调试过程包括检查 OpenAI 请求是否成功返回,并确保 SSE 数据流正常推送。

总结

  1. 后端:
    • 使用 WebClient 调用 OpenAI API,获取模型响应。
    • 使用 SseEmitter 将数据实时推送给前端。
  2. 前端:
    • 使用 EventSource API 接收 SSE 数据流,并实时展示返回结果。

通过这种方式,你可以实现 Spring Boot 后端与 OpenAI API 的集成,并使用 SSE 向前端实时推送数据。