服务器软件:可以接收用户的请求,处理请求,做出响应。
在web服务器软件中,可以部署web项目,让浏览器可以访问这些项目,中小型免费开源的JavaEE服务器,首选tomcat。
这里就不讲tomcat的安装,可以直接在官网下载安装。
一般将Tomcat集成到IDEA中,并且创建JavaEE的项目,部署项目。采用热部署可以有更好的使用体验。
Servlet
Servlet,可以拆分成server applet,概念上就是运行在服务器端的小程序。
实际上,Servlet是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
想要使用接口,用户就是要自定义实现类了。
自定义类的实现原理:
1.当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
2. 查找web.xml文件,是否有对应的<url-pattern>
标签体内容。
3. 如果有,则在找到对应的<servlet-class>
全类名
4. tomcat会将字节码文件加载进内存,并且创建其对象
5. 调用其方法
Servlet3.0以上就支持注解配置了,不需要在web.xml里面写一系列的代码了。只需要在自定义实现类上加上注解@WebServlet(“资源路径”)
那调用Servlet接口,然后复写里面的抽象方法,我们也总是只复写Service这一个方法,不方便。
这里就引出了一个HttpServlet类,它是Servlet类的实现类。
那我们现在就可以通过继承这个HttpServlet类来使用Servlet的方法了。
而且HttpServlet还有一个好处就是在服务器接收到请求的时候,提供了判断请求方式的方法,常用的就是doPost()和doGet()。我们就不用自己去判断是哪一种请求方式。
HTTP
Hyper Text Transfer Protocol 超文本传输协议
这种传输协议定义了客户端和服务器端通信时,发送数据的格式
特点
1.基于TCP/IP的高级协议
2. 默认端口号:80
3. 基于请求/响应模型的:一次请求对应一次响应
4. 无状态的:每次请求之间相互独立,不能交互数据
请求消息的数据格式
HTTP协议中的请求方式,常用的有GET和POST,先简单讲一下他们的区别:
GET
1.请求参数在请求行中,在url后。
2. 请求的url长度有限制的
3. 不太安全
POST
1.请求参数在请求体中
2. 请求的url长度没有限制的
3. 相对安全
请求信息的数据格式
请求消息:客户端发送给服务器端的数据
1.请求行:请求方式 请求url 请求协议/版本
2.请求头:客户端浏览器告诉服务器一些信息
3.请求空行:就是用于分割POST请求的请求头,和请求体的。
4.请求体(正文):封装POST请求消息的请求参数的
响应信息的数据格式
响应消息:服务器端发送给客户端的数据
1.响应行:协议/版本 响应状态码 状态码描述
响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。都是3位数字,分类如下:
1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx多状态码
2xx:成功。代表:200
3xx:重定向。代表:302(重定向),304(访问缓存)
4xx:客户端错误。 例如: 404(请求路径没有对应的资源) 405:请求方式没有对应的doXxx方法
5xx:服务器端错误。代表:500(服务器内部出现异常)
2.响应头
格式:头名称: 值
常见的响应头有以下两种
Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
++++++++++分割线+++++++++++++
Content-disposition:服务器告诉客户端以什么格式打开响应体数据
常用的值:
in-line:默认值,在当前页面内打开
attachment;filename=xxx:以附件形式打开响应体。文件下载
3.响应空行
4.响应体:传输的数据
Request
request对象和response对象的原理
1.request和response对象是由服务器创建的。我们来使用它们
2.request对象是来获取请求消息,response对象是来设置响应消息
request功能
1.获取请求消息数据
1.1 获取请求行数据
package demo;
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;
@WebServlet("/req_demo1")
public class Request_demo1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求行数据: GET /Servlet_DEMO/req_demo1?username=123456 HTTP/1.1
//获取请求方式:GET
String method = request.getMethod();
System.out.println(method);
//获取虚拟目录:/Servlet_DEMO
String contextPath = request.getContextPath();
System.out.println(contextPath);
//获取资源文件(Servlet)路径:/req_demo1
String servletPath = request.getServletPath();
System.out.println(servletPath);
//获取get方式的请求参数:username=123456
String queryString = request.getQueryString();
System.out.println(queryString);
//获取请求URI: /Servlet_DEMO/req_demo1
String requestURI = request.getRequestURI();
System.out.println(requestURI);
//获取请求URL:http://localhost/Servlet_DEMO:war exploded/req_demo1
StringBuffer requestURL = request.getRequestURL();
System.out.println(requestURL);
//获取协议及版本:HTTP/1.1
String protocol = request.getProtocol();
System.out.println(protocol);
//获取客户机的IP地址:
String remoteAddr = request.getRemoteAddr();
System.out.println(remoteAddr);
}
}
1.2 获取请求头数据
String getHeader(String name):通过请求头的名称获取请求头的值
Enumeration<String> getHeaderNames():获取所有的请求头名称
demo
package demo;
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.util.Enumeration;
@WebServlet("/request_demo2")
public class Request_demo2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取所有请求头名称
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String s = headerNames.nextElement();
String header = request.getHeader(s);
System.out.println(s+"----"+header);
}
}
}
package demo;
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.util.Enumeration;
@WebServlet("/request_demo3")
public class Request_demo3 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求头名称:user-agent
String header = request.getHeader("user-agent");
if(header.contains("Chrome")){
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("我是谷歌O ");
}
}
}
1.3 获取请求体数据
只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
1.获取流对象
BufferedReader getReader():获取字符输入流,只能操作字符数据
ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
2.再从流对象中拿数据
demo
package demo;
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.BufferedReader;
import java.io.IOException;
//获取请求体数据
@WebServlet("/request_demo4")
public class Request_demo4 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
BufferedReader reader = request.getReader();
String line=null;
while((line=reader.readLine())!=null){
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(line);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
2.其他功能
2.1 获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数
demo
package demo;
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.util.Enumeration;
import java.util.Map;
import java.util.Set;
/*
获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数
1. String getParameter(String name):根据参数名称获取参数值 username=zs&password=123
2. String[] getParameterValues(String name):根据参数名称获取参数值的数组 hobby=xx&hobby=game
3. Enumeration<String> getParameterNames():获取所有请求的参数名称
4. Map<String,String[]> getParameterMap():获取所有参数的map集合
*/
@WebServlet("/request_demo5")
public class Request_demo5 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//post中文乱码解决,设置字符编码为utf-8
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
String[] parameterValues = request.getParameterValues("hobby");
// for (String parameterValue : parameterValues) {
// System.out.println(parameterValue);
// }
Enumeration<String> parameterNames = request.getParameterNames();
// while(parameterNames.hasMoreElements()){
// String s = parameterNames.nextElement();
// String parameter = request.getParameter(s);
// System.out.println(s+"=="+parameter);
// }
Map<String, String[]> parameterMap = request.getParameterMap();
Set<String> strings = parameterMap.keySet();
for (String string : strings) {
String[] strings1 = parameterMap.get(string);
System.out.println(string);
for (String s : strings1) {
System.out.println(s);
}
System.out.println("---------");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
2.2 请求转发:一种在服务器内部的资源跳转方式
步骤
1.通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
2.使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response)
特点:
1.浏览器地址栏路径不发生变化
2.只能转发到当前服务器内部资源中。
3.转发是一次请求
2.3 共享数据
首先得说明一下域对象,之前我们说请求转发是一次请求,就是一次请求可以波及到多个Servlet,这个影响的范围就是request域。
域对象:一个有作用范围的对象,可以在范围内共享数据
request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
方法
1.void setAttribute(String name,Object obj):存储数据
2.Object getAttitude(String name):通过键获取值
3.void removeAttribute(String name):通过键移除键值对
Response
功能:设置响应消息
1.设置响应行
格式为:HTTP/1.1 200 ok
可以设置状态码:setStatus(int sc)
2.设置响应头
setHeader(String name, String value)
3.设置响应体
步骤:
1.获取输出流,有两种
字符输出流:PrintWriter getWriter()
字节输出流:ServletOutputStream getOutputStream()
2.使用输出流,将数据输出到客户端浏览器
重定向
一种资源跳转的方式
package demo;
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;
@WebServlet("/response_demo1")
public class Response_demo1 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo1被访问到了");
// //设置响应状态码为302,也就是重定向
// resp.setStatus(302);
// //设置重定向的页面
// resp.setHeader("location","/Servlet_Demo/response_demo2");
//重定向的简便写法
resp.sendRedirect("/Servlet_Demo/response_demo2");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}
response如果回复中文,会产生乱码
package demo;
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;
@WebServlet("/response_demo2")
public class Response_demo2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
resp.getWriter.write("demo2又被访问到了");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}
forward 和 redirect 区别
重定向的特点:redirect
- 地址栏发生变化
- 重定向可以访问其他站点(服务器)的资源
- 重定向是两次请求。不能使用request对象来共享数据
转发的特点:forward
- 转发地址栏路径不变
- 转发只能访问当前服务器下的资源
- 转发是一次请求,可以使用request对象来共享数据
SevletContext
代表整个web应用,可以和程序的容器(服务器)来通信
可以通过两种方法获取对象
1.通过request对象获取
request.getServletContext();
2.(推荐)通过HttpServlet获取
this.getServletContext();
SevletContext功能如下
1.获取MIME类型
MIME类型:在互联网通信过程中定义的一种文件数据类型
格式: 大类型/小类型
例如:text/html 或 image/jpeg
String getMimeType(String file)
在获取MIME类型后,我们就可以用来设置响应头类型
2.域对象:共享数据
这个跟request域的概念差不多,方法一样
但是ServletContext的范围更大,是所有用户所有请求的数据。request域的共享数据只在于一次请求的范围
1.setAttribute(String name,Object value)
2.getAttribute(String name)
3.removeAttribute(String name)
3.获取文件的真实(服务器)路径
方法:String getRealPath(String path)
String b = context.getRealPath("/b.txt");//web目录下资源访问
System.out.println(b);
String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
System.out.println(c);
String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问
System.out.println(a);
会话技术
会话:一次会话中包含多次请求和响应。
一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止
功能:在一次会话的范围内的多次请求间,共享数据
方式:
1.客户端会话技术:Cookie
2.服务器端会话技术:Session
Cookie
概念:客户端会话技术,将数据保存到客户端
使用步骤:
1.创建Cookie对象,绑定数据
new Cookie(String name, String value)
2.发送Cookie对象
response.addCookie(Cookie cookie)
3.获取Cookie,拿到数据
Cookie[] request.getCookies()
实现原理:基于响应头set-cookie和请求头cookie实现
服务器新建cookie对象,响应头发给客户端。
客户端收到cookie对象后,保存在客户端,之后的请求头会夹带cookie信息,所以服务器可以在请求头里拿cookie数据
cookie的细节
1.一次可不可以发送多个cookie?
可以创建多个Cookie对象,使用response调用多次addCookie方法发送cookie即可。
2.cookie在浏览器中保存多长时间?
默认情况下,当浏览器关闭后,Cookie数据被销毁
我们可以使用持久化存储:
setMaxAge(int seconds)
seconds为存活秒值
正数:将Cookie数据写到硬盘的文件中。持久化存储。并指定cookie存活时间,时间到后,cookie文件自动失效
负数:默认值
零:删除cookie信息
3.cookie能不能存中文?
(注意)在tomcat 8 之后,cookie支持中文数据。特殊字符还是不支持,建议使用URL编码存储,URL解码解析
URL编码:
String value=URLEncoder.encode(value,"utf-8");
URL解码:
String value=URLDecoder.decode(value,"utf-8");
4.cookie共享问题?
4.1 假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?
默认情况下cookie不能共享
setPath(String path):设置cookie的获取范围。默认情况下,设置当前的虚拟目录
如果要共享,则可以将path设置为"/"
4.2 不同的tomcat服务器间cookie共享问题?
setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie可以共享
setDomain(".baidu.com"),那么tieba.baidu.com和news.baidu.com中cookie可以共享
Cookie的特点和作用
特点
1.cookie存储数据在客户端浏览器
2.浏览器对於单个cookie 的大小有限制(4kb) 以及 对同一个域名下的总cookie数量也有限制(20个)
作用
1.cookie一般用于存储少量的不太敏感的数据
2.在不登录的情况下,完成服务器对客户端的身份识别
Session
服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。HttpSession
使用步骤:
1.获取HttpSession对象:
HttpSession session = request.getSession();
2.使用HttpSession对象:
Object getAttribute(String name)
void setAttribute(String name, Object value)
void removeAttribute(String name)
原理:Session的实现是依赖于Cookie的。
客户端请求头会请求在服务器获取session值,如果没获取到,那就创建一个新的session对象。如果获取到了,那么服务器会自动响应给客户端一个sessionID。
接下来客户端的请求头就会夹带这个sessionID去服务器内获取session值。
细节
1.当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
默认情况下。不是。
如果需要相同,则可以创建Cookie,键为JSESSIONID,设置最大存活时间,让cookie持久化保存。
Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60*60);
response.addCookie(c);
2.客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
不是同一个,但是要确保数据不丢失。tomcat自动完成以下工作
session的钝化:
在服务器正常关闭之前,将session对象系列化到硬盘上
session的活化:(IDEA无法完成此功能,所以在IDEA中服务器一旦关闭在重启就是新的session了)
在服务器启动后,将session文件转化为内存中的session对象即可。
3.session什么时候被销毁?
1.服务器关闭
2.session对象调用invalidate() 。
3.session默认失效时间 30分钟
选择性配置修改
30
session的特点
1.session用于存储一次会话的多次请求的数据,存在服务器端
2.session可以存储任意类型,任意大小的数据
session与Cookie的区别:
1.session存储数据在服务器端,Cookie在客户端
2.session没有数据大小限制,Cookie有
3.session数据安全,Cookie相对于不安全