解决Spring报错Content-Type applicationx-www-form-urlencoded;charset=UTF-8 is not supported

笔者报错时的运行环境:

  • 安卓:

    • OkHttp 4.11.0

    • Android Studio Flamingo | 2022.2.1

    • Android SDK 33

    • Gradle 8.0.1

    • JDK 17

  • 后端:

    • Spring Cloud Alibaba:2022.0.0.0-RC2

      扫描二维码关注公众号,回复: 17094856 查看本文章
    • Spring Cloud:2022.0.0

    • Spring Boot:3.0.2

    • Nacos 2.2.3

    • Maven 3.8.3

    • JDK 17.0.7

    • IntelliJ IDEA 2022.3.1 (Ultimate Edition)

问题描述

  最近笔者在安卓中使用 OkHttp 向后端 Spring Cloud 发送请求的时候,整个过程发生了如下报错。

  • 后端 Spring MVC 控制器代码:

    @PostMapping("/XXX")
    @ResponseBody
    @CrossOrigin
    public XXX xxx(HttpServletRequest request, HttpServletResponse response,
                               @RequestBody XXX xxx) {
          
          
        // ...
    }
    

    Spring MVC 报错信息:

    202X-XX-XX XX:XX:XX.XXX [WARN] [http-nio-XXX-exec-X] org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.logException(AbstractHandlerExceptionResolver.java:207) 
     Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content-Type 'application/x-www-form-urlencoded;charset=UTF-8' is not supported]
    
  • 安卓 OkHttp 代码:

    OkHttpClient client = new OkHttpClient();
    RequestBody formBody = new FormBody.Builder()
            .add("XXX", XXX)
            .build();
    Request request = new Request.Builder()
            .url("http://XXXXX:XXXX/XXX")
            .post(formBody)
            .build();
    try (Response response = client.newCall(request).execute()) {
          
          
        if (!response.isSuccessful()) {
          
          
            Log.e(TAG, "请求失败。返回码:" + response);
            this.runOnUiThread(() -> {
          
          
                Toast.makeText(this, "请求失败", Toast.LENGTH_SHORT).show();
            });
            return;
        }
        // ... 请求发送成功...
    } catch (IOException exception) {
          
          
        Log.e(TAG, "服务器连接失败");
        Log.e(TAG, ExceptionUtil.getStackTraceString(exception));
        this.runOnUiThread(() -> {
          
          
            Toast.makeText(this, "服务器连接失败", Toast.LENGTH_SHORT).show();
        });
    }
    

    OkHttp 报错信息:

    Response{protocol=http/1.1, code=415, message=Unsupported Media Type, url=http://XXX.XXX.XXX.XXX:XXXXX/XXX/XXX}
    

问题原因

  这是因为上面使用 OkHttp 发送 POST 请求时,使用的是 FormBody.Builder() 构造的请求体,这样发送的 HTTP 报文的 Content-Type 就会被设置成 application/x-www-form-urlencoded;charset=UTF-8

  而后端 Spring MVC 在接收端使用的是 @RequestBody 注解,这样就会只认可值为 application/jsonContent-Type。因此,Spring Cloud 微服务拒绝了这个请求。

  可以验证这一点,使用 Wireshark 抓包时,OkHttp 发送的 HTTP 报文如下。

在这里插入图片描述

  而 Spring Cloud 微服务拒绝了这个请求,返回码为 415,且说明只接受值为 application/jsonContent-Type

在这里插入图片描述

解决办法

  这种问题是客户端与服务端发送数据没有协调好导致的,因此可以选择修改它们其中的任意一方来完成适配。

  这里以修改安卓 OkHttp 代码为例,改使用 RequestBody.create 来创建请求体即可。

OkHttpClient client = new OkHttpClient();
var jsonType = MediaType.parse("application/json; charset=utf-8");
String content = JsonUtil.pojo2Json(XXX);
RequestBody requestBody = RequestBody.create(jsonType, content);
Request request = new Request.Builder()
        .url("http://XXXXX:XXXX/XXX")
        .post(requestBody)
        .build();
try (Response response = client.newCall(request).execute()) {
    
    
    if (!response.isSuccessful()) {
    
    
        Log.e(TAG, "请求失败。返回码:" + response);
        this.runOnUiThread(() -> {
    
    
            Toast.makeText(this, "请求失败", Toast.LENGTH_SHORT).show();
        });
        return;
    }
    // ... 请求发送成功...
} catch (IOException exception) {
    
    
    Log.e(TAG, "服务器连接失败");
    Log.e(TAG, ExceptionUtil.getStackTraceString(exception));
    this.runOnUiThread(() -> {
    
    
        Toast.makeText(this, "服务器连接失败", Toast.LENGTH_SHORT).show();
    });
}

猜你喜欢

转载自blog.csdn.net/wangpaiblog/article/details/131820727