java gettype错误_java - 即使我已经给出了结果,但在Accept-Header中具有参数时,也会得到错误的Content-Type - 堆栈内存溢出...

博客指出在accept-header中添加参数时,Spring因AbstractMessageConverterMethodProcessor的isCompatibleWith()方法难以获得正确响应头。通过示例展示不同请求头下的返回情况,调试分析了Spring处理逻辑,还提及Github相关讨论,并探讨处理无效content-type参数的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近我发现,如果我们在accept-header中添加一些参数,由于org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor的isCompatibleWith()方法,Spring很难获得正确的响应头。

例如,如果我们有一个这样的端点:

@ApiResponse(code = ...)

@RequestMapping(value = {/Id}, method = RequestMethod.GET, produces = {

MediaType.APPLICATION_JSON_VALUE + "; profile='http://profiles/v1'",

MediaType.APPLICATION_JSON_VALUE + "; profile='http://profiles/v2'",

MediaType.APPLICATION_JSON_VALUE + "; profile='http://profiles/v3'",})

@ResponseBody

public HttpEntity ..> getXXX(@PathVariable(value = "Id") String Id) throws Exception {

// business logic

}

现在,如果在请求标头中,我给出:

Accept = " "或No Accept header =>,它将按预期返回Content-Type为"application/json; profile='http://profiles/v1'" 。

Accept = "application/json; profile='http://profiles/v3'" =>它将返回相同的Content-Type,因为producibleMediaTypes存在匹配项。

Accept = "application/json; profile='http://invalid/profiles/v1'" => producibleMediaTypes没有匹配项,如预期的那样,它应该返回默认的Content-Type,它是producibleMediaTypes的第一个。 但是现在,它将返回与我们在Request-Header中提供的值完全相同的值,对于上面的值,我将获得"application/json; profile='http://invalid/profiles/v1'"

调试后,我发现:

对于第一种情况, Spring在进入writeWithMessageConverters()之前将自动将Accept-Header设置为"*/*" ,这意味着在与producibleMediaTypes匹配后,它将基于其比较策略传递默认的Content-Type。

对于第二种情况,由于它在producibleMediaTypes具有完美的匹配,它将返回正确的Content-Type。

对于Error场景,这是Spring处理@ResponseBody :

protected void writeWithMessageConverters(T returnValue, MethodParameter returnType,

ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)

throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

Class> returnValueClass = getReturnValueType(returnValue, returnType);

Type returnValueType = getGenericType(returnType);

HttpServletRequest servletRequest = inputMessage.getServletRequest();

List requestedMediaTypes = getAcceptableMediaTypes(servletRequest);

List producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass, returnValueType);

if (returnValue != null && producibleMediaTypes.isEmpty()) {

throw new IllegalArgumentException("No converter found for return value of type: " + returnValueClass);

}

Set compatibleMediaTypes = new LinkedHashSet();

for (MediaType requestedType : requestedMediaTypes) {

for (MediaType producibleType : producibleMediaTypes) {

if (requestedType.isCompatibleWith(producibleType)) {

compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));

}

}

}

if (compatibleMediaTypes.isEmpty()) {

if (returnValue != null) {

throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);

}

return;

}

requestedMediaTypes就是我们在请求标题过去了, producibleMediaTypes就是我们在提供@ReqeustMapping ,我的理解是,在比较之后,***春*会选择更具体的一个作为selectedMediaType这将是返回响应头。 但是,总是选择无效的作为更具体的一个。

这是有关Github的一些讨论:

似乎他们想改进兼容策略,或者以某种方式允许开发人员实现自己的参数匹配,而不是等待它,还有其他方法可以处理无效的content-type参数吗?

就像是否有任何方法在匹配之前获取producibleMediaTypes ? 如果是这样,我们可以将无效的内容类型标记为"*/* ,那么我们可以按预期获得默认的内容类型。还是可以在将其传递给Response-Header之前再次检查该内容类型?

您以前遇到过这种情况吗? 任何建议或经验都会有所帮助!

<think>好的,用户现在问的是:“我用java你给我kotlin干嘛”,看起来用户之前得到了一个用Kotlin写的示例,但用户实际使用的是Java,所以感到困惑。我需要解决用户的问题,提供Java版本的代码,同解释两者的区别,确保用户理解如何转换。 首先,我应该检查之前的回答,确认确实提供了Kotlin代码而没有Java版本。用户可能对Kotlin不熟悉,或者他们的项目是基于Java的,所以需要调整代码。我需要向用户道歉,说明之前的疏忽,并迅速提供Java的解决方案。 接下来,我需要将Kotlin代码转换为Java,同保持功能一致。例如,OkHttp的同步请求、异步处理、流式响应等部分都需要用Java重写。同,要确保代码示例正确,包括必要的异常处理、JSON解析等。 另外,用户可能不仅需要代码示例,还需要了解为什么之前收到的是Kotlin代码,以及两者之间的主要区别。需要简要解释Kotlin和Java在语法上的不同,比如Lambda表达式、空安全特性等,帮助用户理解转换的逻辑。 同,用户可能希望知道在Java中如何处理JSON解析,比如使用原生的JSONObject还是第三方库如Gson或Jackson,需要提供这两种方式的示例,并说明各自的优缺点,以便用户选择适合自己项目的方法。 还需要注意Java和Kotlin在异步处理上的不同,比如在Kotlin中使用enqueue和Lambda,而Java可能需要更详细的匿名内部类,这部分需要详细展示,避免用户混淆。 最后,确保整个回答结构清晰,步骤明确,用户能够按照示例一步步实现,同强调异常处理和最佳实践,比如资源释放、UI线程更新等,确保代码的健壮性。 总结步骤: 1. 道歉并解释之前的疏忽。 2. 提供Java版本的同步和异步请求示例。 3. 包含流式响应的处理。 4. 对比Kotlin和Java的语法差异,帮助用户理解转换逻辑。 5. 提供JSON解析的多种方法示例。 6. 强调异常处理和资源管理。 7. 给出性能优化建议,如使用单例OkHttpClient。</think>非常抱歉疏忽了您的技术栈要求!以下是使用Java语言实现OkHttp获取Ollama JSON响应的完整方案: --- ### 一、基础依赖配置 ```java // build.gradle 添加依赖 dependencies { implementation 'com.squareup.okhttp3:okhttp:4.12.0' implementation 'com.google.code.gson:gson:2.10.1' // JSON解析库 } ``` --- ### 二、同步请求实现 ```java OkHttpClient client = new OkHttpClient(); String jsonBody = "{\"model\":\"llama2\",\"prompt\":\"为什么天空是蓝色的?\"}"; Request request = new Request.Builder() .url("http://localhost:11434/api/generate") .post(RequestBody.create(jsonBody, MediaType.parse("application/json"))) .build(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("请求失败: " + response); } // 使用Gson解析 String jsonString = response.body().string(); OllamaResponse resp = new Gson().fromJson(jsonString, OllamaResponse.class); System.out.println("答案:" + resp.getResponse()); } catch (IOException e) { e.printStackTrace(); } // 数据模型类 public class OllamaResponse { private String response; @SerializedName("total_duration") private long duration; // getters/setters } ``` --- ### 三、异步请求实现 ```java client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } @Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); } String jsonData = response.body().string(); OllamaResponse resp = new Gson().fromJson(jsonData, OllamaResponse.class); // 更新UI需要切回主线程(Android示例) runOnUiThread(() -> { textView.setText(resp.getResponse()); }); } }); ``` --- ### 四、流式响应处理 ```java Request streamRequest = new Request.Builder() .url("http://localhost:11434/api/generate") .post(RequestBody.create(jsonBody, MediaType.parse("application/json"))) .header("Accept", "application/x-ndjson") .build(); client.newCall(streamRequest).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } @Override public void onResponse(Call call, Response response) throws IOException { try (BufferedSource source = response.body().source()) { while (!source.exhausted()) { String line = source.readUtf8Line(); if (line != null && !line.isEmpty()) { OllamaResponse partial = new Gson().fromJson(line, OllamaResponse.class); // 处理实响应 } } } } }); ``` --- ### 五、关键差异说明(Kotlin vs Java) 1. **Lambda表达式**: - Kotlin: `enqueue { ... }` - Java: `enqueue(new Callback() { ... })` 2. **空安全处理**: ```java // Java需要显式null检查 if (response.body() != null) { String data = response.body().string(); } ``` 3. **JSON解析选择**: - 原生方案: ```java JSONObject json = new JSONObject(responseData); String answer = json.optString("response"); ``` - 推荐Gson方案: ```java Gson gson = new GsonBuilder().create(); Type type = new TypeToken<Map<String, Object>>(){}.getType(); Map<String, Object> data = gson.fromJson(jsonString, type); ``` --- ### 六、最佳实践建议 1. **客户端复用**: ```java // 单例模式管理 public class OkHttpClientManager { private static final OkHttpClient instance = new OkHttpClient(); public static OkHttpClient getClient() { return instance; } } ``` 2. **异常增强处理**: ```java try { // 请求逻辑... } catch (IOException e) { if (e instanceof SocketTimeoutException) { System.out.println("请求超"); } else if (e instanceof UnknownHostException) { System.out.println("域名解析失败"); } } ``` 3. **性能优化**: ```java OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build(); ``` --- 这个Java实现方案与之前Kotlin版本功能完全一致,主要差异体现在语法层面。建议根据实际响应数据结构调整`OllamaResponse`模型类的字段定义。如果遇到SSL证书问题,可添加`OkHttp`的SSL信任配置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值