之前已经创建了商店的数据表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")
,在mapper使文件中通过#{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文件所对应的接口类为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)
}