内容类型为 application/x-www-form-urlencoded 的 HTTP Post 请求在 Spring 引导服务中不起作用

Posted

技术标签:

【中文标题】内容类型为 application/x-www-form-urlencoded 的 HTTP Post 请求在 Spring 引导服务中不起作用【英文标题】:HTTP Post request with content type application/x-www-form-urlencoded not working in Spring boot service 【发布时间】:2019-05-04 03:13:10 【问题描述】:

我最近刚接触 Spring Boot,我正在尝试通过 Retrofit2 REST API 库从一个 android 应用程序发出 HTTP POST 请求,并使用 application/x-www-form-url 编码,但是当我点击我的 Spring Boot POST 服务时,它显示了我以下错误

"status":415,"error":"不支持的媒体 类型","异常":"org.springframework.web.HttpMediaTypeNotSupportedException","消息":"内容 输入 'application/x-www-form-urlencoded;charset=UTF-8' 不是 支持","路径":"/api/login"

有人知道怎么解决吗?

这是我的代码 Android 代码示例

ApiService.java

public interface ApiService 
    @FormUrlEncoded
    @POST("/api/login")
    Call<LoginData> postLogIn(
            @Field("username") String username,
            @Field("password") String password);

ApiHandler.java

    private static final String SERVER_URL = "http://192.168.0.12:8080/";
    private static final long CONNECTION_TIMEOUT = 30;
    public static Retrofit restAdapter;

    //public static final int CONNECTION_TIME_OUT = 120;
    private static Retrofit getRestAdapter() 
        if (restAdapter == null) 
            restAdapter = new Retrofit.Builder()
                    .baseUrl(SERVER_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(getClient()).build();
        
        return restAdapter;
    

    private static OkHttpClient getClient() 
        OkHttpClient.Builder okClientBuilder = new OkHttpClient.Builder();
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        okClientBuilder.addInterceptor(httpLoggingInterceptor);
        okClientBuilder.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
        okClientBuilder.readTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
        okClientBuilder.writeTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
        return okClientBuilder.build();
    

PostHandler.java

@Override
    public void callAPI(final Context context, final ApiClientResponse callback, Object arg0) 
        this.callback = callback;
        apiService.postLogIn(Login.username, Login.password).enqueue(new Callback<LoginData>() 
            @Override
            public void onResponse(Call<LoginData> call, Response<LoginData> response) 
                if (response.isSuccessful()) 
                    LoginData loginData = response.body();
                    successLoginData = loginData;
                    successCallBack();
                 else 
                    ApiErrorHandler.handleError(context, response, errorResponse);
                
            

            @Override
            public void onFailure(Call<LoginData> call, Throwable t) 
                ApiErrorHandler.handleError(context, t, errorResponse);
            

            RetrofitErrorResponse errorResponse = new RetrofitErrorResponse() 
                @Override
                public void errorMessage(String errorMessage) 
                    failureCallBack(errorMessage);
                

                @Override
                public void tSyncError() 

                

                @Override
                public void invalidTokenError() 

                
            ;

        );
    

LoginData.java 模型类

@Generated("org.jsonschema2pojo")
public class LoginData 
    @SerializedName("access_token")
    @Expose
    private String accessToken;
    @SerializedName("token_type")
    @Expose
    private String tokenType;
    @SerializedName("refresh_token")
    @Expose
    private String refreshToken;
    @SerializedName("expires_in")
    @Expose
    private long expiresIn;
    @SerializedName("scope")
    @Expose
    private String scope;

    // getter setter 

这是我的 Spring Boot 应用程序代码示例

MainApplicationClass.java

@SpringBootApplication
public class MainApplicationClass 
    public static void main(String[] args) 
        SpringApplication.run(MainApplicationClass.class, args);
    

Controller.java

@RestController
public class BlogController 
    @Autowired
    BlogRespository blogRespository;
    BlogMockedData blogMockedData = BlogMockedData.getInstance();

    @GetMapping("/blog")
    public List<Blog> index() 
        return blogMockedData.fetchBlogList();
    

    @GetMapping("/blog/id")
    public Blog show(@PathVariable String id) 
        int blogId = Integer.parseInt(id);
        return blogMockedData.getBlogById(blogId);
    

    @PostMapping(value = "/api/login",
            consumes = MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_FORM_URLENCODED_VALUE,
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.APPLICATION_JSON_VALUE
    )
    public LoginData postLogin(@RequestBody Map<String, String> body) 
        String userName = body.get("username");
        String password = body.get("password");
        return blogMockedData.getLoginToken(userName, password);
    

注意:如果我从 POSTMAN 访问 spring boot POST 服务,我会得到以下结果

但是,如果我从我的 android 客户端点击 POST 服务,它会给我带来错误

"status":415,"error":"不支持的媒体 类型","异常":"org.springframework.web.HttpMediaTypeNotSupportedException","消息":"内容 输入 'application/x-www-form-urlencoded;charset=UTF-8' 不是 支持","路径":"/api/login"

【问题讨论】:

【参考方案1】:

为了让 Spring 正确加载表单编码数据,您需要将端点参数定义为:

public LoginData postLogin(@RequestBody MultiValueMap<String, String> body)

public LoginData postLogin(@RequestParam Map<String, String> body)

【讨论】:

嗨@bluurr 我应用了你的答案,但仍然得到同样的错误 您好,您使用的是哪个版本的 Spring boot? hi @bluurr org.springframework.bootspring-boot-starter-parent1.5.9.RELEASE版本> 父> 当您使用 application/x-www-form-urlencoded 发送邮递员时,您是否仍然看到相同的错误? 嗨@bluurr,当我使用 application/x-www-form-urlencoded 发送邮递员时,它会显示 Content-Type →application/json;charset=UTF-8 Date →Mon, 2018 年 12 月 3 日 18:01:58 GMT Transfer-Encoding →chunked【参考方案2】:

使用 application/x-www-form-urlencoded 时,Spring 不会将其理解为 RequestBody。因此,请删除映射数据的参数列表中的 @RequestBody 注释,因为我可以在那里看到它。

请阅读此Spring doesn't understand application/x-www-form-urlencoded

【讨论】:

嗨 @dexter 已经应用了这个解决方案,但仍然出现同样的错误【参考方案3】:

在 Spring Boot 中处理一个 URL 编码的 POST (Content-Type: application/x-www-form-urlencoded)

这不起作用:

    public String processForm(@RequestBody RespFormDTO formDTO, 
HttpServletRequest request, HttpServletResponse response) 

这将起作用:

public String processForm(RespFormDTO formDTO,
       HttpServletRequest request, HttpServletResponse response) 

【讨论】:

【参考方案4】:

当标头 Content-Type:application/x-www-form-urlencoded 添加到我对 Springboot 应用程序的请求时,我遇到了类似的问题。请求正文为空。

我按照链接下方的说明进行操作,并且成功了。

简答,你需要禁用 HiddenHttpMethodFilter

下面的链接解释了原因以及如何禁用

https://newbedev.com/why-is-httpservletrequest-inputstream-empty

【讨论】:

以上是关于内容类型为 application/x-www-form-urlencoded 的 HTTP Post 请求在 Spring 引导服务中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

使用axios请求发送数据

bibli直播弹幕实时爬取

cocos2d-js网络教程篇cocos2d-js http网络请求

自动将 drupal 7 内容类型转换为代码。内容类型生成器代码?

为同一端点使用任何类型的内容类型

drupal7 为视图添加 过滤标准 内容类型