这个案例算是我第一次完成比较完整的前后端交互的案例,从思路非常乱到一步一步理清思路,用了一天左右的时间,写下这篇博客,一是为了给初学者提供一些经验或者帮助加深一下记忆,二是想让大佬查一下我理解中存在的漏洞或者错误,三是为了做一下笔记,方便自己随时回头巩固一下。
内容比较多,语言组织的也不太好,望包涵。
首先,需要简单了解一下下面几个方法的用法及作用,新手不理解也没有关系,可以继续往下看结合案例理解(由于都放到一起比较乱,大家可能看晕,所以每个方法间隔开了):
request.setCharacterEncoding(“utf-8”);作用是指定服务器响应给浏览器的编码。同时,浏览器也是根据这个参数来对其接收到的数据进行重新编码(或者称为解码),如果不设置编码,可能会在页面存在中文的时候导致编码,所以通常来讲第一步设置编码。
req.getParameter(“表单的name”);是一种比较低级的获取表单的值的方法,根据表单中的name值获取value值,需要获取几个值就要调用几次这个方法,为什么这种方法比较低级呢?试想一下在用户注册页面中,我们需要调用一次获取用户名,再调用获取密码,再调用获取姓名、性别、年龄等等各种信息,而下面的request.getParameterMap();方法完美解决了这个问题。
request.getParameterMap();法则不同,不需要参数,返回结果为Map<String,String[]> ,将所有页面属性值封装在Map集合中。根据Java规范:request.getParameterMap()返回的是一个Map类型的值,该返回值记录着前端(如jsp页面)所提交请求中的请求参数和请求参数值的映射关系。这个返回值有个特别之处——只能读。不像普通的Map类型数据一样可以修改。
BeanUtils.populate(loginUser, map);这个方法会遍历map<key, value>中的key,如果bean中有这个属性,就把这个key对应的value值赋给bean的属性。也就是把map中的元素赋值给bean对象,这样我们就可以通过bean.getXxxx()来取值了。
request.getRequestDispatcher().forward(req, resp);首先要知道Web是 请求/响应 架构的使用,而request和response就是在服务器端生成的相应的两个对象,request能够获取客户端传递的参数及相关的一些信息,而response就是给客户端响应相关的页面及信息
request.getRequestDispatcher().forward(request.response)这个语句意思是将客户端的请求转向(forward)到getRequestDispatcher()方法中参数定义的页面或者链接。
说通俗点就是,当一个客户端的请求到这个页面后,不做处理或者不处理完,将请求转给另一个页面处理,然后再响应给客户端。request.setAttribute(name,value);增加一个指定名称和值的新属性,或者把一个现有属性设定为指定的值,说明:1.name:要设置的属性名。2.value:要设置的属性值。
在request对象中加入名为name的属性并附值为value,因为request对象是可以更改的,你可以在同一个请求中象这样访问这个属性。req.getRequestDispatcher().forward(req, resp); 在一个servlet中可以调用另外一个servlet来处理请求,是跳转,转发,前后页面共同使用一个request,地址栏没变化,等于一次请求两次相应,我的理解是把请求交给.getRequestDispatcher()方法括号中的servlet来处理,并传递request和response。相当于前后两次请求但是地址栏不会发生变化。
response.getWriter().write(“你好你好你好”);简单理解为将你好你好你好这一串字返回给前端,具体原理大家可以自行百度一下。
template.queryForObject(sql,new BeanPropertyRowMapper(User.class), loginUser.getUsername(), loginUser.getPassword());
方法参数比较长,大家可以简单理解为template中的query方法,说白了就是用于执行查询相关语句,之只不过他跟普通的query不同的是,这个方法可以返回一个基本类型对应包装类型的对象。
下面是具体的代码实现以及我个人的理解(代码包含注释):
前端的表单页面代码
action属性:表单提交后会通过action属性跳转到后面的路径文件中,其中/test是我的虚拟目录
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/test/loginServlet" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
User实体类
不作特殊讲解,学过javase的应该都懂,该类用来存储数据库中的数据。这里重写了toString方法用来方便输出
package domain;
/**
* User表的实体类
*/
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "user{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
UserDao类:主要作用是用来操作数据库中的表。
注意:需要注意与i下queryForObject方法的用法,将该方法返回的User对象作为参数return给login方法,其参数就是前端返回的username和password的值,用于判断数据库中是否存在对应的值,如果不存在,则报异常,所以我们可以在捕捉异常的时候return null,这样servlet只需要判断这个方法的返回值就可以知道数据库中是否存在对应的值,也就可以判断前端输入的用户名和密码是否正确。
/**
* 操作数据库中的User表中的类
*/
public class UserDao {
//声明JDBCTemplate对象共用
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
*登录方法
* @param loginUser 只有用户名和密码
* @return 包含用户全部数据
*/
public User login(User loginUser){
try {
//1.编写SQL
String sql = "select * from user where username = ? and password = ?";
//2.调用query方法
User user = template.queryForObject(sql,
new BeanPropertyRowMapper<User>(User.class),
loginUser.getUsername(), loginUser.getPassword());
return user;
} catch (DataAccessException e) {
e.printStackTrace();
return null;
}
}
}
logininServlet类
前端页面的表单提交后会执行这个servlet
(导包部分太占地所以去掉了,其中第二步注释掉的就是前面讲到的比较低级的获取值的方法,可作简单了解)
注意:这里调用UserDao对象的login方法,实现了判断前端输入数据是否正确的功能,重复一遍,如果userdao中的login方法没有在数据库中找到对应的值,则返回null,所以直接判断user是否为null,就可以作为页面跳转的依据,由于学识尚浅,我们只输出了一段简单的提示。至于getRequestDispatcher("/failServlet").forward(req, resp);这个方法,大家可以到文章开头回顾一下。
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置编码
req.setCharacterEncoding("utf-8");
/* //2.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
//3.封装User对象
---------
/**User loginUser = new User();
loginUser.setUsername(username);
loginUser.setPassword(password);*/
//----------------------------------------------------
/** 映射的过程就是将页面中的内容先用request获得,然后再将之转换为Map(这里用request.getParameterMap())
* 最后使用BeanUtils.populate(info,map)方法将页面各个属性映射到bean中。之后我们就可以这样使用bean.getXxxx()来取值了。
*/
//2.获取所有请求参数
Map<String, String[]> map = req.getParameterMap();
//3.创建user对象
User loginUser = new User();
//3.2使用BeanUtils封装
try {
BeanUtils.populate(loginUser, map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//-------------------------------------------------------------
//4.调用UserDao的login方法
UserDao dao = new UserDao();
User user = dao.login(loginUser);
//5.判断user
if (user == null) {
//登陆失败
req.getRequestDispatcher("/failServlet").forward(req, resp);
} else {
//登陆成功
//存储数据
req.setAttribute("user", user);
//转发
req.getRequestDispatcher("/successServlet").forward(req, resp);
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
successServlet类
注意:当刚才的LoginServlet类中判断数据正确,也就是user不为空的话,就会跳转到这个successServlet类中,这里输出简单提示,需要说一下write方法输出的内容中,使用++可以动态的传递参数,而user.getUsername(),不难理解就是我们从数据库中查到的Username值赋值到User类中,通过get方法获取到的值。
@WebServlet("/successServlet")
public class SeccessServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取request域中共享的user对象
User user = (User) req.getAttribute("user");
if (user != null) {
//给页面写一句话
//设置页面编码
resp.setContentType("text/html;charset=utf-8");
//输出
resp.getWriter().write("登陆成功!" + user.getUsername() + "欢迎您!");
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}
failServlet类
该类不做特殊讲解,作用与successServlet类相同。
@WebServlet("/failServlet")
public class FailServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//给页面写一句话
//设置页面编码
resp.setContentType("text/html;charset=utf-8");
//输出
resp.getWriter().write("登陆失败,用户名或密码错误");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}
最后附上一张自己画的图,帮助加深理解。
如有错误,望指出。