08-web项目的基础设置
1、设置欢迎页面
在idea中创建好web项目之后,编写好相应代码以及配置,启动服务器后idea会自动启动我们设置的调试浏览器并转到http://localhost:8080/部署后的项目名称/,在idea中将项目创建为web项目时,会生成一个web目录,该目录中有WEB-INF目录,而且还生成了一个index.jsp文件在web目录下。
注意:我们知道这个web目录我们可以从项目的硬盘文件夹中将其复制到Tomcat的webapps目录下,就算是把这个项目部署好了,部署后的项目名称就叫web,当然可以改成其他名字。这针对的是不用IDE部署webapp的情况下部署项目,idea中该web目录只是一个标识,标识该项目要部署的所有东西都在web目录中,部署完之后服务器上该项目的名字可以在配置服务器时设置为真正的项目名。Myeclipes中是WebRoot目录,两者是一样的,只不过该IDE创建web项目不自动生成index.jsp文件。
在idea中点击Tomcat部署运行,就像上面说的,浏览器打开自动跳转到请求路径为 http://localhost:8080/部署后的项目名称/ 的页面,当我们没删除index.jsp时,上面的请求路径就会输出该资源文件。前面的测试中,又建了一个index.html文件,在其中编写了一个超链接,连接Servlet对象的执行结果资源,部署运行后浏览器自动输出的是index.html中的内容。
出现上面这种情况的原因是欢迎页面的设置,如果我们想项目部署后输入的请求路径只到项目名称后不加任何资源的名称代号路径能转到一个欢迎页面,我们可以在web.xml文件中添加欢迎页面的标签,设置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<!--这样的文件路径必须是在web目录下,前面不需要加/-->
<welcome-file>welcome.html</welcome-file>
<!--若在web目录下的html目录这样写-->
<welcome-file>html/welcome.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>servletcontext1</servlet-name>
<servlet-class>com.servlet.AServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletcontext1</servlet-name>
<url-pattern>/a</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>servletcontext2</servlet-name>
<servlet-class>com.servlet.BServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletcontext2</servlet-name>
<url-pattern>/b</url-pattern>
</servlet-mapping>
</web-app>
上面的这种转到欢迎页面的标签的优先级是从上往下下降的,还有就是为什么没有将web目录下的index.html或者index.jsp的路径添加到欢迎标签中却能将这些页面自动设置为欢迎页面,原因是Tomcat的conf目录中的web.xml中将index这个名字的html文件,jsp文件等设置为欢迎页面,且html文件在jsp文件上面,优先级高,同时存在index.html被设置为欢迎页面。
所以我们可以知道服务器tomcat中的web.xml是一个全局配置文件,而服务器上面部署的项目的web.xml是局部配置文件,而且我们还可以将一个Servlet对象的执行结果作为欢迎页面,就是说作为欢迎页面的种类是很多的,欢迎页面文件资源是在web目录下或者是WEB-INF下,所以路径不需要加部署后的项目名称。
2、http响应状态码
--webapp中常见的状态码:
-404
Not Found:资源未找到
【请求路径写错了,可能是浏览器上面,也可能是web.xml文件中绑定的写错了,是双向的】
-500
Server Inner Error:服务器内部错误【一般都是Java程序出现异常】
404和500都是HTTP协议状态码,这些状态号都是W3C制定的,所有浏览器和服务器都必须遵守
正常响应的HTTP协议状态码:200
在一些错误发生后我们想进行一些统一的处理,可以在web.xml文件中做一下配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<!--这样的文件路径必须是在web目录下,前面不需要加/-->
<welcome-file>welcome.html</welcome-file>
<!--若不在web目录下应这样写,html目录在web目录下-->
<welcome-file>html/welcome.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>servletcontext1</servlet-name>
<servlet-class>com.servlet.AServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletcontext1</servlet-name>
<url-pattern>/a</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>servletcontext2</servlet-name>
<servlet-class>com.servlet.BServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletcontext2</servlet-name>
<url-pattern>/b</url-pattern>
</servlet-mapping>
<!--下面路径不需要加部署的项目名,但要以/开头-->
<error-page>
<error-code>404</error-code>
<location>/error/error.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/error.html</location>
</error-page>
</web-app>
3、适配器(Adapter)模式和GenericServlet类
在前面的练习中,当web项目中有两个或两个以上的Servlet实现类时,而且每一个Servlet接口的实现类都需要实现Servlet接口中的所有方法。
但是大部分时候仅需要将需求代码写到service方法中,其他方法一直是空着的,这样看着很别扭,所以我们是否可以就创建一个抽象的实现Servlet接口的类,叫适配器类Adapter。
当我们的Servlet对象只需要使用到service方法而其他方法不需要时,
该适配器类就将service方法定义成抽象方法, 而其他方法在该适配器类中进行实现,
然后我们的项目类只需要继承这个适配器类,将其中的抽象方法实现,其他方法不需要就不用重写。代码页面简洁,实用,而且还可以在适配器类中添加其他方法。
这样的设计模式叫做适配器模式
3.1 设计模式的分类:
--创建型:解决对象的创建问题
--行为型:该模式与方法、行为、算法有关的设计模式
--结构型:更多类,更多的对象组合成更大的结构解决某个特定的问题
--Gof95(1995年,四人组提出的23种设计模式)
*单例模式
*工厂模式
*适配器模式
*迭代模式【集合】
*策略模式【集合】
*装饰器模式【IO流中】
...
--JavaEE模式
...
3.2 适配器举例
下面我们创建一个web项目,创建一个抽象适配器类GenericServlet--顾名思义:通用的Servlet。
但是这里有几个错误需要关注,因为本电脑上安装了JDK8和JDK11,在创建Java普通项目和创建一个Java空工程然后再在该工程中添加模块其中有些配置会有区别,但是都一定要注意Project SDK和Project language level版本要一致,如下图所示,1.8就要对应8
GenericServlet适配器类代码如下
package com.javaweb.servlet;
import javax.servlet.*;
import java.io.IOException;
/**
* GenericServlet是一个适配器类,这个适配器是一个Servlet
* 以后程序员无需实现Servlet接口,只需要继承GenericServlet抽象类,单单重写service方法就行了
*/
public abstract class GenericServlet implements Servlet {
private ServletConfig config;
@Override
//有时候我们希望在服务器启动阶段执行一段特殊的代码,如果我们在子类中重写init方法将特殊的代码写进去,那么里面
//的赋值语句就没法执行了,config是私有的就没法得到成员的ServletConfig对象,然而把定义代码和赋值代码搬到子类中
//显得有些多余,就不像适配器模式了,那么我们可以在适配器类中定义一个init的无参数方法,在适配器中的init有参数方法
//中去调用,在子类中覆盖init无参方法,将特殊程序写到重写的无参的init方法中
public void init(ServletConfig servletConfig) throws ServletException {
this.config = servletConfig;
this.init();
}
//定义一个无参的init方法,以防服务器启动阶段有特殊需求执行代码
/**
* 在初始化时刻需要执行一段特殊的程序,在子类中重写下面无参数的init方法
*/
public void init(){}
@Override
public ServletConfig getServletConfig() {
return config;
}
public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException;
@Override
//该方法是返回一段信息,该信息包含项目的作者、版本以及版权等
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
//-------------------------以下所有方法是扩展方法---------------------------
public ServletContext getServletContext(){
return config.getServletContext();
}
//.......
}
子类如下
package com.javaweb.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class HelloServlet extends GenericServlet{
//子类继承父类,将父类中的方法和变量都继承了,若没有重写父类的方法,子类对象调用某个方法时是执行父类中的
//方法代码,这可以在父类的方法中添加断点测试,若重写了父类的某个方法,子类对象调用该方法时执行的是重写后的
//方法代码,此时若想访问父类中的该方法,可以在重写的方法中用super.方法名的方式调用父类中的该方法。super表示
//子类对象中的父类型特征,不是一个父类型对象的引用
@Override
public void init() {
System.out.println("Hello init...");
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
servletResponse.setContentType("text/html;charset=UFT-8");
PrintWriter out = servletResponse.getWriter();
out.print("hello servlet!");
}
}
SUN公司已经写好了一个适配器抽象类也叫GenericServlet,完整类名:javax.servlet.GenericServlet。我们不用写,以后编程直接继承该类就可以了,并且SUN公司写的init方法解决法案与上面的例子一直,并且该适配器类还扩展了许多有用的方法。
4、http协议和get与post请求
4.1 安装请求和响应监视器(当前可忽略)
先安装HttpWatch软件,该软件可以对Http协议的监测,该软件只能在IE浏览器上运行监测,在IE浏览器上的打开步骤如下图,使用方法是点击Recored,然后运行网页,点击监测到的网页点击Stream模块
4.2 创建项目进行分析
创建一个普通的web项目,新建一个在html目录下的index.html文件,在web.xml文件中配置为欢迎页面。
其中编写一个输入用户名和密码的页面,但是不需要将输入的数据与底层数据库匹配判断,只是形式上输入。再写一个超链接,点击login不是将输入的用户名与密码到数据库匹配,而是跳转到连接请求,然后请求方式设为GET,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login page</title>
</head>
<body>
<!--下面中的连接是在form标签中输入相应用户名和密码跳转到某个页面-->
<!--这里输入用户名和密码只是个形式,不进行连接数据库匹配,只要点击登录login都能跳转-->
<!--这里只是test-->
<form action="/idea_servlet_http_05_war_exploded/system/hello" method="get">
<!--前面username是名字,输入内容是text类型,该内容是value,而key值是name中的username-->
username<input type="text" name="username"><br>
password<input type="text" name="password"><br>
<input type="submit" value="login">
</form>
</body>
</html>
创建HelloServlet类继承适配器抽象类GenericServlet,实现service方法。
部署项目,在IE浏览器中打开欢迎页面,同时启动监测,随便输入用户名和密码点击login
由于HttpWatch的版本与win10的IE浏览器版本不兼容,开启Recored后点击login无法打开网页,无法测试,将教学中的截图作为分析理解。下图中的左边是请求,右边是响应
GET请求截图:
POST请求截图
--1、HTTP协议的详细内容
1.1 什么是HTTP协议?
--该协议是一直超文本传输协议
--是浏览器和服务器之间的一种通讯协议
--由W3C制定,本质是一种提前制定好的数据传送格式。
服务器和浏览器必须遵从这种数据格式传送和发送数据
1.2 HTTP协议版本号
--上面截图的请求行中可以看到HTTP/1.1,就是HTTP协议的版本号
1.3 HTTP协议的几部分
--请求协议:从Browser发送到Server的数据格式
--响应协议:从Server发送到Browser的数据格式
1.4 请求协议
请求协议包括四部分:
--请求行:上两图的第一行是请求行
--消息报头:在请求行到空白行之间的内容就是消息报头
--空白行:GET请求截图中不明显,看POST请求中有一行空白,就是空白行
--请求体:如POST截图中的最后一行信息就是请求体,GET请求中请求体为空
由截图左部分可以看到请求行的信息由请求方式,请求路径(URL),和HTTP协议的版本号。
并且对比GET请求和POST请求,GET请求中发送的数据在请求行中,
就是说在请求路径中。POST请求发送的数据在请求体中。
空白行是专门用来分离消息报头和请求体的
1.5 响应协议
四部分
--状态行
--响应报头
--空白行
--响应体、正文
状态行:协议版本号、状态码、状态描述信息
空白行:分离响应报头和响应体
--该协议中重点掌握状态码:
200:响应成功正常结束
404:资源未找到
500:服务器内错误
...
--2、GET请求和POST请求的区别
2.1 什么情况下浏览器发送的请求是GET还是POST请求
使用表单form。将form标签中的method属性设置为method="post"
此时才是POST的请求方式,其余的所有请求都是基于GET方式的
2.2 GET与POST请求的区别
--GET请求是在请求行上提交数据
格式为:URL?name=value&name=value...
这种方式最终提交的数据会显示到浏览器的地址栏上
--POST请求是在请求体中提交数据,是一种相对安全的方式
格式为:name=value&name=value...
该方式最终提交的数据不会显示在浏览器的地址栏上
--POST请求在请求体中提交数据,该数据是没有长度限制的(可以提交大数据)
--GET请求在请求行上提交数据,所以请求的长度是有限制的
--GET请求只能提交字符串数据
--POST请求可以提交任何类型,如视频、图片等等,且这些文件必须用POST请求
--GET请求到的最终结果,会被浏览器收纳。而POST请求的最终结果不会被浏览器缓存
查看上面POST请求方式的监测截图
在其请求报头 Cache-Control:no-cache 表示不缓存请求结果。
原因是GET请求多数情况下是到服务器中读取资源,该资源短时间内不会发生改变,
所以请求到的结果被浏览器缓存起来了,下次再进行一模一样的该请求路径时效率就很高。
POST请求是为了修改服务器端的资源,修改后的结果都不会相同,也就没必要缓存到浏览器中。
2.3 GET与POST的选择
--有敏感数据必须使用POST
--数据不是字符串使用POST
--传送的数据非常多,使用POST
--请求是为了修改服务器中的资源使用POST
其余情况使用基于GET请求方式的请求
--注意:
GET请求后,浏览器将结果资源缓存,该缓存资源就与该路径绑定,
下一次该浏览器再次发送该请求时,这时浏览器就会在对应的缓存中寻找资源,
不再访问服务器,这样缓解了服务器的压力,提高了用户体验。
但是有些时候我们希望每次GET请求都访问服务器,可以在请求路径中添加时间戳,
例如:http://ip:port/oa/syatem/logout?timestamp=112121
JS获取毫秒:new Data().getTime();