SpringMVC 參數映射與文件上傳

来源:https://www.cnblogs.com/yangyuanhu/archive/2020/02/11/12297329.html
-Advertisement-
Play Games

handler參數映射: 接下來就是Spring的各個處理細節了,無論框架如何瘋轉其實我們處理請求的流程是不變的,設計到的操作也是固定的,舉個例子,當我們要實現一個登陸功能時: 創建一個用於處理登錄請求的Servlet 實現doget等其他http方法(一些情況可能根據業務需要限制請求方法) 從re ...


handler參數映射:

接下來就是Spring的各個處理細節了,無論框架如何瘋轉其實我們處理請求的流程是不變的,設計到的操作也是固定的,舉個例子,當我們要實現一個登陸功能時:

  • 創建一個用於處理登錄請求的Servlet

  • 實現doget等其他http方法(一些情況可能根據業務需要限制請求方法)

  • 從request對象中取出數據

  • 處理編碼

  • 驗證參數是否符合要求

  • 對參數數據類型進行轉換(需要時)

  • 開始業務邏輯處理(登錄)

  • 可能需要操作session來完成

  • 組織響應給數據,可能是html可能是json,

  • 異常處理
  • Header與cookie的處理

整個SpringMVC其實就是幫我們對上面的操作進行封裝,當然了SpringMVC也提供了更多的功能,如國際化..

Handler方法特殊參數

在handler方法中我們可以添加一下參數,用於獲取一些特殊的對象:

  • HttpServletRequest,HttpServletResponse
  • HttpSession
  • Model
  • ModelMap

​ model 是框架幫我們創建好的的Model對象,若使用該參數作為返回的model,則需要修改方法返回值為String用於指定視圖名稱;

案例:

  @RequestMapping("/courseList2.action")
    public String courseList(Model model) {
        model.addAttribute("courses", courseService.selectCourseList());
        return "courses.jsp";
    }
    @RequestMapping("/courseList3.action")
    public String courseList(ModelMap model, HttpSession session,HttpServletRequest request,HttpServletResponse response) {
        System.out.println(request);
        System.out.println(response);
        System.out.println(session.getId());
        model.addAttribute("courses", courseService.selectCourseList());
        return "courses.jsp";
    }

請求參數映射:

在使用Servlet開發的過程中我們會頻繁的調用request.getAttribute來獲取請求參數,參數較少時還沒什麼,一旦參數較多的時候就會產生大量的冗餘代碼,SpringMVC提供了多種以簡化獲取參數的過程的方法

映射到handler方法的參數

在handler方法中添加與前臺參數名稱和類型匹配的參數,框架會自動解析參數傳入handler方法中;

案例:

假設我們要修改課程信息的功能,首先要獲取原始信息

@RequestMapping("/edit.action")
public String edit(Model model,Integer id) {
    Course course = courseService.selectCourseByID(id);
    model.addAttribute("course",course);
    return "edit.jsp";
}

支持的參數類型:

整形:Integer、int 
字元串:String 
單精度:Float、float 
雙精度:Double、double 
布爾型:Boolean、boolean

@RequestParam

當前後臺參數名稱不匹配時可以@RequestParam註解進行自定義映射;

註解參數:

  • value,name 兩者都代表前臺參數名稱
  • requerid 該參數是否必須
  • defaultValue 參數預設值

案例:

@RequestMapping("/edit.action")
public String edit(Model model,@RequestParam("id") Integer iid) {
    Course course = courseService.selectCourseByID(iid);
    model.addAttribute("course",course);
    return "edit.jsp";
}

註意:參數類型可以是基礎類型也可以是包裝類型,建議使用包裝類型,這樣可以保證為獲取到參數時不會因為null無法轉換為基礎類型而導致的異常;

edit.jsp:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head></head>
<form action="updateCourse.action" method="post">
    <input name="id" value="${course.id}" hidden="hidden"/>
    <input name="name" value="${course.name}"/>
    <input name="teachName" value="${course.teachName}"/>
  <%--    <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/>--%>
    <input name="score" type="number" value="${course.score}"/>
    <input name="hours" type="number" value="${course.hours}"/>
    <input type="submit">
</form>
<body>
</body>
</html>

映射到實體類

當參數個數非常多時上面的方法就顯得麻煩了,SpringMVC支持將參數映射到一個實體類;

在handler方法中添加任意類型實體類作為參數; 同樣的只有參數名稱和實體屬性一致時才能映射成功;

我們繼續完善修改功能,現在要獲取修改後的內容了:

@RequestMapping("/updateCourse.action")
public String update(Course course) {
    courseService.updateCourse(course);
    return "/courseList.action";
}

亂碼過濾器

上面的例子中出現了中文亂碼問題,請求方法為post, 只需要在request中設置編碼方式即可,但是此時參數已經被框架解析了,我們在handler中通過request設置以及不生效了,所以我們需要在請求到達SpringMVC之前就進行處理,這就用到了以前學過的過濾器了;

好消息是SpringMVC以及提供了過濾器,我們只需要配置到web.xml中即可

<!--    編碼過濾器-->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <!--       是否對響應設置編碼 -->
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
<!--    編碼過濾器 END-->

需要註意的是,該過濾器只對post生效,如果是get亂碼則還是需要修改tomcat的server.xml或是通過代碼從ISO-8859-重新編碼為UTF-8

String username = request.getParameter("username"); 
username = new String(username.getBytes("iso8859-1"), "utf-8"); //重新編碼

參數類型轉換

我們將edit.jsp中的開課標簽取消註釋,然後測試會發現系統給出了400異常,查看控制台可以看到以下信息:

意思是框架無法將String類型的請求參數轉換為需要的Date類型,這就需要,這是因為日期格式多種多樣,每個地區不同,所以這需要我們自己來實現轉換;

編寫轉換器

實現convert介面即可作為轉換器,該介面的兩個兩個泛型表示輸入源類型和輸出目標類型;

public class StringToDateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String s) {
        SimpleDateFormat sm = new SimpleDateFormat("yyyy-MM-dd");
        try {
            return sm.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}

mvc配置文件

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.kkb.converter.StringToDateConverter"/>
        </set>
    </property>
</bean>
<mvc:annotation-driven conversion-service="conversionService"/>

使用註解註冊轉換器

使用@DateTimeFormat可以實現上面xml相同的配置:

public class Course {
  private Integer id;
  private String name;
  private String teachName;

  @DateTimeFormat(pattern = "yyyy-MM-dd")
  private Date startTime;
}

註意:註解作用在實體類對應的屬性上,且配置文件必須添加<mvc:annotation-driven>

包裝類型映射

當需要將參數映射到實體類的關聯對象中時,也稱為包裝類型;

例如:在課程對象中有一個用戶對象,表示這是某個用戶的課程;前臺需要同時傳遞課程對象的屬性,和用戶對象的屬性,後臺就需要要用一個包裝類型來接收,即一個包裝了用戶對象的課程對象; 再說的簡單點,即課程對象中包含一個用戶對象;

在前臺需要指出關聯對象的屬性名稱,如:用戶.name

實體:

public class Course {
    private Integer id;
    private String name;
    private String teachName;
    private Date startTime;
    private Integer score;
    private Integer hours;
    private User user;//新添加的User類屬性
  set/get....

handler:

@RequestMapping("/updateCourse.action")
public String update(Course course) {
    courseService.updateCourse(course);
    return "/courseList.action";
}

jsp:

<form action="updateCourse.action" method="post">
    <input name="id" value="${course.id}" hidden="hidden"/>
    <input name="name" value="${course.name}"/>
    <input name="teachName" value="${course.teachName}"/>
    <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/>
    <input name="score" type="number" value="${course.score}"/>
    <input name="hours" type="number" value="${course.hours}"/>
    <input name="user.username"/>  <!-- 新添加的參數-->
    <input type="submit">
</form>

數組參數映射

一些情況下,某一參數可能會有多個值,例如要進行批量刪除操作,要刪除的id會有多個,那就需要將參數映射到一個數組中;HttPServletRequest原本就支持獲取數組參數,SpringMVC僅是幫我們做了一個類型轉換;

1.修改頁面添加多選框:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
</head>
<body>
<form action="deleteCourses">
    <table border="1">
        <tr>
            <th>選擇</th>
            <th>名稱</th>
            <th>講師</th>
            <th>開課日期</th>
            <th>學分</th>
            <th>課時</th>
            <th>操作</th>
        </tr>
        <c:forEach items="${courses}" var="course">
            <tr>
                <td>
                    <input type="checkbox" name="ids" value="${course.id}">
                </td>
                <td>${course.name}</td>
                <td>${course.teachName}</td>
                <td>
                    <fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>
                </td>
                <td>${course.score}</td>
                <td>${course.hours}</td>
                <td>
                    <a href="editCourse?id=${course.id}">修改</a>
                </td>
            </tr>
        </c:forEach>
    </table>
    <input type="submit" value="批量刪除">
</form>
</body>
</html>

2.handler方法:

@RequestMapping("/deleteCourses")
public String deleteCourses(Integer[] ids){
    courseService.deleteCourses(ids);
    return "/getCourses";
}

3.service方法:

public void deleteCourses(Integer[] ids) {
    for (Integer id : ids){
        courseMapper.deleteByPrimaryKey(id);
    }
}

list映射

當請求參數包含多個對象的屬性數據,是需要使用list來接收,通常用在批量修改批量添加等;

list映射要求參數名稱為對象屬性[下標].屬性名稱,同時handler中使要用包裝類型來接收;

以下是實現一個批量修改課程信息的功能;

1.修改頁面中的td,使得每一個td都可以編輯:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
</head>
<body>
<form action="deleteCourses" id="fm">
    <table border="1">
        <tr>
            <th>選擇</th><th>名稱</th><th>講師</th><th>開課日期</th><th>學分</th><th>課時</th><th>操作</th>
        </tr>
        <c:forEach items="${courses}" var="course" varStatus="status">
            <tr>
                <td><input type="checkbox" name="ids" value="${course.id}"></td>
                <td><input value="${course.name}" name="courses[${status.index}].name"/></td>
                <td><input value="${course.teachName}" name="courses[${status.index}].teachName"/></td>
                <td><input value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>' name="courses[${status.index}].startTime"/></td>
                <td><input value="${course.score}" name="courses[${status.index}].score"/></td>
                <td><input value="${course.hours}" name="courses[${status.index}].hours"/></td>
                <input hidden="hidden" name="courses[${status.index}].id" value="${course.id}">
                <td><a href="editCourse?id=${course.id}">修改</a></td>
            </tr>
        </c:forEach>
    </table>
    <input type="submit" value="批量刪除">
    <input type="button" onclick='function updates() {
        document.getElementById("fm").action = "updateCourses"
        document.getElementById("fm").submit()
    }
    updates()' value="批量修改">
</form>
</body>
</html>

2.包裝類型:

public class RequestPack {
    //用於接收參數列表的list
    private List<Course> courses;

    public List<Course> getCourses() {
        return courses;
    }

    public void setCourses(List<Course> courses) {
        this.courses = courses;
    }

    public RequestPack(List<Course> courses) {
        this.courses = courses;
    }
}

3.handler方法:

@RequestMapping("/updateCourses")
public String updateCourses(RequestPack data){
    courseService.updateCourses(data.getCourses());
    return "/getCourses";
}

4.service方法:

public void updateCourses(List<Course> courses) {
    for (Course course:courses){
        courseMapper.updateByPrimaryKey(course);
    }
}

強調:list只能映射到包裝類型中,無法直接映射到handler參數上

錯誤案例:

@RequestMapping("/updateCourses")
public String updateCourses(RequestPack data, ArrayList<Course> courses){
    courseService.updateCourses(data.getCourses());
    return "/getCourses";
}

文件上傳

文件上傳是web項目中非常常見的需求,SpringMVC使用了apache開源的兩個庫用於處理文件上傳,所以在編寫代碼前我們需要先導入下麵兩個依賴包:

<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version>
</dependency>

假設需要實現一個上傳圖片的功能,需要現在資料庫中添加一個欄位用於存儲圖片的路徑,同時不要忘記修改pojo以及mapper文件,使之與資料庫欄位對應

1.頁面增加input 用於提交文件,並修改表單的enctype為multipart/form-data

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head></head>
<form action="updateCourse" method="post" enctype="multipart/form-data">
    <input name="id" value="${course.id}" hidden="hidden"/>
    名稱:<input name="name" value="${course.name}"/>
    <br/>
    講師:<input name="teachName" value="${course.teachName}"/>
    <br/>
    <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/>
    <br/>
    學分:<input name="score" type="number" value="${course.score}"/>
    <br/>
    課時:<input name="hours" type="number" value="${course.hours}"/>
    <br/>
    <c:if test="${course.pic != null}">
        <img src="${pageContext.servletContext.contextPath}${course.pic}" style="width: 100px;height: 100px">
    </c:if>
    圖片:<input name="picFile" type="file"/><br/> <!-- 新增input-->
    
    <br/>
    <input type="submit">

</form>
<body>
</body>
</html>

2.在mvc配置文件中添加multipart解析器,(頁面上傳文件都是以,multipart編碼方式)

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

3.handler方法中添加MultipartFile類型的參數

@RequestMapping("/updateCourse")
public String updateCourse(Course course, MultipartFile picFile) throws IOException {
    /***
     * 1.獲得文件,取出文件尾碼
     * 2.生成唯一標識,
     * 3.寫入文件到指定路徑
     * 4.存儲文件路徑到資料庫
     */
    System.out.println(picFile.getName());
    String suffix = picFile.getOriginalFilename().substring(picFile.getOriginalFilename().lastIndexOf("."));
    String fileName = UUID.randomUUID() + suffix;
    String basepath = getClass().getClassLoader().getResource(".").getPath();
    System.out.println(basepath);
    picFile.transferTo(new File(basepath+"../../images/"+fileName));
    course.setPic("/images/"+fileName);
    courseService.update(course);
    return  "/getCourses";
}

註意:實際開發中都是存儲到文件伺服器,不會放在項目里

4.靜態資源處

若web.xml中DispatcherServlet的URLmapping 為/ 則還需要在SpringMVC中添加靜態資源配置

<mvc:resources mapping="/images/**" location="/images/"/>
<!--當請求地址為/images/開頭時(無論後面有多少層目錄),作為靜態資源 到/images/下查找文件-->

若URLMapping為*.action 或類似其他的時則無需處理,因為Tomcat會直接查找webapp下的資源,不會交給DispatcherServlet

請求限制

一些情況下我們可能需要對請求進行限制,比如僅允許POST,GET等...

RequestMapping註解中提供了多個參數用於添加請求的限制條件

  • value 請求地址
  • path 請求地址
  • method 請求方法
  • headers 請求頭中必須包含指定欄位
  • params 必須包含某個請求參數
  • consumes 接受的數據媒體類型 (與請求中的contentType匹配才處理)
  • produce 返回的媒體類型 (與請求中的accept匹配才處理)

案例:

@RequestMapping(value = "/editCourse",method = RequestMethod.POST,headers = {"id"},params = {"name"},consumes = {"text/plain"})

為了簡化書寫,MVC還提供了集合路徑和方法限制的註解,包括常見的請求方法:

PostMapping
GetMapping
DeleteMapping
PutMapping

例:
@PostMapping("/editCourse")

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最初使用回調函數 ​ 由於最初j s官方沒有明確的規範,各種第三方庫中封裝的非同步函數中傳的回調函數中的參數沒有明確的規範, 沒有明確各個參數的意義, 不便於使用。 ​ 但是node中有明確的規範 ​ node中的的回調模式: 1. 所有回調函數必須有兩個參數,第一個參數表示錯誤,第二個參數表示結果 ...
  • 前言 準確的說eval處理過的代碼應該叫做壓縮代碼,不過效果上算是加密過了一樣!很多小伙伴不想直接讓別人看到自己的js代碼往往就會採取這樣的處理措施。不過,其實這樣的方法只能防禦那些小白。對於真正的大佬來說,破解就是秒秒鐘的事!真的就是秒秒鐘!話不多說,我們直接進入正題! 演示代碼: 操作步驟 1. ...
  • 前言 微信紅包業務,發紅包之後如果24小時之內沒有被領取完就自動過期失效。 架構設計 業務流程 老闆發紅包,此時緩存初始化紅包個數,紅包金額(單位分),並非同步入庫。 紅包數據入延遲隊列,唯一標識+失效時間 紅包數據出延遲隊列,根據唯一標識清空紅包緩存數據、非同步更新資料庫、非同步退回紅包金額 代碼案例 ...
  • 前言 截至2020年,Java仍然是構建Web應用程式的最流行的編程語言之一,儘管它必須面對來自Go,Python和TypeScript等新型語言的激烈競爭。 在Java世界內部,Spring框架已成為微服務開發的事實上的標準,通過諸如Spring Boot和Spring Data之類的庫,該框架易 ...
  • 本文將介紹一個重要的 "數據結構" —棧,和之前講到的 "鏈表" 、 "數組" 一樣也是一種數據呈 線性排列 的數據結構,不過在這種結構中,我們只能訪問最新添加的數據。棧就像是一摞書,拿到新書時我們會把它放在書堆的最上面,取書時也只能從最上面的新書開始取。 棧 如上就是棧的概念圖,現在存儲在棧中的只 ...
  • 作為一個Java從業者,面試的時候肯定會被問到過HashMap ...
  • 經過這次在公司實習中獲取到的經歷,我發現確實有時候書本上的知識發揮的作用微乎其微,好像是被問題打了太極拳一樣,你明明想去攻剋這個地方,他卻給你報了其他地方的錯誤。 平常的一些小項目根本就不能匹配到企業級別的開發經驗尤其我也不是ACM得獎的大佬,更是覺得尤為不適應,還好經過4個月左右的實習時間,我漸漸 ...
  • 結構體模板 1 struct STU 2 { 3 string name; //用string可以代替char 4 string num; 5 int s; 6 }; sort是用快速排序實現的,屬於不穩定排序,stable_sort是用歸併排序實現的,因此是穩定的。從此以後,為了保險起見我打算使用 ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...