JSP入门详解
JSP
Servlet基本概念
1.Servlet是什么
Servlet是JavaEE Web组件,必须运行在Web容器(tomcat)中,需要容器为其提供运行的环境,课程中使用Tomcat作为容器。
本质上,Servlet就是一个Java类,不过这个Java类要遵守一定的规范,即继承HttpServlet类
HttpServlet类中定义了很多方法,自定义的Servlet类需要覆盖其中的方法,一般情况下,只覆盖doGet或doPost。使用GET方式访问时,自动调用doGet,使用POST方式访问时,自动调用doPost
2.HttpServletRequest (req) - 读数据
拿参数
String value = req.getParameter("name"); // 拿单个 String[] values = req.getParameterValues("hobby"); // 拿多个
看信息
String method = req.getMethod(); // GET/POST String ip = req.getRemoteAddr(); // 客户端IP String url = req.getRequestURI(); // 请求路径
存东西(请求域)
req.setAttribute("key", object); // 存 Object obj = req.getAttribute("key"); // 取
会话相关
HttpSession session = req.getSession(); // 获取Session
3.HttpServletResponse (resp) - 写响应
设响应头(防乱码必备)
resp.setContentType("text/html;charset=UTF-8");
写数据
PrintWriter out = resp.getWriter(); // 字符流(文本) out.println("<h1>内容</h1>"); ServletOutputStream out = resp.getOutputStream(); // 字节流(文件)
重定向
resp.sendRedirect("新页面.html"); // 地址栏会变
设Cookie
Cookie cookie = new Cookie("name", "value"); resp.addCookie(cookie);
经典场景
登录验证:
req.getParameter()
拿账号密码 → 验证 →resp.sendRedirect()
跳转数据显示:查询数据 →
req.setAttribute()
存数据 → 转发到JSP显示文件下载:
resp.getOutputStream()
写文件流
4.doGet 和 doPost 的区别与例子
特性 | doGet | doPost |
---|---|---|
HTTP 方法 | 处理 GET 请求 | 处理 POST 请求 |
参数传递 | 参数附加在 URL 之后(如:?name=张三&age=20 ) | 参数放在 请求体 (Request Body) 中 |
安全性 | 差,参数在地址栏明文显示,可能被浏览器历史记录缓存 | 较好,参数不在地址栏显示,也不会被缓存 |
数据长度 | 有限制(因浏览器和服务器对URL长度有限制) | 理论上无限制(实际上服务器可配置) |
幂等性 | 幂等(多次执行同一请求,效果相同) | 不幂等(多次提交可能会产生不同效果,如重复下单) |
用途 | 用于获取数据(如:搜索、查询页面) | 用于提交/修改数据(如:登录、注册、支付) |
总结
你用
method="get"
,表单数据就会显示在地址栏(LoginServlet?username=admin&password=123456
),非常不安全,且doGet
方法会被调用。你用
method="post"
,地址栏只有LoginServlet
,数据隐藏传输,doPost
方法被调用。所以登录、注册等操作必须用POST。
5.HttpServletRequest (req) 和 HttpServletResponse (resp) 的作用与例子
可以把一次HTTP请求和响应想象成一次顾客和服务员的对话。
HttpServletRequest req
(请求对象): 就像是顾客对服务员说的话。它包含了客户发来的所有信息。HttpServletResponse resp
(响应对象): 就像是服务员准备回复顾客的工具。你用它来组织你要返回给客户的内容。
总结
核心功能 | 例子 | ||
---|---|---|---|
req | 顾客 | 获取信息:参数、URL、IP等;传递数据 | String name = req.getParameter("name"); |
resp | 服务员 | 设置回应:内容类型、编码、状态码;发送数据 | resp.getWriter().println("Hello"); |
6.Servlet跳转到其他组件
1)第一种跳转方式:响应重定向
HttpServletResponse中定义了响应重定向的方法
– sendRedirect(String path)
响应重定向是向目标资源重新发送请求,生成新的响应
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {// 1. 获取参数String username = req.getParameter("username"); // 这里能拿到 "admin"String password = req.getParameter("password"); // 这里能拿到 "123456"// 2. 验证登录(假设成功) // 3. 重定向到欢迎页面 resp.sendRedirect("WelcomeServlet"); // 问题就出在这里!}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {// 尝试获取用户名String username = req.getParameter("username"); // 这里得到的是 null!resp.getWriter().println("欢迎你:" + username); // 显示"欢迎你:null" }
为什么是 null?
1)因为 重定向的本质是让浏览器发起一个新的GET请求:
第一次请求:浏览器 POST 提交表单到
LoginServlet
,携带参数username=admin&password=123456
LoginServlet处理:收到参数,验证通过
响应重定向:
LoginServlet
返回302状态码和Location: WelcomeServlet
头信息浏览器动作:浏览器看到302,自动发起一个新的GET请求到
WelcomeServlet
第二次请求:这个新请求没有任何参数,就是单纯的访问
WelcomeServlet
WelcomeServlet处理:
req.getParameter("username")
自然就拿不到值了
2)第二种跳转方式:请求转发
请求转发
能够把当前的请求对象转发到目标资源,是最常用的跳转方法
使用步骤
先使用请求中getRequestDispatcher(String path)方法获得请求转发器对象RequestDispatcher
然后调用RequestDispatcher的forward(request,response)方法进行跳转 forward传参数
修改LoginServlet中的跳转方法
使用请求转发后,当前的请求对象被转发到下一个资源,因此可以得到请求对象中的数据,包括请求参数,请求方法等。在doPost中调用该方法,将调用下一个资源的doPost方法;在doGet方法中调用该方法,调用下一个资源的doGet方法
例子
请求转发简单例子
场景:用户登录成功后跳转到欢迎页面
LoginServlet.java
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. 模拟登录验证if ("admin".equals(username) && "123456".equals(password)) {// 登录成功,把用户名存到请求中req.setAttribute("welcomeMsg", "欢迎回来," + username + "!");// 4. 使用请求转发跳转到欢迎页面RequestDispatcher rd = req.getRequestDispatcher("WelcomeServlet");rd.forward(req, resp); // 关键代码!} else {// 登录失败resp.getWriter().println("登录失败!");} }
WelcomeServlet.java
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=UTF-8");PrintWriter out = resp.getWriter();// 可以直接获取LoginServlet中设置的属性String welcomeMsg = (String) req.getAttribute("welcomeMsg");// 也可以直接获取原始请求的参数(因为是一次请求)String username = req.getParameter("username"); // 这里能拿到值!out.println("<h1>" + welcomeMsg + "</h1>");out.println("<p>用户名: " + username + "</p>"); }
重定向 vs 转发(Forward)
特性 | 重定向 (Redirect) | 转发 (Forward) |
---|---|---|
请求次数 | 2次 | 1次 |
地址栏 | 变化(显示新URL) | 不变(显示原URL) |
数据传递 | 丢失原始请求参数 | 保留原始请求参数和属性 |
实现方式 | resp.sendRedirect("url") | req.getRequestDispatcher("url").forward(req, resp) |
为什么浏览器地址栏显示的是 /login
而不是JSP路径?
因为:请求转发是服务器内部的行为,浏览器完全不知道发生了什么!
整个流程是这样的:
你在浏览器输入:
http://localhost:8080/login
(访问Servlet)浏览器向服务器发送请求:GET
/login
服务器收到请求:
LoginServlet
的doGet()
方法被执行Servlet内部处理:验证登录、查询数据等
Servlet决定转发:
req.getRequestDispatcher("welcome.jsp").forward(req, resp);
服务器内部跳转:服务器直接调用
welcome.jsp
来生成HTML内容服务器返回响应:把JSP生成的HTML内容返回给浏览器
浏览器显示结果:显示JSP的内容,但地址栏仍然显示最初的
/login
这是正常且正确的行为!
优点:
隐藏技术细节:用户看不到JSP路径,更安全
保持URL整洁:逻辑URL(
/login
)和物理文件路径(welcome.jsp
)分离更好的SEO:统一的URL结构