ServletRequest
针对于请求报文的封装,JAVAEE规范制定的就是ServletRequest接口
我们在使用中绝大多数的情况都是使用HttpServletRequest
常用API
获取请求行
request.getmethod():获取请求的方法
request.getRequestURI():获取请求资源,/应用名/资源路径
request.getRequestURL():获取完整的访问的域名路径名
request.request.getProtocol():获取请求协议
获取请求头
request.getHeaderNames():获取请求头的key列表,类似迭代器
hasMoreElements():查看是否还有下一个元素
headerNames.nextElement():迭代
request.getHeader(headerName):获取请求头对应的value
Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()){ String headerName = headerNames.nextElement(); String headerValue = request.getHeader(headerName); System.out.println(headerName + ":" + headerValue); }
获取客户机和服务器主机的信息
request.getLocalAddr():获取服务器主机的地址
request.getLocalPort():服务器端口
request.getRemoteAddr():客户端地址
request.getRemotePort():客户端的端口
获取请求参数
只要提交的数据是key=value&key=value形式,那么都可以使用接下来介绍的API来获取请求参数,无论是在请求行还是请求体均适用。
只能获取用=连接的键值对,并且多个键值对直接用&来连接
request.getParameter:获取单个参数的值
request.getParameterValues:获取多个参数的值,返回一个数组
request.getParameterNames():获取所有参数的key,返回一个 Enumeration,类似于迭代器,接下来只需循环迭代即可
Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()){ String key = parameterNames.nextElement(); String[] values = request.getParameterValues(key); if(values.length == 1){ System.out.println(key + ":" + values[0]); }else { System.out.println(key + ":" + Arrays.toString(values)); } }
获取请求体
request.getInputStream()
其他
req.getServletPath() = requestURI - 应用名 相当于是获取资源的相对路径名
req.getContextPath() //获取应用名
封装数据到对象
JavaBean的特征:
1.一定要有无参构造函数
2.成员变量必须是private
3.一定要提供public的get和set方法
使用反射来手动获取并封装
package com.fh.ServletReq; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Method; import java.util.Enumeration; //通过反射获取请求参数并封装到对象 @WebServlet("/ref") public class GetParamServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Class<?> user = null; User u = null; try { user = Class.forName("com.fh.ServletReq.User"); //获取字节码文件 u = (User) user.newInstance(); //实例化 } catch (Exception e) { e.printStackTrace(); } //首先得拿到请求参数的key和value Enumeration<String> parameterNames = req.getParameterNames(); while (parameterNames.hasMoreElements()) { String key = parameterNames.nextElement(); String[] values = req.getParameterValues(key); String upKey = key.substring(0, 1).toUpperCase() + key.substring(1); //key就是用来封装的方法的名字,将首字母大写 try { Method set = user.getDeclaredMethod("set" + upKey, String.class);//调用set方法,这里一定要注意参数列表需要匹配 if (values.length == 1) { //当values长度为1时,直接放入 set.invoke(u, values[0]); } else { //长度大于1需要使用StringBuilder来拼接 StringBuilder sb = new StringBuilder(); for (int i = 0; i < values.length; i++) { if (i == values.length - 1) { sb.append(values[i]); } else { sb.append(values[i]).append(","); } } String s = sb.toString(); set.invoke(u, s); } } catch (Exception e) { e.printStackTrace(); } } System.out.println(u); } }
使用工具类commons-beanutils
此时注意如果要使用第三方jar包,那么需要对EE项目进行配置,主要有两种方案
1.直接将jar包赋值到web的WEB-INF目录下,这样项目可以通过快速通道复制到根目录下
2.在Artifacts下新建一个lib文件夹,并将idea目录下的lib添加进去
package com.fh.ServletReq; import org.apache.commons.beanutils.BeanUtils; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @WebServlet("/reg2") public class RegServlet2 extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { User user = new User(); try { BeanUtils.populate(user,req.getParameterMap()); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } System.out.println(user); } }
Debug
排查的过程:
1.查看状态码,如果是500,那么肯定表示代码出错了,但是如果没有出现500,不能说明没有错误
2.查看server(output)和localhost log,看看有无错误信息
3.具体去分析哪行代码出现了问题(仔细去查找,找和你的代码相关的部分)
在EE阶段要使用执行后续所有代码的按钮才能使页面正常响应
中文乱码问题
中文是否乱码要以debug的结果为准,不要看控制台的结果。
客户端发送的HTTP请求报文是UTF-8格式
请求报文被解析成为request对象之后,乱码了,解析的时候,解码用的不是utf-8 用的是ISO-8859-1 不支持中文
使用request.setCharacterEncoding来解决
注意事项:
1.只针对请求体有效
2.必须要在读取请求参数之前调用,也就是调用request.getParameter之前才可以使用
一般放在第一行,不容易出错
中文乱码只是在使用post请求方式时有乱码问题,如果使用get请求方式没有问题。前提是用的utf-8
网络路径总结
网络路径是指网络访问过程中的输入路径,与硬盘的路径有区别
全路径:htttp://localhost/app/1.html
可读性比较好,但在开发的过程中域名可能不同
相对路径:相对于当前页面的路径
比如提交的表单页面:http://localhost/app/1.html,里面的form表单的提交地址是
http://localhost/app/upload
那么写成相对路径就可以相对于1.html出发,去掉1.html然后再拼接上最终的路径
这种方式不灵活,不推荐
/开头的路径(推荐):
/应用名/资源路径。浏览器会自行补充访问协议、主机、端口号
转发
应用场景:
比如点击登录按钮,请求交给一个servlet来处理,如果servlet处理完成之后,希望将页面显示给用户,需要去调用页面,就可以使用转发
一个servlet在执行过程中,如果需要调用另外一个servlet,那么此时就可以使用转发。
转发----页面跳转相关的
要注意,转发前后使用的方法应该相同
package com.cskaoyan.request.forward; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName ${NAME} * @Description: TODO * @Author 远志 zhangsong@cskaoyan.onaliyun.com * @Date 2021/12/9 11:15 * @Version V1.0 **/ @WebServlet("/forward1") public class ForwardServlet1 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //访问forward1,需要在代码中去调用forward2,把2里面的数据显示出来 System.out.println("forward1"); //需要在forward1中去调用forward2 //千万不要自己去new一个servlet,然后调用service //生命周期应该交由tomcat来统一管理 //你去给tomcat发送一个指令,让tomcat去调用它 //这行代码的作用是给tomcat发送一个指令,告诉tomcat去加载当前应用下的/forward2 //tomcat就去加载forward2,如果它之前没有被访问过,首先实例化;如果访问过,直接调用service方法 //记住唯一的特殊情况:tomcat的转发中/开头不要加应用名 //看路径的解析执行主体是谁,如果是浏览器,则/应用名/资源路径,如果是服务器,则/资源路径 request.getRequestDispatcher("/forward2").forward(request, response); } }
ackage com.cskaoyan.request.forward; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName ${NAME} * @Description: TODO * @Author 远志 zhangsong@cskaoyan.onaliyun.com * @Date 2021/12/9 11:15 * @Version V1.0 **/ @WebServlet("/forward2") public class ForwardServlet2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("forward2"); } }
request域
context域:一块内存空间,可以作为运行时共享数据的场所。
只要是同一个应用下的不同资源,拿到的都是同一个servletContext对象,那么就可以共享context域
request域:只要可以拿到同一个request对象,那么也可以共享request域
如果一个地址频繁刷新,每次都会创建新的request对象
要注意只有同一个request对象才能共享request域
主要体现在转发的过程中
只有转发的两个组件间可以共享数据
用法:
1.比如查询数据,先到servlet中执行查询数据库的逻辑,拿到数据之后,放入request域中,然后将数据共享给jsp(本质来说就是servlet, 页面、servlet)
htttp://localhost/app/productList
------> servlet-----DB query List—放入request域中,转发请求到jsp页面,在页面中进行渲染数据