之前已經創建了商店的數據表tb_shop和對應的實體類Shop:https://blog.csdn.net/theVicTory/article/details/105739461
那麼如何將商店信息傳遞到前端展示頁面,並且前端的用戶操作又作用於數據呢?
數據在Spring中的流動如下
DAO層
首先實現Shop對象的DAO層,DAO(Data Access Object)主要用來封裝對數據庫的訪問操作。由於使用Mybatis,所以在ShopDao
類中只需要定義DAO接口,具體的數據庫操作在mapper文件ShopCategory.xml中實現,如下所示定義了Shop類的新增、更改、查詢操作的接口。在定義接口時將Shop對象作爲參數傳入,其屬性名可以在mapper文件中通過#{}的方式獲取到。如果需要傳入多個參數,要使用註解@Param("pageSize")
,在使用#{pageSize}
獲得參數。
public interface ShopDao {
//新增店鋪
int insertShop(Shop shop);
//更新店鋪
int updateShop(Shop shop);
//根據Id查詢指定店鋪
Shop queryShopById(int shopId);
//根據條件查詢店鋪列表
List<Shop> queryShop(@Param("conditionShop") Shop conditionShop,
@Param("offset") int offset, @Param("pageSize") int pageSize);
}
mapper文件定義如下,首先在<mapper>
標籤的namespace
屬性中指明mapper文件所對應的Dao接口類爲ShopDao。
接着定義數據庫返回結果和Shop對象之間的映射<resultMap>
,它定義了數據庫的字段如何映射爲Shop對象。主鍵字段用<id>
標籤,普通字段用<result>
,property
屬性代表類屬性,column
代表數據庫中的字段。值得注意的是,如果數據庫字段是一個連接到其他對象的外鍵,例如通過外鍵area_id連接到另一張表,那麼可以通過<association>
將類屬性area映射爲一個Area對象,並從外鍵連接的表中取出數據填充對象屬性areaId和areaName。
接着實現在接口中定義的insertShop、updateShop、queryShopById、queryShop方法,parameterType
爲傳入參數的類型,resultMap
爲返回結果的類型,如果返回多組結果Mybatis會自動轉爲List
。在queryShop中根據傳入的conditionShop對象來匹配符合條件的結果,通過<where>
標籤將Sql條件語句包裹起來,在其中首先通過<if>
判斷conditionShop中的條件是否爲空,若不爲空,則用AND將查詢語句拼接起來。最後使用ORDER BY
進行排序,LIMIT
返回指定偏移和個數的數據。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tory.shop.dao.ShopDao">
<resultMap id="shopMap" type="com.tory.shop.entity.Shop">
<id property="shopId" column="shop_id"/>
<result property="shopName" column="shop_name"/>
<result property="shopAddr" column="shop_addr"/>
<result property="shopDescribe" column="shop_describe"/>
<result property="shopPhone" column="shop_phone"/>
<result property="shopImg" column="shop_img"/>
<result property="enableStatus" column="enable_status"/>
<result property="priority" column="priority"/>
<result property="createTime" column="create_time"/>
<result property="lastEditTime" column="last_edit_time"/>
<result property="adviceMessage" column="advice_message"/>
<association property="area" column="area_id" javaType="com.tory.shop.entity.Area">
<id property="areaId" column="area_id"/>
<result property="areaName" column="area_name"/>
</association>
<association property="shopCategory" column="shop_category" javaType="com.tory.shop.entity.ShopCategory">
<id property="categoryId" column="category_id"/>
<result property="categoryName" column="category_name"/>
</association>
<association property="owner" column="owner_id" javaType="com.tory.shop.entity.PersonInfo">
<id property="userId" column="user_id"/>
<result property="name" column="name"/>
</association>
</resultMap>
<insert id="insertShop" useGeneratedKeys="true" keyColumn="shop_id" keyProperty="shopId">
INSERT INTO tb_shop
(owner_id, area_id, shop_category,
shop_name, shop_describe, shop_addr, shop_phone, shop_img,
create_time, last_edit_time, enable_status, advice_message)
VALUES (#{owner.userId}, #{area.areaId}, #{shopCategory.categoryId},
#{shopName}, #{shopDescribe}, #{shopAddr}, #{shopPhone}, #{shopImg},
#{createTime}, #{lastEditTime}, #{enableStatus}, #{adviceMessage})
</insert>
<update id="updateShop" parameterType="com.tory.shop.entity.Shop">
UPDATE tb_shop
<set>
<if test="shopName != null">shop_name=#{shopName},</if>
<if test="shopDescribe != null">shop_describe=#{shopDescribe},</if>
<if test="shopAddr != null">shop_addr=#{shopAddr},</if>
<if test="shopPhone != null">shop_phone=#{shopPhone},</if>
<if test="shopImg != null">shop_img=#{shopImg},</if>
<if test="priority != null">priority=#{priority},</if>
<if test="lastEditTime != null">last_edit_time=#{lastEditTime},</if>
<if test="enableStatus != null">enable_status=#{enableStatus},</if>
<if test="adviceMessage != null">advice_message=#{adviceMessage},</if>
<if test="area != null">area_id=#{area.areaId},</if>
<if test="shopCategory != null">shop_category=#{shopCategory.categoryId},</if>
</set>
WHERE shop_id=#{shopId}
</update>
<select id="queryShopById" resultMap="shopMap" parameterType="int">
SELECT s.shop_id,s.shop_name,s.shop_describe,s.advice_message,s.shop_addr,s.shop_phone,
s.shop_img,s.priority,s.create_time,s.last_edit_time,s.enable_status,
a.area_id,a.area_name,c.category_id,c.category_name
FROM tb_shop s,tb_area a,tb_shop_category c
WHERE shop_id = #{_parameter}
AND s.area_id = a.area_id
AND s.shop_category = c.category_id
</select>
<select id="queryShop" resultMap="shopMap">
SELECT
s.shop_id,s.shop_name,s.shop_describe,s.advice_message,s.
shop_addr,s.shop_phone,s.shop_img,s.priority,s.create_time,s.last_edit_time,s.enable_status,
a.area_id,a.area_name,c.category_id,c.category_name
FROM
tb_shop s,tb_area a,tb_shop_category c
<where>
<if test="conditionShop.shopCategory!=null and conditionShop.shopCategory.categoryId!=null">
AND s.shop_category=#{conditionShop.shopCategory.categoryId}
</if>
<if test="conditionShop.area!=null and conditionShop.area.areaId!=null">
AND s.area_id=#{conditionshop.area.areaId}
</if>
<if test="conditionShop.shopName!=null">
AND s.shop_name like '% ${conditionShop.shopName} %'
</if>
<if test="conditionShop.enableStatus !=null">
AND s.enable_status=#{conditionShop.enableStatus}
</if>
<if test="conditionShop.owner!=null and conditionShop.owner.userId !=null">
AND s.owner_id=#{conditionShop.owner.userId}
</if>
AND s.area_id=a.area_id AND s.shop_category=c.category_id
</where>
ORDER BY s.priority DESC
LIMIT #{offset},#{pageSize}
</select>
</mapper>
DTO層
Data Transfer Object數據傳輸對象,該層負責屏蔽後端的實體層,用於將DAO取到的數據進行再次處理加工後返回給Service層。在實際的業務場景下,後端存儲的數據遠比用戶需要的數據要龐大和複雜,因此需要進行處理、組合之後再返回。
如下所示爲Shop的DTO類,在其中保存商鋪操作結果狀態及信息,以及返回的Shop對象。
public class ShopExecution {
private int state; //結果狀態
private String stateInfo; //結果信息
private Shop shop; //操作的商鋪
private int shopCount; //返回商鋪的數量
private List<Shop> shopList; //返回的商鋪列表
//執行失敗時的構造器,只有狀態枚舉作爲參數
public ShopExecution(ShopStateEnum stateEnum){
this.state=stateEnum.getState();
this.stateInfo=stateEnum.getStateInfo();
}
//執行增刪改成功時的構造器,傳入狀態枚舉和Shop對象
public ShopExecution(ShopStateEnum stateEnum, Shop shop) {
this.state = stateEnum.getState();
this.stateInfo = stateEnum.getStateInfo();
this.shop = shop;
}
//執行查詢成功時的構造器,返回了
public ShopExecution(ShopStateEnum stateEnum, List<Shop> shopList) {
this.state = stateEnum.getState();
this.stateInfo = stateEnum.getStateInfo();
this.shopList = shopList;
}
......getter/setter
其中用枚舉類型來儲存結果狀態,如下所示爲枚舉類ShopStateEnum的定義
public enum ShopStateEnum {
CHECK(0, "審覈中"), OFFLINE(-1, "非法商鋪"), SUCCESS(1, "操作成功"), PASS(2, "通過認證"), INNER_ERROR(-1001, "操作失敗"), NULL_SHOPID(-1002, "ShopId爲空"), NULL_SHOP_INFO(-1003, "傳入了空的信息");
private int state;
private String stateInfo;
//枚舉類的構造函數
ShopStateEnum(int state, String stateInfo) {
this.state=state;
this.stateInfo=stateInfo;
}
//根據狀態值返回狀態枚舉對象
public static ShopStateEnum stateOf(int index){
for (ShopStateEnum state : values()){
if (state.getState()==index)
return state;
}
return null;
}
......getter/setter
Service層
在Service層,首先在接口中定義了添加、更新、查詢商店的四個方法
public interface ShopService {
//添加商店
ShopExecution addShop(Shop shop, CommonsMultipartFile shopImg);
//更新商店
ShopExecution updateShop(Shop shop, CommonsMultipartFile shopImg);
//根據id查詢商店
Shop getShopById(int shopId);
//根據條件查詢商店列表
ShopExecution getShopList(Shop conditionShop,int pageIndex,int pageSize);
}
接着實現service層。首先實現店鋪添加的操作addShop()
,該方法傳入shop
對象和shopImg
圖片,首先進行判空操作,若shop對象爲空,則返回插入失敗的ShopExecution
對象。否則爲shop增加一些初始屬性後調用Dao層將店鋪信息存入數據庫。然後調用saveImg()
方法將圖片保存到服務器並返回圖片地址,最後將圖片地址信息更新到shop對象的數據庫中。saveImg()中調用ImageUtil類進行的圖片操作,實現記錄在:https://blog.csdn.net/theVicTory/article/details/106007111
updateShop()
用於更新店鋪,若傳入的shopImg不爲空,則更新圖片。接着調用shopDao
的updateShop()方法更新shop對象的信息
在查詢商鋪列表的getShopList()
方法中,首先根據傳入的pageIndex
和pageSize
計算出所需數據在數據庫中的偏移量offset
,然後傳入shopDao.queryShop()
中得到Shop列表。若shopList不爲空,則將其傳入構造ShopExecution
對象,否則構造錯誤的ShopExecution,最後將其返回。
@Service
public class ShopServiceImpl implements ShopService {
@Autowired
private ShopDao shopDao;
@Override
public ShopExecution addShop(Shop shop, CommonsMultipartFile shopImg) {
//如果傳入shop對象爲空,則返回失敗的ShopExecution
if (shop==null)
return new ShopExecution(ShopStateEnum.NULL_SHOP_INFO);
//爲shop設置一些初始值屬性
shop.setEnableStatus(0);
shop.setCreateTime(new Date());
shop.setLastEditTime(new Date());
//存入店鋪信息
int affectedRows=shopDao.insertShop(shop);
if (affectedRows<=0)
throw new RuntimeException("插入數據庫失敗!");
else {
if (shopImg!=null){
saveShopImg(shop,shopImg);
//更新店鋪的圖片地址
affectedRows= shopDao.updateShop(shop);
if (affectedRows<=0)
throw new RuntimeException("更新數據庫失敗!");
}
}
return new ShopExecution(ShopStateEnum.CHECK,shop);
}
@Override
public ShopExecution updateShop(Shop shop, CommonsMultipartFile shopImg) {
if (shop ==null)
return new ShopExecution(ShopStateEnum.NULL_SHOP_INFO);
//更新圖片
if (shopImg!=null){
String tempShopImg=shopDao.queryShopById(shop.getShopId()).getShopImg();
if (tempShopImg !=null) //如果原來Shop的圖片不爲空,則刪除
FileUtil.deleteFile(tempShopImg);
saveShopImg(shop,shopImg);
}
//更新Shop信息
shop.setLastEditTime(new Date());
int influenceNum=shopDao.updateShop(shop);
if (influenceNum<=0)
return new ShopExecution(ShopStateEnum.INNER_ERROR);
else
return new ShopExecution(ShopStateEnum.SUCCESS,shop);
}
@Override
public Shop getShopById(int shopId) {
return shopDao.queryShopById(shopId);
}
@Override
public ShopExecution getShopList(Shop conditionShop, int pageIndex, int pageSize) {
int offset=pageIndex>1?(pageIndex-1)*pageSize:0; //根據頁碼計算在數據庫中對應的偏移量
List<Shop> shopList=shopDao.queryShop(conditionShop,offset,pageSize);
ShopExecution shopExecution;
if (shopList!=null){
shopExecution=new ShopExecution(ShopStateEnum.SUCCESS,shopList);
shopExecution.setShopCount(shopList.size());
}else {
shopExecution=new ShopExecution(ShopStateEnum.INNER_ERROR);
}
return shopExecution;
}
public void saveShopImg(Shop shop, CommonsMultipartFile shopImg) {
String imgPath= FileUtil.getShopImagePath(shop.getShopId());
String shopImgAddr= ImageUtil.generateThumbnail(shopImg,imgPath);
shop.setShopImg(shopImgAddr);
}
}
Controller層
首先定義ShopRouteController
類用於返回關於頁面的請求,這裏主要有三個頁面,比如getRegisterView()
方法映射爲/ShopView下的/edit的GET請求,返回店鋪的註冊頁面。
@Controller
@RequestMapping("ShopView")
public class ShopRouteController {
//店鋪註冊頁面
@RequestMapping("edit")
public String getRegisterView() {
return "shop/shop-edit";
}
//返回店鋪列表頁面
@RequestMapping("list")
public String getShopListView() {
return "shop/shop-list";
}
//店鋪管理頁面
@RequestMapping("manage")
public String getManageView() {
return "shop/shop-manage";
}
}
定義ShopManagementController
類用於處理店鋪相關數據操作的請求。
getInitInfo()
用於返回註冊頁面所需要的初始化信息–店鋪類別列表categoryList和區域列表areaList。這裏使用Map<String, Object>
來儲存返回的數據,之後由於添加了@ResponseBody
註解,返回的內容會被轉換爲JSON格式。
registerShop()
方法映射爲/shop下的/register的POST請求,用於接收店鋪的信息並完成註冊。首先調用checkCode()方法比對驗證碼是否輸入正確。然後通過request.getParameter()
取出shopStr,前端以JSON字符串的形式將Shop對象放在了字符串shopStr中,通過JSON的ObjectMapper.readValue()
方法將其轉換爲Shop對象。接着提取POST請求中的圖片文件,首先將request轉換爲MultipartHttpServletRequest
類型,然後調用getFile()
獲取圖片對象shopImg。最後調用shopService儲存shop、shopImg對象,根據Service層返回的ShopExecution判斷儲存操作是否成功,若成功則設置modelMap
中的success字段爲true,否則設爲false並將錯誤信息保存在errMsg
字段中。最後將儲存結果信息的modelMap返回給客戶端。
initUpdateShop()
方法用於返回店鋪信息,當用戶修改店鋪時,需要將店鋪原來的信息渲染到頁面,在初始化頁面時請求/shop/updateInit到達這個方法,通過request.getParameter()獲取到請求中的shopId,調用service層根據shopId查詢到shop對象的信息返回給客戶端。
updateShop()
方法用於更新店鋪信息,和registerShop()類似,首先比對驗證碼,然後獲取到前端傳回的shop和shopImg對象,最後調用Service層進行保存操作,最後返回操作結果。
getShopList()
方法用於根據條件查詢商店並返回結果列表,由於這裏沒有登錄,所以先手動將用戶信息存入session。然後從session中取出用戶user信息,以此作爲條件設置到conditionShop
中,然後將其傳給service層的getShopList獲得查詢結果並返回給客戶端。
最後一個getCurrentShop()
方法用於設置/獲取當前操作店鋪的shopId,如果請求中帶有shopId,則將其保存到session中,否則從session中查找是否有shopId,若也沒有,則未選擇店鋪,頁面跳轉到店鋪列表頁面。
package com.tory.shop.controller.shop;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tory.shop.dto.ShopExecution;
import com.tory.shop.entity.Area;
import com.tory.shop.entity.PersonInfo;
import com.tory.shop.entity.Shop;
import com.tory.shop.entity.ShopCategory;
import com.tory.shop.enums.ShopStateEnum;
import com.tory.shop.service.AreaService;
import com.tory.shop.service.ShopCategoryService;
import com.tory.shop.service.ShopService;
import com.tory.shop.util.CodeUtil;
import com.tory.shop.util.RequestUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("shop")
public class ShopManagementController {
@Autowired
ShopService shopService;
@Autowired
ShopCategoryService shopCategoryService;
@Autowired
AreaService areaService;
//返回註冊頁面的初始化信息
@RequestMapping("init")
@ResponseBody
public Map<String, Object> getInitInfo() {
Map<String, Object> modelMap = new HashMap<>();
try {
List<Area> areaList = areaService.getAreaList();
List<ShopCategory> categoryList = shopCategoryService.getCategoryList(new ShopCategory());
modelMap.put("categoryList", categoryList);
modelMap.put("areaList", areaList);
modelMap.put("success", true);
} catch (Exception e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
}
return modelMap;
}
//接收註冊店鋪的POST請求
@RequestMapping(value = "register", method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> registerShop(HttpServletRequest request) {
Map<String, Object> modelMap = new HashMap<>();
//校驗驗證碼是否正確
if (!CodeUtil.checkCode(request)) {
modelMap.put("success", false);
modelMap.put("errMsg", "驗證碼錯誤");
return modelMap;
}
//將前端傳回的json字符串的shop數據轉爲shop對象
String shopStr = request.getParameter("shopStr");
ObjectMapper jsonMapper = new ObjectMapper();
Shop shop;
try {
shop = jsonMapper.readValue(shopStr, Shop.class);
} catch (JsonProcessingException e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
return modelMap;
}
//接收前端傳回的圖片文件
MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) request;
CommonsMultipartFile shopImg = (CommonsMultipartFile) mRequest.getFile("shopImg");
//存儲shop和shopImg對象
if (shop != null && shopImg != null) {
//從session中獲取用戶信息
PersonInfo owner = (PersonInfo) request.getSession().getAttribute("user");
shop.setOwner(owner);
ShopExecution shopExecution = shopService.addShop(shop, shopImg);
//如果存儲成功將"success"設爲true,否則設爲false並返回錯誤信息
if (shopExecution.getState() == ShopStateEnum.CHECK.getState()) {
modelMap.put("success", true);
//將用戶對應的商店列表存入Session
List<Shop> shopList = (List<Shop>) request.getSession().getAttribute("shopList");
if (shopList == null)
shopList = new ArrayList<>();
shopList.add(shopExecution.getShop());
request.getSession().setAttribute("shopList", shopList);
} else {
modelMap.put("success", false);
modelMap.put("errMsg", shopExecution.getStateInfo());
}
} else {
modelMap.put("success", false);
modelMap.put("errMsg", "店鋪信息不能爲空");
}
return modelMap;
}
//店鋪的初始化信息
@RequestMapping(value = "updateInit", method = RequestMethod.GET)
@ResponseBody
public Map<String, Object> initUpdateShop(HttpServletRequest request) {
Map<String, Object> modelMap = new HashMap<>();
int shopId = Integer.parseInt(request.getParameter("shopId"));
if (shopId > 0) {
//根據shopId查詢shop信息
Shop shop = shopService.getShopById(shopId);
modelMap.put("shop", shop);
List areaList = areaService.getAreaList();
modelMap.put("areaList", areaList);
modelMap.put("success", true);
} else {
modelMap.put("success", false);
modelMap.put("errMsg", "shopId is null");
}
return modelMap;
}
//修改店鋪信息
@RequestMapping(value = "update", method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> updateShop(HttpServletRequest request) {
Map<String, Object> modelMap = new HashMap<>();
//校驗驗證碼是否正確
if (!CodeUtil.checkCode(request)) {
modelMap.put("success", false);
modelMap.put("errMsg", "驗證碼錯誤");
return modelMap;
}
//將前端傳回的json字符串的shop數據轉爲shop對象
String shopStr = request.getParameter("shopStr");
ObjectMapper jsonMapper = new ObjectMapper();
Shop shop;
try {
shop = jsonMapper.readValue(shopStr, Shop.class);
} catch (JsonProcessingException e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
return modelMap;
}
//接收前端傳回的圖片文件
MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) request;
CommonsMultipartFile shopImg = (CommonsMultipartFile) mRequest.getFile("shopImg");
//更新shop和shopImg對象
if (shop.getShopId() != null) {
ShopExecution shopExecution = shopService.updateShop(shop, shopImg);
//如果存儲成功將"success"設爲true,否則設爲false並返回錯誤信息
if (shopExecution.getState() == ShopStateEnum.SUCCESS.getState())
modelMap.put("success", true);
else {
modelMap.put("success", false);
modelMap.put("errMsg", shopExecution.getStateInfo());
}
} else {
modelMap.put("success", false);
modelMap.put("errMsg", "未找到店鋪Id");
}
return modelMap;
}
//按條件查詢店鋪列表
@RequestMapping(value = "getList", method = RequestMethod.GET)
@ResponseBody
public Map<String, Object> getShopList(HttpServletRequest request) {
Map<String, Object> modelMap = new HashMap<>();
//手動設置用戶session
PersonInfo user = new PersonInfo();
user.setUserId(1);
request.getSession().setAttribute("user", user);
//從session中獲取用戶信息
user = (PersonInfo) request.getSession().getAttribute("user");
Shop conditionShop = new Shop();
conditionShop.setOwner(user);
//調用Service層按conditionShop條件進行查詢
ShopExecution shopExecution = shopService.getShopList(conditionShop, 1, 10);
if (shopExecution.getState() == ShopStateEnum.SUCCESS.getState()) {
modelMap.put("shopList", shopExecution.getShopList());
modelMap.put("user", user);
modelMap.put("success", true);
} else {
modelMap.put("success", false);
modelMap.put("errMsg", shopExecution.getStateInfo());
}
return modelMap;
}
//獲取當前shop
@RequestMapping("getCurrentShop")
@ResponseBody
public Map<String, Object> getCurrentShop(HttpServletRequest request) {
Map<String, Object> modelMap = new HashMap<>();
int shopId = RequestUtil.getInt(request, "shopId");
if (shopId < 0) { //如果請求中沒有shopId參數,則從session中查找
Shop currentShop = (Shop) request.getSession().getAttribute("currentShop");
if (currentShop == null) { //如果session中也沒有,則重定向到商店列表頁面
modelMap.put("redirect", true);
modelMap.put("url", "/ShopDemo/ShopView/list");
} else {
modelMap.put("redirect", false);
modelMap.put("shopId", currentShop.getShopId());
}
} else { //若請求中帶有shopId,將其保存到session中
Shop currentShop = new Shop();
currentShop.setShopId(shopId);
request.getSession().setAttribute("currentShop", currentShop);
modelMap.put("redirect", false);
}
return modelMap;
}
}
值得注意的是這裏使用multipartResolver
來接收混合字符串和圖片文件的FormData
類型POST請求,因此需要在spring-mvc.xml配置文件中註冊該bean,否則使用request.getParameter()接收FormData會爲空值null。而且bean的id必須爲multipartResolver。記得在POM中引入該類的依賴庫commons-fileupload:commons-fileupload
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/> <!--文件最大10M=10485760字節-->
<property name="defaultEncoding" value="UTF-8"/>
<property name="resolveLazily" value="true"/> <!--開啓文件延遲解析-->
</bean>
CodeUtil.checkCode()
實現如下,就是分別獲取request中傳來的用戶輸入的內容和session中儲存的內容進行比較,如果相同返回true
package com.tory.shop.util;
import com.google.code.kaptcha.Constants;
import javax.servlet.http.HttpServletRequest;
public class CodeUtil {
public static boolean checkCode(HttpServletRequest request) {
String expectedCode = (String) request.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
String inputCode = (String) request.getParameter("inputCode");
if (inputCode == null || !expectedCode.equals(inputCode))
return false;
else return true;
}
}
前端頁面
在前端頁面實現簡單的店鋪信息註冊如下所示,這裏使用的是一個輕量級的UI庫:SUI
使用CDN的方式引入SUI的相關文件,css文件在頁面加載之前,js文件在頁面加載之後。最後引入用於加載信息和提交Ajax請求的shopedit.js文件
<link rel="stylesheet"
href="//g.alicdn.com/msui/sm/0.6.2/css/sm.min.css">
<link rel="stylesheet"
href="//g.alicdn.com/msui/sm/0.6.2/css/sm-extend.min.css">
</head>
<body>
<div>
.......
</div>
<script type='text/javascript' src='//g.alicdn.com/sj/lib/zepto/zepto.js' charset='utf-8'></script>
<script type='text/javascript' src='//g.alicdn.com/msui/sm/0.6.2/js/sm.min.js' charset='utf-8'></script>
<script type='text/javascript' src='//g.alicdn.com/msui/sm/0.6.2/js/sm-extend.min.js' charset='utf-8'></script>
<script type='text/javascript' src='../resources/js/shop/shopedit.js' charset='utf-8'></script>
</body>
如下所示爲頁面的Javascript代碼,這裏使用的是zepto.js,它是一個和jQuery語法類似但更爲輕量的js庫。首先通過$.getJSON()
從服務器獲取categoryList和areaList信息並填充到頁面中。之後爲#submit
添加點擊事件,提交商店的信息。首先獲取頁面中的信息構建shop對象,並且獲取圖片文件shopImg。然後通過JSON.stringify()
將shop對象序列化爲json字符串,並和shopImg一起放到formData
對象中。最後通過$.ajax()
將formData發送給服務器。值得注意的是contentType
指定發送數據的形式,默認爲application/x-www-form-urlencoded
,即一般的表格將數據編碼爲urlenconded的方式,但是如果需要傳輸文件則需要FormData的方式而不是默認。這裏可以指定contentType爲false即不採用默認值,而是根據實際內容自動調整爲FormData。
$(function () {
registerShop();
function registerShop() {
//獲取初始化信息並填充到頁面
$.getJSON("/ShopDemo/shop/init", function (data) {
if (data.success) { //獲取店鋪分類和區域信息填充到頁面
var categoryHtml = '';
var areaHtml = '';
data.categoryList.map(function (item, index) {
categoryHtml += '<option data-id="' + item.categoryId + '">' + item.categoryName + '</option>';
});
data.areaList.map(function (item, index) {
areaHtml += '<option data-id="' + item.areaId + '">' + item.areaName + '</option>';
});
$('#shop-category').html(categoryHtml);
$('#area').html(areaHtml);
}
});
//提交註冊信息
$('#submit').click(function () {
//獲取頁面shop的信息與圖片
var shop = {};
shop.shopName = $('#shop-name').val();
shop.shopAddr = $('#shop-addr').val();
shop.shopPhone = $('#shop-phone').val();
shop.shopDescribe = $('#shop-desc').val();
shop.shopCategory = {
categoryId: $('#shop-category').find('option').not(function () {
return !this.selected;
}).data('id')
};
shop.area = {
areaId: $('#area').find('option').not(function () {
return !this.selected;
}).data('id')
};
var shopImg = $('#shop-img')[0].files[0];
//將shop信息和圖片封裝爲FormData並上傳給服務器
var formData = new FormData();
formData.append('shopStr', JSON.stringify(shop));
formData.append('shopImg', shopImg);
$.ajax({
url: '/ShopDemo/shop/register',
type: 'POST',
data: formData,
contentType: false,
processData: false,
cache: false,
success: function (data) {
if (data.success) {
$.toast('註冊成功!');
} else {
$.toast('註冊失敗' + data.errMsg);
}
}
})
})
}
});
頁面中驗證碼的實現使用的是com.github.penggle:kaptcha庫,在pom.xml文件中引入該依賴後需要在web.xml文件中配置servlet如下,
<!--配置驗證碼生成工具kaptcha -->
<servlet>
<!-- 生成Servlet -->
<servlet-name>Kaptcha</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
<!-- 是否有邊框 -->
<init-param>
<param-name>kaptcha.border</param-name>
<param-value>no</param-value>
</init-param>
<!-- 字體顏色 -->
<init-param>
<param-name>kaptcha.textproducer.font.color</param-name>
<param-value>red</param-value>
</init-param>
<!-- 圖片寬度 -->
<init-param>
<param-name>kaptcha.image.width</param-name>
<param-value>135</param-value>
</init-param>
<!-- 使用哪些字符生成驗證碼 -->
<init-param>
<param-name>kaptcha.textproducer.char.string</param-name>
<param-value>ACDEFHKPRSTWX345679</param-value>
</init-param>
<!-- 圖片高度 -->
<init-param>
<param-name>kaptcha.image.height</param-name>
<param-value>50</param-value>
</init-param>
<!-- 字體大小 -->
<init-param>
<param-name>kaptcha.textproducer.font.size</param-name>
<param-value>43</param-value>
</init-param>
<!-- 干擾線的顏色 -->
<init-param>
<param-name>kaptcha.noise.color</param-name>
<param-value>black</param-value>
</init-param>
<!-- 字符個數 -->
<init-param>
<param-name>kaptcha.textproducer.char.length</param-name>
<param-value>4</param-value>
</init-param>
<!-- 使用哪些字體 -->
<init-param>
<param-name>kaptcha.textproducer.font.names</param-name>
<param-value>Arial</param-value>
</init-param>
</servlet>
<!-- 映射的url -->
<servlet-mapping>
<servlet-name>Kaptcha</servlet-name>
<url-pattern>/Kaptcha</url-pattern>
</servlet-mapping>
其中<servlet-mapping>
映射路徑爲/Kaptcha,即請求該路徑會生成驗證碼並返回圖片,如下所示在html頁面中設置驗證碼圖片的src爲…/Kaptcha
<div class="item-inner">
<label for="kaptcha-code" class="item-title label">驗證碼</label>
<input id="kaptcha-code" name="kaptcha-code" type="text"
class="form-control in" placeholder="驗證碼"/>
<div class="item-input">
<img id="kaptcha_img" alt="點擊更換" title="點擊更換"
onclick="changeCode(this)" src="../Kaptcha"/>
</div>
</div>
點擊更換驗證碼即更改圖片的src,向/Kaptcha請求一個新的圖片,並且請求的參數附帶隨機數作爲參數
function changeCode(img) {
img.src="../Kaptcha?"+Math.floor(Math.random()*100)
}