1. 程式人生 > >SpringMVC 完美解決PUT請求參數綁定問題(普通表單和文件表單)

SpringMVC 完美解決PUT請求參數綁定問題(普通表單和文件表單)

rand callback 方案 url web 觀察 alc gmv nag

一 解決方案

修改web.xml配置文件 將下面配置拷貝進去(在原有的web-app節點裏面配置 其它配置不變)

<!-- 處理PUT提交參數(只對基礎表單生效) -->
<filter>
    <filter-name>httpPutFormContentFilter</filter-name>
    <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>httpPutFormContentFilter</filter-name>
    <!-- 攔截所有 -->
    <url-pattern>/*
</url-pattern> </filter-mapping>

寫一個PostAndPutCommonsMultipartResolver繼承CommonsMultipartResolver 重寫isMultipart()

/**
 * 處理PUT提交參數(只對文件表單生效)
 * Created by Hy on 2018/9/30.
 */
public class PostAndPutCommonsMultipartResolver extends CommonsMultipartResolver {

    @Override
    public boolean isMultipart(HttpServletRequest request) {
        
if ("POST".equalsIgnoreCase(request.getMethod()) || "PUT".equalsIgnoreCase(request.getMethod())) { return FileUploadBase.isMultipartContent(new ServletRequestContext(request)); } return false; } }

修改spring-mvc.xml配置文件 將下面配置拷貝進去(在原有的beans節點裏面配置 其它配置不變)

<!-- 配置文件上傳實現類 -->
<bean id="multipartResolver" class
="com.hy.mm.manager.resolver.PostAndPutCommonsMultipartResolver"> <!-- 設定默認編碼 --> <property name="defaultEncoding" value="UTF-8" /> <!-- 文件上傳大小(單位B) 30M = 30 * 1024 * 1024 --> <property name="maxUploadSize" value="31457280" /> </bean>

寫一個Controller

/**
 * PUT請求
 * Created by Hy on 2018/9/30.
 */
@Controller
public class PutController {

    @PutMapping("/put/normal") @ResponseBody
    public String normalForm(String name, Integer age) {
        System.out.println("name = " + name);
        System.out.println("age = " + age);
        return "ok";
    }

    @PutMapping("/put/file") @ResponseBody
    public String fileForm(String name, MultipartFile file) throws Exception {
        System.out.println("name = " + name);
        if (null != file && !file.isEmpty()) {
            System.out.println("file = " + file.getSize());
            // 保存圖片
            String fileName = UUID.randomUUID().toString().replace("-", ""); //文件名
            String extension = FilenameUtils.getExtension(file.getOriginalFilename()); //擴展名 不包含(.)
            file.transferTo(new File("/Users/HUANGYI/Downloads/" + fileName + "." + extension));
            return "ok";
        }
        return "error";
    }

}

以上就能完美解決PUT請求參數綁定問題 趕時間的老哥可以忽略下文

二 解決思路

先bb一下起因

我最近再重構一個自己的項目 打算把接口交互修改成RESTful風格 淺顯的說一下RESTful風格 增刪改查對應POST DELETE PUT GET請求

環境

客戶端: Android 使用Retrofit發起請求

服務端: Java 使用SpringMVC處理請求

思路

客戶端使用PUT請求發送表單數據 不管是普通表單還是文件表單 服務端Controller層參數綁定均為null

但是 客戶端使用PUT請求發送非文件數據攜帶在Url上(類似GET請求) 服務端Controller層參數就能接收到

為了避免重復造輪子 我用Google解決了普通表單數據接收不到 也就是使用上面說的org.springframework.web.filter.HttpPutFormContentFilter就可以解決該問題

但是 文件表單數據還是接收不到 Google也不好用了 不知道是不是我姿勢不對

自己嘗試解決吧

先驗證文件表單數據到底寫入請求體沒有?

我用logging-interceptor和Charles觀察了好幾遍請求 確認了數據確實已經寫入了請求體

那麽 問題肯定就出現在SpringMVC的文件參數綁定上

仔細觀察org.springframework.web.multipart.commons.CommonsMultipartResolver

技術分享圖片

其中 isMultipart()是一個比較重要的方法 用來判斷請求是否包含多部分內容 也就是判斷是否是文件表單 深入觀察一下該方法的實現

技術分享圖片

真相大白 該方法默認POST請求才可能包含多部分內容

使用上面說的PostAndPutCommonsMultipartResolver就可以解決該問題

Android客戶端核心代碼

/**
 * ...
 * Created by Hy on 2018/9/30.
 */
public interface PutApi {

    @PUT("/put/normal") @FormUrlEncoded
    Call<ResponseBody> normal(@Field("name") String name, @Field("age") Integer age);

    @PUT("/put/file") @Multipart
    Call<ResponseBody> file(@Part("name") String name, @Part MultipartBody.Part file);
}
@Override
public void onClick(View view) {
    switch (view.getId()) {
        case R.id.normal:
            Call<ResponseBody> normalCall = mApi.normal("黃祎", 18);
            normalCall.enqueue(new Callback<ResponseBody>() {
                @Override
                public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                    try {
                        Log.i("HUANG", "code = " + response.code());
                        if (null != response.body())
                            Log.i("HUANG", "body = " + response.body().string());

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onFailure(Call<ResponseBody> call, Throwable t) {
                    Log.i("HUANG", "t = " + t.getMessage());
                }
            });

            break;

        case R.id.file:
            RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), copy());
            MultipartBody.Part body = MultipartBody.Part.createFormData("file", "a.mp4", fileBody);
            Call<ResponseBody> fileCall = mApi.file("黃祎", body);
            fileCall.enqueue(new Callback<ResponseBody>() {
                @Override
                public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                    try {
                        Log.i("HUANG", "code = " + response.code());
                        if (null != response.body())
                            Log.i("HUANG", "body = " + response.body().string());

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onFailure(Call<ResponseBody> call, Throwable t) {
                    Log.i("HUANG", "t = " + t.getMessage());
                }
            });
            break;
    }
}

總結

雖然只是寥寥幾句 但是我走完這幾步也花了一下午時間 哈哈哈 技術有限技術有限

希望能幫助到你 如果你的問題得到解決 請給個推薦點個贊 這樣能幫助到更多人 畢竟搜索不到解決方案的時候太痛苦了

SpringMVC 完美解決PUT請求參數綁定問題(普通表單和文件表單)