文章目录
- 案例(请求分发案例)
- Response
- 响应行
- 响应头
- 响应体
- 特殊响应头
- refresh
- Content-type
- Content-disposition
- location
- 案例(登录案例)
案例(请求分发案例)
- 场景:有多个请求
Http://localhost:8080/user/login
→ 登录Http://localhost:8080/user/register
→ 注册Http://localhost:8080/user/info
→ 查看用户信息
eg:
/**
* localhost:8080/demo1/user/login
* localhost:8080/demo1/user/register
* localhost:8080/demo1/user/info
*/
//@WebServlet(value = {"/user/login", "/user/register", "/user/info"})
@WebServlet("/user1/*")
public class UserServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
process(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
process(req, resp);
}
private void process(HttpServletRequest request, HttpServletResponse resp) {
String operation = null;
// /demo1/user/login
String requestURI = request.getRequestURI();
operation = requestURI.substring(requestURI.lastIndexOf("/") + 1);
// if("login".equals(operation)) {
// login(request,resp);
// } else if("info".equals(operation)) {
// info(request,resp);
// }
switch (operation) {
case "login":
login(request, resp);
break;
case "info":
info(request, resp);
break;
case "register":
register(request, resp);
break;
case "remove":
remove(request, resp);
break;
}
}
private void remove(HttpServletRequest request, HttpServletResponse resp) {
}
private void register(HttpServletRequest request, HttpServletResponse resp) {
}
private void info(HttpServletRequest request, HttpServletResponse resp) {
}
private void login(HttpServletRequest request, HttpServletResponse resp) {
}
}
优化版本:
eg:
DispatchUtil.java
- 通过反射的方式,实现其通用性
public class DispatchUtil {
@SneakyThrows
public static void dispatch(String operation, HttpServletRequest request
, HttpServletResponse response, HttpServlet instance) {
// java.lang.Class.getDeclaredMethod()方法返回一个Method对象,
// 它反映此Class对象所表示的类或接口的指定已声明方法。
// name 参数是一个字符串,指定所需的方法的简单名称,
// parameterTypes 参数是一个数组的Class对象识别方法的形参类型,在声明的顺序
Method method = instance.getClass().getDeclaredMethod(operation
, HttpServletRequest.class, HttpServletResponse.class);
method.setAccessible(true);
method.invoke(instance, new Object[]{request, response});
}
}
//@WebServlet(value = {"/user/login", "/user/register", "/user/info"})
@WebServlet("/user2/*")
public class UserServlet2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
process(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
process(req, resp);
}
private void process(HttpServletRequest request, HttpServletResponse resp) {
String operation = null;
// /demo1/user/login
String requestURI = request.getRequestURI();
operation = requestURI.substring(requestURI.lastIndexOf("/") + 1);
// if("login".equals(operation)) {
// login(request,resp);
// } else if("info".equals(operation)) {
// info(request,resp);
// }
// 不需要再switch判断,直接写方法名函数就可以
DispatchUtil.dispatch(operation, request, resp, this);
}
private void remove(HttpServletRequest request, HttpServletResponse resp) {
}
private void register(HttpServletRequest request, HttpServletResponse resp) {
}
private void info(HttpServletRequest request, HttpServletResponse resp) {
}
private void login(HttpServletRequest request, HttpServletResponse resp) {
}
private void logout(HttpServletRequest request, HttpServletResponse resp) {
System.out.println("logout");
}
}
Response
响应报文的封装,设置响应报文
eg:
HTTP/1.1 200
Vary: accept-encoding,origin,access-control-request-headers,access-control-request-method,accept-encoding
Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Sun, 12-Feb-2023 06:51:56 GMT
Set-Cookie: JSESSIONID=24287278-5ebb-407d-a3f7-56b74782c4c7; Path=/; HttpOnly
Access-Control-Allow-Origin: *
Content-Type: application/json;charset=UTF-8
Date: Mon, 13 Feb 2023 06:51:56 GMT
Content-Length: 200
{"errno":0,"data":{"adminInfo":
{"nickName":"admin123","avatar":"https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif"},
"token":"24287278-5ebb-407d-a3f7-56b74782c4c7"},"errmsg":"成功"}
响应行
设置一下响应状态码
方法名 | 参数 | 说明 |
---|---|---|
setStatus(int) | 参数就是状态码 | 设置响应状态码 |
eg:
/**
* 常用的响应状态码:200、404、302、400、500
* 响应行中的响应状态码
*/
@WebServlet("/line")
public class LineServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse response)
throws ServletException, IOException {
response.setStatus(302);
}
}
响应头
- 响应头是
key:value
的格式,提供了通用的方法,可以设置响应头的key和value - 提供了一些特定的方法,特定的方法做的事情,就是设置特定响应头的值
方法 | 参数 | 说明 |
---|---|---|
setHeader(String,String) | 参数1提供key,参数2提供value | 通用的方法 |
eg:
@WebServlet("/header")
public class HeaderServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse response)
throws ServletException, IOException {
// 第一个参数是key,第二个参数是value
response.setHeader("custom-header","aaaaaa");
}
}
响应体
- 可以使用字符流,也可以使用字节流
- 场景:
- 字符流:响应文本数据,最主要的场景就是前后端分离之后,通过字符流响应Json数据
- 字节流:响应图片、文件,也通常在文件下载的场景下使用
方法 | 返回值 | 描述 |
---|---|---|
getWriter() | PrintWriter | 字符流 |
getOutputStream() | ServletOutputStream | 字节流 |
eg:
- 字符流举例
@WebServlet("/body1")
public class BodyServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse response)
throws ServletException, IOException {
// response.getWriter().write("hello world");
// 如果把响应体里的字符以utf-8的方式编码,不一定能解决中文乱码的问题
// 因为浏览器不一定以utf-8的方式来解码
// 从根本上解决这个问题通知浏览器以utf-8的方式解码
// content-type:指响应体里的正文类型
response.setHeader("content-type","text/html;charset=utf-8");
// response.getWriter().write("hello world");
// 默认的编码:iso-8859-1
response.getWriter().println("hello world");
}
}
- 字节流举例
@WebServlet("/body2")
public class BodyServlet2 extends HttpServlet {
// http://localhost:8080/demo1/body2?pic=1.jpg
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String pic = request.getParameter("pic");
String path = "D:\\test_photo";
File file = new File(path, pic);
// 要显示图片,需要在响应体中提供其字节数据 -> 浏览器就会显示图片
ServletOutputStream outputStream = response.getOutputStream();
FileInputStream inputStream = new FileInputStream(file);
int length = 0;
byte[] bytes = new byte[1024];
while((length = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0 ,length);
}
inputStream.close();
outputStream.close();
}
}
特殊响应头
- 特殊的几个响应头:
refresh
→ 定时刷新、跳转content-type
→ 限定响应的正文(也可以解决中文乱码问题)content-disposition
→ 文件下载location
→ 重定向
refresh
eg:
/**
* 自动显示当前的时间
*/
@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse response)
throws ServletException, IOException {
response.setHeader("refresh", "1");
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
String dateStr = simpleDateFormat.format(new Date());
response.getWriter().println(dateStr);
}
}
@WebServlet("/refresh2")
public class RefreshServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse response)
throws ServletException, IOException {
// 指3s之后去访问demo2/hello.jsp
response.setHeader("refresh", "3;url=/demo1/hello.jsp");
response.setContentType("text/html;charset=utf-8");
response.getWriter().println("请稍后马上跳转到欢迎页面");
}
}
Content-type
- 通常不需要设置
- 比如我们响应Json数据给前端,我们可以设置
Content-Type:application/json
- 我们要在这里做字符集的设置,如果没有做有可能出现中文乱码
Content-disposition
- 下载的场景会使用
- eg:
content-disposition: attachment;filename=1.jpg
- 以
1.jpg
来下载正文
- eg:
eg:
@WebServlet("/download")
public class ContentDispositionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String pic = request.getParameter("pic");
String path = "D:\\test_photo";
// 如果代码没有这行内容,那么就是显示该图片
// 如果做下载,需要设置header content-disposition
response.setHeader("content-disposition", "attachment;" + pic);
FileInputStream inputStream = new FileInputStream(new File(path, pic));
ServletOutputStream outputStream = response.getOutputStream();
int length = 0;
byte[] bytes = new byte[1024];
while ((length = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, length);
}
inputStream.close();
}
}
location
- 重定向
- 状态码:
302
eg:
@WebServlet("/location")
public class LocationServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("访问到LocationServlet");
resp.setStatus(302);
resp.setHeader("location","http://localhost:8080/demo1/refresh");
}
}
案例(登录案例)
- 这个请求由登录页面提供,我们可以通过html提供一个登录表单,该表单会发出请求
http://localhost:8080/user/login
→ Servlet → 检查用户名和密码是否正确(使用一下MyBatis) →
- 如果正确,那么就提示登录成功
- 如果错误,那么刷新登录页面
任务拆解:
- 包含登录表单的 login.html文件,放在webapp目录下
- 开发UserServlet
- /user/login对应的处理方法,使用MyBatis做查询
- /user/info对应的处理方法
- 整合MyBatis,在应用程序中维护SqlSessionFactory实例
- Mybatis的配置:
public interface UserMapper {
List<User> selectByUserNameAndPassword
(@Param("username") String username, @Param("password") String password);
User selectByPrimaryKey(Integer id);
}
<mapper namespace="com.coo1heisenberg.demo2.mapper.UserMapper">
<select id="selectByUserNameAndPassword" resultType="com.coo1heisenberg.demo2.bean.User">
select id, username, password, age, birthday, createDate, mobile from test_user
<where>
username = #{username} and password = #{password}
</where>
</select>
<select id="selectByPrimaryKey" resultType="com.coo1heisenberg.demo2.bean.User">
select id, username, password, age, birthday, createDate, mobile from test_user
<where>
id = #{id}
</where>
</select>
</mapper>
- Servlet的配置
@WebServlet("/user/*")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
process(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
process(req, resp);
}
private void process(HttpServletRequest request, HttpServletResponse response) {
String requestURI = request.getRequestURI();
String operation = null;
operation = requestURI.substring(requestURI.lastIndexOf("/") + 1);
switch (operation) {
case "login":
login(request, response);
break;
case "info":
info(request, response);
break;
}
}
@SneakyThrows
private void info(HttpServletRequest request, HttpServletResponse response) {
Object user = request.getAttribute("user");
response.getWriter().println(user);
}
@SneakyThrows
private void login(HttpServletRequest request, HttpServletResponse response) {
// 1. 首先获得username和password
String username = request.getParameter("username");
String password = request.getParameter("password");
// 2. 查询user记录
UserMapper userMapper = MybatisUtil.getSqlSession().getMapper(UserMapper.class);
List<User> users = userMapper.selectByUserNameAndPassword(username, password);
// 3. 根据user记录判断登录状态
response.setContentType("text/html;charset=utf-8");
// 4. 判断登录状态 user的list是否为空
if (users == null || users.size() == 0) {
// 如果登陆失败(list == null || list.size() == 0)
// 刷新到refresh --> 2;url=/demo2/login.html
// 响应登录失败信息
response.getWriter().println("登录失败,即将跳转登录页面");
response.setHeader("refresh", "2;url=/demo2/login.html");
} else {
// 如果登录成功(list.size > 0)
// 响应登录成功信息
response.getWriter().println("登录成功");
request.setAttribute("user", users.get(0));
request.getRequestDispatcher("/user/info").forward(request, response);
// 跳转到info -> 分享user的id信息(直接分享user信息)
}
}
}
- HTML的配置
<body>
<h1>登录页面</h1>
<!--action:表示当前表单中的内容提交给哪个页面进行处理-->
<!--method:表示当前表单提交的方式,常见的有get和post方式,默认是get提交-->
<form action="/demo2/user/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>
</body>