1. 程式人生 > >SpringMVC 完美解決PUT請求引數繫結問題(普通表單和檔案表單)

SpringMVC 完美解決PUT請求引數繫結問題(普通表單和檔案表單)

一 解決方案

修改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;
    }
}

總結

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

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