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; } }
總結
雖然只是寥寥幾句 但是我走完這幾步也花了一下午時間 哈哈哈 技術有限技術有限
希望能幫助到你 如果你的問題得到解決 請給個推薦點個贊 這樣能幫助到更多人 畢竟搜尋不到解決方案的時候太痛苦了