年轻人要讲抄德,抄袭作业又蠢又坏,大家耗子尾汁
bupt web 作业3
Servlet实例对象
1、在附图所示的DemoServlet类的doGet方法中有HttpServletRequest和HttpServletResponse对象作为参数传入,请查阅资料说明这两个对象从何而来?
参考:【【转帖】Servlet技术】
2、在Web应用运行时对于DemoServlet类会创建多少个对象实例,当多个客户同时发起 /add?inc=100的请求时,请思考变量sum和变量count是否会收到多并发的影响?
关于servlet的实例对象,我们需要明确的是一个服务器就相当于一个对象。那么下图中,私有类型变量sum就肯定是DemoServlet的一个实例,与之不同的是doGet()函数里的count变量。因为doGet()函数每次都只是在接收到了HTTP请求后才会被调用,因此每次调用都只是临时创建一个count和inc去记录该次请求的参数,一旦执行完工作就会被JVM回收掉。因此多并发请求影响的只有sum
![](https://i-blog.csdnimg.cn/blog_migrate/937780194a9b8b898e81213ed82a3834.png#pic_center)
Servlet项目创建(TomCat 8.5.59 + Windows 10 + IDEA2020.2.3)
详见https://blog.csdn.net/Mr_Ohahah/article/details/109278483
过滤器以及会话管理
设计一个需要验证后才能使用的加法计算Servlet,具体要求如下:
1)设计实现一个addServlet,用来接收形求和请求,参数形如 a=1&b=1,addServlet对参数a、b是否有有效数字进行检查,当a、b值为有效数字时,将请求转发给doneServlet进行处理。
2)设计实现doneServlet,对接收到了两个参数a、b进行求和然后返回结果。
3)设计一个过滤器,对请求进行过滤,检查session中是否有登录成功标志,如果没有,则重定向到登录页面
4)设计一个登录页面,其中有表单用于输入用户名和密码,点击提交时表单将post到loginServlet上。
5)设计一个loginServlet,当请求参数为user=admin,password=123456时,认为登录成功,将登录成功标志位记录在session中。
6)设计一个logoutServlet,用来清除当前session中的登录成功标志
测试流程
1)首次请求url /addServlet/a=1, 由于未执行登录,则被重定向到登录页面
2)在登录页面输入用户名密码,当用户名密码正确时登录成功
3)再次请求url /addServlet/a=1,返回参数错误信息
4)请求url /addServlet/a=1&b=2,返回ab的和3(有doneServlet计算完成)
5)请求url /logoutServlet, 注销当前登录
6)请求url /addServlet/a=1&b=2,由于已注销,被重定向到登录页面
需求分析
根据题目要求,我们先设定好环境,建立这些Servlet和Filter类。整个项目的框架如下:
Login模块
1. 登录
根据需求,未登录状态的话肯定首先要面对登录问题。所以我们首先需要解决的就是登录问题。
我们先写一个登录的静态页面login.html。注意要放在web文件夹下!!!这里使用了Form表单,因为整个项目是设计Client-Server模式的,肯定要有信息的提交和处理。表单的提交方式自然是post。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆界面</title>
</head>
<body>
<form action="loginServlet" method="post">
<table id="mytable">
<tr>
<td><span>用户名</span></td>
<td><input type="text" id="username" name="username" value=""></td>
</tr>
<tr>
<td><span>密码</span></td>
<td><input type="password" id="password" name="password" value=""></td>
</tr>
</table>
<br>
<input type="submit" value="登录" id="checkBtn">
</form>
</body>
</html>
2. 获取登录信息,判断是否允许登录
这是第一个Servlet服务。这个Servlet需要和静态页面连接着。首先,在login.html中,表单可以进行提交。那么loginServlet就要处理这条post信息。重写doPost():
package com.servlet;
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.io.PrintWriter;
@WebServlet(name = "loginServlet",urlPatterns = {"/loginServlet"})
public class loginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置编码方式
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
//根据post消息获取用户名和密码
String user = request.getParameter("username");
String pass = request.getParameter("password");
//必须用“admin”和“123456”去匹配接收到的内容。因为如果
if(user.equals("admin") && pass.equals("123456")){
request.getSession().setAttribute("login",1);
out.println("登陆成功");
}
else{
out.println("登陆失败");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
测试:启动服务器后,用url进入/login.html,输入admin和123456,提交表单信息:
测试:启动服务器后,用url进入/login.html,输入错误的用户名密码,提交表单信息:
3. 登出
在上面的loginServlet中,我设置了登陆成功的标志位“login”,成功时为1 。注意这个标志位是存放在session对象里的,也就是说其他任何一个Servlet现在都可以通过查询得知session里有这么一个值了(具体原因本文最后会继续阐明)。logoutServlet也不例外。所以其思路就是检查这个标志位,存在就删掉,不存在就说明根本没登陆。
package com.servlet;
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.io.PrintWriter;
@WebServlet(name = "logoutServlet",urlPatterns = "/logoutServlet")
public class logoutServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
Object flag = request.getSession().getAttribute("login");
if(null == flag){
out.println("你还没登陆");
}
else{
out.println("已登出");
//从session中移除该标志位
request.getSession().removeAttribute("login");
}
}
}
测试:登陆成功后登出,访问logoutSevlet
第一次访问logoutSevlet,成功登出
测试:什么也没干直接请求logoutServlet
过滤器模块
注解配置:Servlet从3.0开始支持注解配置,注解的属性如下表
1. 认识过滤器filer
举个栗子:想要进入小破邮的“将军冢”——学一宿舍,那么先要通过大门保安的过滤,再通过宿管门禁的过滤,从而进入宿舍。
对于login来说,在你没成功login之前,你不应当有任何权限访问其他任何的Servlet才对。所以这就是问题所在:过滤器需要判断到底有没有login成功,成功了,就相当于过关了(进大门了,可以干别的事了);不成功,没过关(被堵在大门外面进不去校区玩耍),就被分配到login界面去就对了。
这个项目涉及到需要被过滤的地方其实也就这几个:就是在没登陆之前把访问addServlet、logoutServlet、doneServlet的请求直接过滤掉就好。
package com.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//触发过滤器的url正则格式就是以下三种:addServlet、logoutServlet、doneServlet
@WebFilter(filterName = "the_Filter",urlPatterns = {"/addServlet/*","/logoutServlet/*","/doneServlet/*"})
public class the_Filter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
Object flag = request.getSession().getAttribute("login");
//已经登录,标志位存在
if(null != flag){
chain.doFilter(req, resp);
}
//没有登陆,重定向到登陆界面
else{
response.sendRedirect(request.getContextPath()+"/login.html");
}
}
public void init(FilterConfig config) throws ServletException {
}
}
测试:什么也没干直接请求addServlet、logoutServlet、doneServlet
加法器模块
现在,整个项目就已经是一个能够过滤登陆状态的项目了,已经完成了一大半。那么接下来这两个Servlet类就要协调好两个Servlet之间的通信就行。
1. addServlet
package com.servlet;
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.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@WebServlet(name = "addServlet",urlPatterns = {"/addServlet/*"})
public class addServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
String where = request.getRequestURI();
String[] List = where.split("/");
//getURI函数是把服务器localhost:8080后面所有的内容扒拉下来,
//即/zuoye3_war_exploded/addServlet/a=1&b=2,而且注意split,它会使where[0]是空字符串
if(List.length != 4){
out.println("输入的级数不足");
}
else{
//正则匹配所有整型数字
String str= "^a=(-?[1-9]\\d*|0)&b=(-?[1-9]\\d*|0)$";
Pattern pattern = Pattern.compile(str);
Matcher m = pattern.matcher(List[3]);
boolean flag = false;
while(m.find()){
flag = true;
Integer a = Integer.parseInt(m.group(1));
Integer b = Integer.parseInt(m.group(2));
request.getSession().setAttribute("a",a);
request.getSession().setAttribute("b",b);
}
if(!flag){
out.println("输入的参数不够或错误");
}
else{
request.getRequestDispatcher("/doneServlet").forward(request,response);
}
}
}
}
2. doneServlet
package com.servlet;
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.io.PrintWriter;
@WebServlet(name = "doneServlet",urlPatterns = "/doneServlet")
public class doneServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
Object A = request.getSession().getAttribute("a");
Object B = request.getSession().getAttribute("b");
Integer c = (Integer)A+(Integer)B;
out.println(c);
}
}
测试:登陆后计算1+2和其他计算
测试:登陆后只输入到残缺不完整的参数
测试:登出,重定向
到这里,全部功能就都已经保证实现了
关于Servlet细节
上面的代码没太注释,所以其中很多细节就放在这里阐述了
1. .jsp是神马
项目里,有个index.jsp
JSP和Sevlet其实算是两种开发模式,二者关系如下:
所以大人时代变了?
2. form表单的action是什么
action一定要有,其实就类似于< a href = " " >,action起到一个替换作用,把login.html这个uri位置替换成loginServlet罢了
3. doPost()和doGet()
由于现在是初学Servlet,所以用doGet()多一点。其实通用一点的是doPost(),doGet方法提交表单的时候会在url后边显示提交的内容,所以不安全。而且doGet方法只能提交256个字符(1024字节),而doPost没有限制,因为get方式数据的传输载体是URL(提交方式能form,也能任意的URL链接),而POST是HTTP头键值对(只能以form方式提交)。
4. Servlet九大对象和四个作用域
【转】Servlet 九大对象和四个作用域
这个项目主要涉及的思考点在于四个作用域:HTTPServletRequest、HTTPServletResponse;Page;Session;WebApplication
类型 | 阐述 |
---|---|
Page | (虽然涉及它的作用域的注意事项没用到)page对应this关键字。JSP网页本身 page对象是当前页面转换后的Servlet类的实例。page的作用域最小,一个request可以包含多个page页(include,filter,forward) |
Request、Response | 作用域只限于单词请求,也就是说即便设置了Request自己的一个标志位,这个请求发生后,可以创建它的一些标志位。但是一旦请求被接收处理了,请求就自动销毁了,这个标志位就不存在了 |
Session | 会话作用域,当用户首次访问时,产生一个新的会话,以后服务器就可以记住这个会话状态。生命周期:会话超时,或者服务器端强制使会话失效。也就是说,每次浏览器开启,访问服务器了,那么这就是一次新的会话,所有在此次Session里设置的标志位可以被所有Servlet访问,直到浏览器关闭(关闭会话) |
WebApplication | 全局作用范围,整个应用程序共享,就是在部署文件中的同一个webApp共享,生命周期为:应用程序启动到停止。也就是说,只要服务器不关闭,那么在这里设置的标志位就可以一直存在,无论你开多少浏览器,这些标志位都在,直到关闭服务器 |
敲黑板打重点!!!弄明白Session的作用域很重要!!!
在这个项目中,浏览器打开后以任意方式首次访问服务器时就会启用Session并生成一个Session ID并在接下来都用此ID标注、检索鉴别该会话。
这也就是上面代码中设置标志位都是利用request.getSession().setAttribute("login",1);
这种方式的原因,因为诸如登录状态标志位等都是需要在整个会话周期内使用的,自然是需要设置为Session的一种属性即可。
否则,如果只是request.setAttribute("login",1);
即便设置了标志位属性,在request到达后还没被来得及处理就被清除了,就会永远无法识别该种属性,登录也就不会成功。
5. 为什么使用Object去接收属性值
在上面的代码中,我采用的是
Object flag = request.getSession().getAttribute("login");
关于login的属性值,其实挺明显的,就是个整型。之所以用Object,是因为JAVA里所有这些基础变量类型都不过是Object的子类而已,学过面向对象编程的小伙伴应该都清楚,一个子类类型的对象非要定义成父类类型,那么肯定是可以从父类强转为子类的。所以这里直接以Object接收,安全又方便,毕竟不一定所有属性值我们都偏要设置为整型(嗯)。
6. dofilter()的机制
每个Servlet类都可以通过不同的urlPattern访问,其实在讲过滤器的时候已经给了一副过滤器的工作流程图,dofilter()其实就是根据urlPattern这个序列一个个过滤,这一关过了就去闯下一关这个意思。
7. 重定向/转发/包含
重定向redirect :服务器可以通过返回状态码为302的响应,告知浏览器应访问另外一个URL。
Servlet对象的service方法由容器调用,一个Servlet对象是不能直接调用另一个Servlet对象的service方法的。 有时Web应用在响应客户端请求时,需要多个Web组件共同协作,此时可使用请求转发或包含操作。
转发forward:一个Servlet(源组件)先对客户请求做一些预处理,然后把请求转发给其它Web组件(目标组件)来完成包括生成响应在内的后续操作。
包含: Servlet(源组件)把其它Web组件(目标组件)生成的响应结果包含到自身的响应结果中。
这是两种逻辑不太一样的处理方式,重定向下,如果Servlet处理不了当前请求,它会踢皮球让客户自己去找别的方法(url);转发包含下,如果首个接收消息的Servlet处理不了请求消息,那它会把这个请求提交给下一个Servlet继续尝试处理,直到转发到能完全处理这条请求的Servlet,再由这最终的Servlet把处理结果返回给客户。
从浏览器使用者的我们来看,两者的使用体验其实基本一样,这一个过程基本就是透明的,但是我还是举个栗子阐释一下:
我们重新来看addServlet这个类,我们目前是通过forward方式指示doneSevlet做计算并把结果返回给客户端的,这种形式就是那句
request.getRequestDispatcher("/doneServlet").forward(request,response);
通过F12查看请求响应,我们会发现response是直接返回来的,这就表明doneServlet这时候就是直接被抽调来给addServlet工作的,虽然表面上依然是addServlet帮我们处理业务,但实际上是doneServlet给我们的返回数据
重定向的话,把forward这句话改写为
response.sendRedirect(request.getContextPath()+"/doneServlet");
此时是先返回给浏览器一个302,指示浏览器去访问doneServlet,在url栏中,我们也可以看到重定向的结果
再次运行,alright,使用体验确实一点区别都没有,但浏览器获取信息的方式的确变了
我们再写个include的:把前面两句去掉,我们写个
request.getRequestDispatcher("/doneServlet").include(request,response);
out.println("没想到吧其实是我addServlet把消息返给你的");
include,会调用叶节点然后和自己的处理合并,再由自己返回回去。所以呢,最后的返回结果是这样的:
8. 正则匹配的规范
java里的正则匹配大家还是可以上网学一学,正则匹配这个东西无论校验格式还是网络爬虫都太重要了!
这里涉及一个规范,大佬告诉我Matcher m在find的时候尽量使用while循环,理由就是匹配项次数有时不一定只有一次,所以为了保证把所有东西都匹配上,就要使用while循环。
这个项目的知识点大概就这么多,本人web小白没接触过前后端的东西,初次上手Servlet,希望总结的这些对大家有帮助!
如何实现Servlet识别网址中的参数信息
如何实现Servlet识别网址中的参数信息?
URL的定义
那么现在将要求改为:从addServlet出发,如何实现:
1)首次请求url /addServlet?a=1, 由于未执行登录,则被重定向到登录页面
2)在登录页面输入用户名密码,当用户名密码正确时登录成功
3)再次请求url /addServlet?a=1,返回参数错误信息
4)请求url /addServlet?a=1&b=2,返回ab的和3(有doneServlet计算完成)
5)请求url /logoutServlet, 注销当前登录
6)请求url /addServlet?a=1&b=2,由于已注销,被重定向到登录页面
过滤器不必要修改
明白过滤器的工作原理话,过滤器其实不需要做任何修改。因为在urlPattern="/addServlet/*"的情况下,输入了addServlet之后的任何东西其实都会被视为符合Pattern,所以不管怎样依然会被过滤掉。因此过滤器并不需要做修改
addServlet如何获取到参数
看到了上图关于URI,URL的定义,我们可以大致了解到:URL是我们参数之前所有路径内容,URI则不包含服务器地址的信息。因此,对于http://localhost:8080/zuoye3_war_exploded/addServlet?a=1&b=2
来说,request.getRequestURL()返回全路径http://localhost:8080/zuoye3_war_exploded/addServlet
,request.getRequestURI()返回除去host(域名或者ip)部分的路径/zuoye3_war_exploded/addServlet
那么问题来了,现在两者都无法得到a=1&b=2这个字符串,那怎样获取它们呢?
我们不要忘了,一个URL的“?”后面内容的定义是“参数”
想到什么了吗?Parameter!Servlet的函数里面是有一个request.getParameter()函数的。不仅如此,当题目要求改成使用?定位a、b的值时,整个问题相当于更加简化了。
因此我们只需要把doGet()函数重写为:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
//参数的获取可以直接根据字符得到,a=1那么就获得"a"的值,b=2就获得"b"的值
String the_a = request.getParameter("a");
String the_b = request.getParameter("b");
//每个括号都代表一个组,因此a的值和b的整型值都可以由匹配结果转过来
Integer a = Integer.parseInt(the_a);
Integer b = Integer.parseInt(the_b);
request.getSession().setAttribute("a",a);
request.getSession().setAttribute("b",b);
request.getRequestDispatcher("/doneServlet").forward(request,response);
测试:改用参数方式启动计算器
在网上找了好久怎么匹配"?",原来还是自己太菜了,没想到正是URL的定义决定了Servlet有什么更多的神奇的函数。又一次希望总结的这些对大家有帮助!