nginx 这个属性一定要设为 关闭 :proxy_buffering off;

okhttp3默认是禁止重定向的,但是forest内部实现了重定向,如果想让okhttp3和forest实现一样的功能,需要在拦截器中配置重定向定制

 

pom文件  

<dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.12.0</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp-sse</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

拦截器

package com.uniauth.satoken.forest;

import okhttp3.*;
import okio.Buffer;
import java.io.IOException;
public class CustomLoggingInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);

        System.out.println("=== Request Headers ===");
        for (String name : request.headers().names()) {
            System.out.println(name + ": " + request.header(name));
        }

        if (request.body() != null) {
            Buffer buffer = new Buffer();
            request.body().writeTo(buffer);
            System.out.println("=== Request Body ===");
            System.out.println(buffer.readUtf8());
        }

        System.out.println("Request URL: " + request.url());
        System.out.println("Response Code: " + response.code());
        if (response.isRedirect()) {
            String location = response.header("Location");
            System.out.println("Redirect to: " + location);
            if (location != null && !location.isEmpty()) {
                // 构建新的请求,保留原始方法 (POST) 和 Body
                Request newRequest = request.newBuilder()
                        .url(location) // 新的 URL
                        // 确保保留原始的 Body 和 Headers
                        .method(request.method(), request.body())
                        .build();
                System.out.println("重定向====保留原始method:"+request.method());
                response.close();
                return chain.proceed(newRequest);
            }
        }
        return response;
    }
}
Configuration:如果需要手动重定向,不要在 addNetworkInterceptor 直接写,要在拦截器中写好,通过.addInterceptor(new CustomLoggingInterceptor()) 放进去
因为addNetworkInterceptor 在这里写,会触发安全认证,导致写的重定向不生效
package com.uniauth.satoken.forest;

import okhttp3.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;

@Configuration
public class Okhttp3Configuration {
    @Bean
    public OkHttpClient okHttpClient() {
        return new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .followRedirects(false)
                .followSslRedirects(false)
//                .addNetworkInterceptor(chain -> {
//
//                })
                .addInterceptor(new CustomLoggingInterceptor())
                .build();
    }
}

controller

@GetMapping(value = "/chat",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter chat(@RequestParam @NotBlank String query) {
        SseEmitter emitter = new SseEmitter(0L);
        try{
             RequestBody requestBody =
                  RequestBody.create(okhttp3.MediaType.parse("application/json"), JSON.toJSONString(conversationParam));
            Request difyRequest = new Request.Builder()
                        .url("http://xxx.cn/ab/xxxxxxxx/runs")
                        .post(requestBody)
                        .addHeader("Authorization", Authorization)
                        .addHeader("X-Authorization", XAuthorization)
                        .addHeader("content-type", "application/json")
                        .build();
           return SseForwardUtils.sseForward(difyRequest, okHttpClient);
        }catch (Exception e){
            emitter.completeWithError(e);
        }
        return emitter;
    }

 

image