侧边栏

老冯笔记Servlet

1.Tomcat服务器

1.1 介绍

软件分为B/S架构与C/S架构,而我们学习的是B/S架构

C/S(客户端/服务端)架构如:QQ、微信、抖音等

特点:
1、使用前必须安装。
2、更新的时候,服务器和客户端必须同时更新。
3、不能跨平台使用
4、客户端和服务器通信采用的是自有协议,相对来说比较安全。

B/S(浏览器/服务端)本质上也是C/S,只不过B/S架构的软件,使用浏览器作为客户端。B/S架构软件通过使用浏览器访问网页的形式,来使用软件。比如:淘宝、微博、百度等

特点:
1、软件不需要安装,直接访问指定的网址即可。
2、软件更新时客户端不需要更新。
3、软件可以跨平台,只要系统中有浏览器就可以使用。

常见的web应用服务器

Tomcat:Apache开源组织下的,免费

webLogic:是Oracle,收费

webSphere:是IBM的,收费

Nginx:后面要学习的,免费的,一般用于负载均衡

Apache:Apache开源组织下的,免费

web资源

​ 在web应用服务器上可以提供给外界访问的资源就是web资源,比如:html,css,图片,mp3,......

​ 动态资源:web页面中供人浏览的数据,由程序产生的,比如:jsp,php,Servlet

​ 静态资源:web页面中供人浏览的数据,不变的。比如:html,css,图片,mp3,......

image-20220831154109889

1.2 下载与安装Tomcat

Tomcat下载页面地址:

https://tomcat.apache.org/

Tomcat与JDK版本对应关系

image-20220831155314755

下载页的说明

image-20220831155710390

1.3 tomcat目录结构说明

image-20220831162426710

bin 存放二进制文件,脚本文件的目录,运行tomcat的脚本也在里面

​ startup.bat 启动tomcat的批处理文件

​ shutdown.bat 停止tomcat的批处理文件

conf 配置文件的目录

​ logging.properties 日志的配置文件

​ server.xml 核心配置文件

​ tomcat-users.xml 用户权限配置文件

​ web.xml 全局的项目默认配置文件,局部只对单个项目有效,全局对所有项目有效

lib tomcat依赖的jar以及tomcat提供的API存放的目录

logs 存放日志的目录

temp 临时文件存放的目录

webapps web应用发布的目录

work tomcat处理jsp的工作目录

1.4 启动tomcat

双击D:\apache-tomcat-8.5.82\bin\startup.bat,就会出现一个黑窗口,如果黑窗口出现闪退情况,证明启动失败。

启动成功后,你可以在浏览器上访问http://localhost:8080/,回出现下图效果

image-20220831163657867

启动出现闪退的常见原因:

​ 1.没有正确的配置JDK的环境变量,如果你没有配置JAVA_HOME这个环境变量,那么就会启动闪退

​ 2.端口冲突,tomcat默认使用的是8080端口,如果8080端口被其他的程序所占用,那么tomcat会启动失败

1.5 eclipse关联tomcat

image-20220831164904201

image-20220831165122373

image-20220831165310852

image-20220831165410210

image-20220831165503875

image-20220831165712092

image-20220831165856582

image-20220831170117986

2.HTTP协议

超文本传输协议(HTTP),它是一个简单的请求-响应协议,它通常运行在TCP之上,它是TCP/IP的子协议。

HTTP协议由HTTP请求和HTTP响应组成。

HTTP协议的特点:

1.基于请求与响应模型的协议,请求和响应是成对出现的。

2.此协议的默认端口为80。

HTTP请求包括:请求行,请求头,请求体

HTTP响应包括:响应行,响应头,响应体

HTTP协议的版本

HTTP/1.0 发送请求的时候,创建一次连接,获得一个web资源,短连接

HTTP/1.1 发送请求的时候,创建一次连接,获得多个web资源,长连接

当你通过域名访问网站的时候,其实就是你利用浏览器向网站所在服务器发送了一次请求

image-20220901143139447

image-20220901143212000

请求头的参数说明:

请求头是客户端发送给服务端的一些信息,这些信息是以key-value形式成对出现的

Accept:浏览器可以支持的MIME类型,文件类型的一种描述方式

Accept-Encoding:浏览器支持的数据压缩格式,这里是gzip

Accept-Language:浏览器支持的语言,zh-CN代表中文

Connection:连接状态,上图的keep-alive代表是连接中的,如果是close就是关闭状态

Content-Length:请求体的长度

Content-Type:请求数据的类型

Host:请求的服务端的主机名和端口

Origin:域名地址

Referer:当前请求来自何处,可以利用它做防盗链

Use-Agent:发送请求的浏览器得相关信息

当网站所在服务器接收到了客户端(浏览器)所发送的请求后,就会响应数据给客户端(浏览器)

image-20220901144906459

image-20220901144947400

3.HTTP常见状态码

状态码在请求行或者响应行中看,比如:HTTP/1.1 200 OK,这里的200就是状态机

​ 常见的状态码:

​ 200 请求成功

​ 302 请求重定向

​ 304 请求资源没有改变,访问本地缓存

​ 404 请求的资源找不到或者压根就不存在

​ 500 服务器内部错误,一般是代码出问题了

更详细的状态码,请参考:https://www.runoob.com/http/http-status-codes.html

image-20220901153443898

4.创建web工程

image-20220831174927287

image-20220831175045047

image-20220831175603237

image-20220831175700495

5.Servlet介绍

Servlet是运行在服务端的Java小程序,是Sun公司提供的一套规范(接口),用于处理客户端请求以及响应的动态资源

Servlet规范主要包含了三大技术点:

​ Servlet技术

​ Filter过滤器技术

​ Listener监听器技术

一般我们创建一个类然后继承HttpServlet,并覆盖doGet和doPost方法。

你在eclipse中创建了一个名叫Demo的web工程,然后你Run on Server后,项目就会被发布到Tomcat安装目录下的webapps目录里面。webapps目录里面的工程的结构:

Demo文件夹

​ META-INF文件夹

​ WEB-INF文件夹

​ classes文件夹(存放着你写的Java代码编译出来的字节码文件)

、 lib文件夹(存放第三方依赖Jar包的,放在这里的Jar会自动导入,不用build path了)

​ web.xml文件(配置Servlet的配置文件)

​ 一些静态资源......

注意:WEB-INF目录是一个受保护的目录。不能被外界直接访问

6.创建Servlet的方式

image-20220901155552020

image-20220901155651860

image-20220901155957603

7.设置Servlet的模板

image-20220901163303563

image-20220901163606127

模板代码

package ${enclosing_package};
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ${primary_type_name} extends HttpServlet {
    
    private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().write("DarkSnow...");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

你在新建完Servlet之后,清除掉原来的内容,然后输入servlet,再按alt + /即可生成代码

8.Servlet的生命周期(重要)

Servlet接口中的方法:

​ 1.public void init(ServletConfig config) 默认只执行一次

​ (1)什么时候执行?

​ Servlet对象创建的时候执行

​ (2)Servlet对象什么时候创建?

​ 默认第一次访问Servlet时创建

​ (3)ServletConfig代表的是该Servlet对象的配置信息(web.xml的信息)

​ 2.public void service(ServletRequest req, ServletResponse res)

​ (1)什么时候执行?

​ 每次请求的时候都会执行(每次访问的时候)

​ (2)ServletRequest

​ 代表请求对象,内部封装了HTTP请求的数据信息

​ (3)ServletResponse

​ 代表响应对象,内部封装了HTTP响应的数据信息

​ 3.public void destroy()

​ (1)什么时候执行?

​ Servlet销毁的时候执行

​ (2)Servlet什么时候销毁?

​ 服务器停止时销毁

image-20220902155454562

image-20220902160957989

9.ServletConfig对象

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>Demo</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <description></description>
    <display-name>MyServletSoft</display-name>
    <servlet-name>MyServletSoft</servlet-name>
    <servlet-class>com.darksnow.MyServletSoft</servlet-class>
    <init-param>
    	<param-name>url</param-name>
    	<param-value>jdbc:mysql://test</param-value>
    </init-param>
    <init-param>
    	<param-name>name</param-name>
    	<param-value>darksnow</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>MyServletSoft</servlet-name>
    <url-pattern>/darksnow</url-pattern>
  </servlet-mapping>
</web-app>

我们在public void init(ServletConfig config)中能够看到有一个ServletConfig对象,此对象是Tomcat帮你创建的,所以你可以直接用。

package com.darksnow;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.Enumeration;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyServletSoft extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;
	
	@Override
	public void destroy() {
	}
	
	@Override
	public void init(ServletConfig config) throws ServletException {
		//ServletConfig对象是Servlet配置对象
		
		//获取了web.xml中的<servlet-name>MyServletSoft</servlet-name>
		String servletName = config.getServletName();
		System.out.println(servletName);
		
		//根据Servlet的初始化参数name(<param-name>url</param-name>)来获取对应value
		String initParameter = config.getInitParameter("url");
		System.out.println(initParameter);
		
		//获取Servlet的所有初始化参数
		Enumeration<String> initParameterNames = config.getInitParameterNames();
		while(initParameterNames.hasMoreElements()) {
			String key = initParameterNames.nextElement();
			String value = config.getInitParameter(key);
			System.out.println(key + "---" + value);
		}
		
		//通过ServletConfig可以获得ServletContext对象,也叫Servlet上下文对象
		ServletContext servletContext = config.getServletContext();
	}
	
	@Override
	public ServletConfig getServletConfig() {
		return null;
	}
	
	@Override
	public String getServletInfo() {
		return null;
	}

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().write("DarkSnow...");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

10.web.xml的配置

<!-- 
	欢迎页面的设置  
	假设我访问http://localhost:8080/Demo/
	如果Demo项目里面有下面所示名称的文件,那么就可以有页面显示
	否则报404
 -->
<welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
</welcome-file-list>
Servlet默认第一次访问时创建,但是加上下面这个配置,Servlet对象就会在Tomcat服务器启动时就创建。
数字代表优先级,数字越小优先级越高!
注意:该配置项写在<init-param>配置后面,如果没有配置<init-param>,则写在<servlet-class>配置后面

<load-on-startup>2</load-on-startup>

11.ServletContext

ServletContext也叫做上下文对象。
    
如何获取ServletContext对象?
	1.通过ServletConfig对象获取(具体参考ServletConfig的笔记)
	2.通过this.getServletContext();的方式获取,
	主要是因为你写的Servlet会从GenericServlet将getServletContext方法给继承过来。
	本质上还是通过ServletConfig对象调用的。

注意:如果你是通过第二种方式获取ServletContext对象,要不你就不重写init方法,要不你就重写不带参数的init方法,如果你要从写init(ServletConfig config)方法,那么在你的方法体中的第一行一定要写上super.init(config); 否则会报空指针异常
	
ServletContext的作用:
(1)可以获取web应用的全局初始化参数,在web.xml中配置(此配置可以写在web-app标签内部的任意子位置)
<context-param>
  	<param-name>driver</param-name>
  	<param-value>com.mysql.jdbc.Driver</param-value>
</context-param>

// 获取ServletContext上下文对象
ServletContext servletContext = this.getServletContext();
// 获取全局的初始化参数
String value = servletContext.getInitParameter("driver");
System.out.println(value);

context-param与init-param的区别:
context-param中设置的初始化值可以被所有的Servlet获取
init-param中设置的初始化值只能被单个Servlet获取
(2)可以获取web应用中任何资源的绝对路径
    getRealPath()里面传入的参数为你要获取的资源的相对路径
    相对于该web应用

image-20220905160949247

(3)ServletContext的域对象
	存储数据的区域对象

ServletContext域对象的作用范围:
    作用于整个Web应用,也就意味着这个web应用下任何的Servlet都可以向ServletContext域中存取数据,以达到一个数据共享的效果。

相关操作:

package com.darksnow;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ServletOne extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext servletContext = this.getServletContext();
		//向ServletContext域中存储key-value(键值对)形式的数据
		servletContext.setAttribute("name", "Mark");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
package com.darksnow;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ServletTwo extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext servletContext = this.getServletContext();
		//通过key来获取ServletContext域对象中存储的值
		String value = (String) servletContext.getAttribute("name");
		response.getWriter().write(value);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
package com.darksnow;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ServletThree extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext servletContext = this.getServletContext();
		//通过key来移除ServletContext域对象中存储的值
		servletContext.removeAttribute("name");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

12.response响应对象

12.1 设置响应的状态码

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//设置状态码
	response.setStatus(501);
	response.getWriter().write("DarkSnow...");
}    

image-20220905172818065

(2)设置响应头
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//添加了一个响应头
		//response.addIntHeader("count", 1314520);
		//response.addDateHeader("MyDate", new Date().getTime());
		
		//修改响应头
		//response.setIntHeader("", 0);
		//response.setDateHeader("", 0L);
		
		//上面的只是给你用来体验的,下面才是真正你要用的
		response.addHeader("MyServer", "Tomcat");
		response.setHeader("Content-Length", "19");
		
		//总结:带add是添加,带set的是修改
}

image-20220905174133520

定时页面跳转

package com.darksnow.api;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class RefreshServlet extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//设置刷新时间的头 里面的5代表5秒
		response.setHeader("refresh", "5;url=http://www.baidu.com");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
	window.onload = function(){
		var time = 4;
		var secondElement = document.getElementById("second");
		var timer = setInterval(function(){
			secondElement.innerHTML = time;
			--time;
			//如果时间为0了,定时器任务就应该停止
			if(time == 0){
				clearInterval(timer);
				location.href = "http://www.baidu.com";
			}
		},1000);
	}
</script>
</head>
<body>
	恭喜您,注册成功,<span style="color:red" id="second">5</span>秒后跳转,如果不跳转点击<a href="http://www.baidu.com">这里</a></body>
</html>

12.2 可以做重定向

//加上访问MyServletOne重定向到MyServletTwo
public class MyServletOne extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().write("one...");
		
		//1.设置状态码为302
			//response.setStatus(302);
		//2.设置响应头Location(此响应头的值的格式:/项目名/资源名)
			//response.setHeader("Location", "/response_demo/two");
		
		//HttpServletResponse对象中内部封装了一个用于重定向的方法  项目名+资源名
		response.sendRedirect("/response_demo/two");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

public class MyServletTwo extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().write("two...");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

重定向的特点:
    (1) 请求了两次
    (2) 地址栏发生了变化

image-20220905180237077

12.3 页面内容打印

package com.darksnow.api;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TextServlet extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		/*
		 * 设置response缓冲区的编码 写在第一行
		 * 但是我们发现设置了也还是会乱码,因为我们的系统是中文环境的,所以浏览器默认用的是GB2312编码
		 * 所以我们不用此方法
		 */
		//response.setCharacterEncoding("UTF-8");
		
		//我们可以通过Content-Type响应头来告诉客户端我们到底使用什么编码
		//response.setHeader("Content-Type", "text/html;charset=UTF-8");
		
		//最终解决方案:Servlet给你提供了相关API(原理和设置响应头一样)
		response.setContentType("text/html;charset=UTF-8");
		
		//向页面写一个内容
        //这里本质上用的是I/O流
		response.getWriter().write("<h1>DarkSnow...</h1>"); //显示正常
		
		/*
		 * 中文乱码原因:
		 * 		内容其实在response缓冲区内,但是这个缓冲区的默认编码为ISO-8859-1
		 * 		然而这编码表中没有中文,所以就乱码了。
		 */
		response.getWriter().write("真热啊..."); //乱码
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

12.4 getOutputStream方法案例

package com.darksnow.api;

import java.io.FileInputStream;
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
 * 写一张图片
 */
public class ImgServlet extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		//这是一个字节流,该字节流可以向response缓冲区写入字节,再由tomcat引擎将字节的内容与其他信息封装成HTTP响应返回给浏览器
		ServletOutputStream out = response.getOutputStream();
		
		//首先得获取服务器上的图片路径  --->  绝对路径
		String realPath = this.getServletContext().getRealPath("22.png");
		FileInputStream in = new FileInputStream(realPath);
		
		int len = 0;
		byte[] buffer = new byte[1024];
		while((len = in.read(buffer))>0) {
			out.write(buffer,0,len);
		}
		
		in.close();
		
		//查看response缓存区的大小
		System.out.println(response.getBufferSize()); //8192Byte  1KB=1024Byte
		
		//设置response缓冲区的大小
		//response.setBufferSize(10240);
		
		/*
		 * 关于getOutputStream的相关注意事项
		 * 1.response获取的流不用手动关闭,tomcat会去关闭
		 * 2.getOutputStream方法与getWriter方法不能同时调用
		 * 3.response缓冲区默认大小为8KB,它存在自动扩容机制,无需你关心
		 */
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

12.5 文件下载案例

前端

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>欢迎来到资源下载小站</h1>
	<a href="/response_demo/download/apache-tomcat-8.5.82.zip">下载Tomcat源码</a>
	<br>
	<a href="/response_demo/download/girl.png">下载美女图片</a>
	<br>
	<a href="/response_demo/download/1.txt">下载文档</a>
	<br>
	<a href="/response_demo/download?filename=girl.png">真正的下载美女图片一</a>
	<br>
	<a href="/response_demo/download?filename=美女.jpeg">真正的下载美女图片二</a>
	<br>
	<a href="/response_demo/download?filename=1.txt">真正的下载文档</a>
</body>
</html>

后端

package com.darksnow.download;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DownloadServlet extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//获取要下载的文件的名称
		String filename = request.getParameter("filename");
		
		//要下载的文件的类型  客户端通过文件的MIME类型去区分文件类型(Tomcat安装目录的conf/web.xml里面去看)
		response.setContentType(this.getServletContext().getMimeType(filename));
		
		//获取要下载的文件的绝对路径
		String path = this.getServletContext().getRealPath("download/" + filename);
		
		//解决文件名中文乱码问题,不同浏览器解决方式还不一样
		//怎么去根据不同的浏览器设置不同的解决方案呢?怎么去区分浏览器,根据浏览器的内核来区分
		//获取请求头中的User-Agent
		String agent = request.getHeader("User-Agent");
		//根据不同的浏览器进行不同的编码
		String filenameEncoder = "";
		if(agent.contains("AppleWebKit")){
			//适用于谷歌浏览器和微软的Edge浏览器
			filenameEncoder = URLEncoder.encode(filename,"UTF-8");
		}else {
			//适用于火狐浏览器
			filenameEncoder = new String(filename.getBytes("GBK"),"ISO-8858-1");
		}
		
		//你要告诉客户端,文件不是直接取解析,而是以附件的形式打开(下载)
		response.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoder);
		
		//获得该文件的输入流
		InputStream in = new FileInputStream(path);
		//获得文件输出流 --- 通过response获得输出流
		ServletOutputStream out = response.getOutputStream();
		
		//边读边写
		int len = 0;
		byte[] buffer = new byte[1024];
		while((len = in.read(buffer)) > 0) {
			out.write(buffer,0,len);
		}
		
		//关闭资源
		in.close();		
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

12.6 图片验证码案例

servlet

package com.darksnow.check;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 验证码生成程序
 */
public class CheckImgServlet extends HttpServlet {

	// 集合中保存所有成语
	private List<String> words = new ArrayList<String>();

	@Override
	public void init() throws ServletException {
		// 初始化阶段,读取new_words.txt
		// web工程中读取 文件,必须使用绝对磁盘路径
        // new_words.txt存放在webapp目录下,此文件的内容为一行一个四字成语
		String path = getServletContext().getRealPath("new_words.txt");
		try {
			BufferedReader reader = new BufferedReader(new FileReader(path));
			String line;
			while ((line = reader.readLine()) != null) {
				words.add(line);
			}
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 禁止缓存
		// response.setHeader("Cache-Control", "no-cache");
		// response.setHeader("Pragma", "no-cache");
		// response.setDateHeader("Expires", -1);

		int width = 120;
		int height = 30;

		// 步骤一 绘制一张内存中图片
		BufferedImage bufferedImage = new BufferedImage(width, height,
				BufferedImage.TYPE_INT_RGB);

		// 步骤二 图片绘制背景颜色 ---通过绘图对象
		Graphics graphics = bufferedImage.getGraphics();// 得到画图对象 --- 画笔
		// 绘制任何图形之前 都必须指定一个颜色
		graphics.setColor(getRandColor(200, 250));
		graphics.fillRect(0, 0, width, height);

		// 步骤三 绘制边框
		graphics.setColor(Color.WHITE);
		graphics.drawRect(0, 0, width - 1, height - 1);

		// 步骤四 四个随机数字
		Graphics2D graphics2d = (Graphics2D) graphics;
		// 设置输出字体
		graphics2d.setFont(new Font("宋体", Font.BOLD, 18));

		Random random = new Random();// 生成随机数
		int index = random.nextInt(words.size());  
		String word = words.get(index);// 获得成语
		System.out.println(word);

		// 定义x坐标
		int x = 10;
		for (int i = 0; i < word.length(); i++) {
			// 随机颜色
			graphics2d.setColor(new Color(20 + random.nextInt(110), 20 + random
					.nextInt(110), 20 + random.nextInt(110)));
			// 旋转 -30 --- 30度
			int jiaodu = random.nextInt(60) - 30;
			// 换算弧度
			double theta = jiaodu * Math.PI / 180;

			// 获得字母数字
			char c = word.charAt(i);

			// 将c 输出到图片
			graphics2d.rotate(theta, x, 20);
			graphics2d.drawString(String.valueOf(c), x, 20);
			graphics2d.rotate(-theta, x, 20);
			x += 30;
		}

		// 将验证码内容保存session
		request.getSession().setAttribute("checkcode_session", word);

		// 步骤五 绘制干扰线
		graphics.setColor(getRandColor(160, 200));
		int x1;
		int x2;
		int y1;
		int y2;
		for (int i = 0; i < 30; i++) {
			x1 = random.nextInt(width);
			x2 = random.nextInt(12);
			y1 = random.nextInt(height);
			y2 = random.nextInt(12);
			graphics.drawLine(x1, y1, x1 + x2, x2 + y2);
		}

		// 将上面图片输出到浏览器 ImageIO
		graphics.dispose();// 释放资源
		
		//将图片写到response.getOutputStream()中
		ImageIO.write(bufferedImage, "jpg", response.getOutputStream());

	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

	/**
	 * 取其某一范围的color
	 * 
	 * @param fc
	 *            int 范围参数1
	 * @param bc
	 *            int 范围参数2
	 * @return Color
	 */
	private Color getRandColor(int fc, int bc) {
		// 取其随机颜色
		Random random = new Random();
		if (fc > 255) {
			fc = 255;
		}
		if (bc > 255) {
			bc = 255;
		}
		int r = fc + random.nextInt(bc - fc);
		int g = fc + random.nextInt(bc - fc);
		int b = fc + random.nextInt(bc - fc);
		return new Color(r, g, b);
	}
}

前端页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
	function checkImg(obj){
		obj.src = "/response_demo/checkImg?time=" + new Date().getTime();
		console.log(obj);
	}
</script>
</head>
<body>
	<form action="#" method="post">
		账号:<input type="text" name="username"><br>
		密码:<input type="password" name="password"><br>
		验证:<input type="text" name="check">
		<!-- 这里的this(当前对象)其实就是img这个文档对象 -->
		<img onclick="checkImg(this)" src="/response_demo/checkImg"><br>
		<input type="submit" value="登录">
	</form>
</body>
</html>

12.7 登陆案例

需求

做一个登录功能,输入账号密码登录,如果登录成功(验证数据库),那么就在页面显示"恭喜xxx登陆成功,这是您第x次成功登录!",如果登录失败,那么就在页面上显示"抱歉您的账号或密码错误"

前端页面login.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="/response_demo/login" method="post">
		账号:<input type="text" name="username"><br>
		密码:<input type="password" name="password"><br>
		<input type="submit" value="登录">
	</form>
</body>
</html>

C3P0工具类

package com.darksnow.utils;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
 * C3P0的相关工具类 
 * 
 * 你可以把数据源看成是连接池
 */
public class C3P0Util {
	//私有构造函数,不让其创建对象
	private C3P0Util() {
		
	}
	
	
	/*
	 * 这里ComboPooledDataSource的构造函数传入了一个"darksnow"的字符串参数,那么就会去使用c3p0-config.xml配置文件中的
	 * <named-config name="darksnow">下面的配置
	 * 
	 * 如果ComboPooledDataSource用的是无参构造,那么就会去使用c3p0-config.xml配置文件中的
	 * <default-config>下面的配置
	 */
	// private static ComboPooledDataSource dataSource = new ComboPooledDataSource("darksnow");
	private static ComboPooledDataSource dataSource = new ComboPooledDataSource();   //设置数据源
	
	/**
	 * javax.sql.DataSource 是Java提供的原生接口,各大厂商实现连接池需要实现这个接口
	 * 获取数据源的方法
	 */
	public static DataSource getDataSource() {
		return dataSource;
	}
	
	/**
	 * 获取连接的方法
	 */
	public static Connection getConnection() {
		try {
			return dataSource.getConnection();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
}

User实体类

package com.darksnow.bean;

/**
 * 这就是JavaBean  对象实体
 * 此对象的成员变量名和数据库的字段名是一一对应的,数据类型也是对应的
 * 
 * 一个实体对象基本上对应一条数据
 */
public class User {
	private int id;
	private String username;
	private String password;
	private String name;
	
	public User() {
		
	}
	
	public User(int id, String username, String password, String name) {
		this.id = id;
		this.username = username;
		this.password = password;
		this.name = name;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", username=" + username + ", password=" + password + ", name=" + name + "]";
	}
}

具体处理业务的Servlet

package com.darksnow.login;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Properties;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import com.darksnow.bean.User;
import com.darksnow.utils.C3P0Util;

public class LoginServlet extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;
	
	@Override
	public void init() throws ServletException {
		try {
			//加载count.properties文件得到一个字节输入流
			InputStream in = LoginServlet.class.getClassLoader().getResourceAsStream("count.properties");
			//创建Properties对象
			Properties properties = new Properties();
			//通过字节输入流将count.properties文件的内容加载到Properties对象中
			properties.load(in);
			//通过key从Properties对象中获取对应的值
			int count = Integer.parseInt(properties.getProperty("count"));
			//创建ServletContext上下文对象,并且向域中存入登陆次数
			this.getServletContext().setAttribute("count", count);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
    
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//处理response缓冲区中文乱码问题
		response.setContentType("text/html;charset=UTF-8");
		
		//登录页面输入完账号密码,然后点击登录以后,就将账号密码数据提交给了LoginServlet来处理
		//所以我们首先需要获取,前端页面提交过来的数据(账号密码)
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		
		//去数据库中校验账号密码是否正确
		QueryRunner queryRunner = new QueryRunner(C3P0Util.getDataSource());
		String sql = "select * from t_user where username=? and password=?";
		
		try {
			User user = queryRunner.query(sql, new BeanHandler<User>(User.class),username,password);
			
			//根据SQL的返回结果来显示不同的提示信息
			if(user != null) {
				//User对象不为空意味着账号密码正确
				//创建ServletContext上下文对象
				ServletContext servletContext = this.getServletContext();
				//获取ServletContext域中已经存在的次数
				int count = (int) servletContext.getAttribute("count");
				//登陆成功,登陆此数加一
				count++;
				response.getWriter().write("<h1>恭喜" + user.getName() +  "登陆成功,这是您第" + count + "次成功登录!</h1>" );
				//将更新后的次数写回ServletContext域中
				servletContext.setAttribute("count", count);
			}else {
				response.getWriter().write("<h1 style='color:red'>抱歉~您的账号或密码错误~</h1>");
			}
		}catch (SQLException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void destroy() {
		try {
			//tomcat服务器停止会执行此方法,所以在这里将ServletContext域中存储的值写入到count.properties中
			//通过key从ServletContext域中获取对应的值
			String count = String.valueOf(this.getServletContext().getAttribute("count"));
			//创建Properties对象
			Properties properties = new Properties();
			//给Properties对象设置key,value
			properties.setProperty("count", count);
			//将Properties对象的内容写入到count.properties文件中
			properties.store(new FileOutputStream(LoginServlet.class.getClassLoader().getResource("count.properties").getPath()), count);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

c3p0-config.xml配置文件,放在src/main/java下

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

	<default-config>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///darksnow</property>
		<property name="user">root</property>
		<property name="password">root</property>
		<property name="initialPoolSize">5</property>
		<property name="maxPoolSize">20</property>
	</default-config>

	<named-config name="darksnow">
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///darksnow</property>
		<property name="user">root</property>
		<property name="password">root</property>
	</named-config>
</c3p0-config>

count.properties配置文件,放在src/main/java下

count=0

数据库截图

image-20220907121751466

13.request请求对象

13.1 请求与响应图解

image-20220908150157579

13.2 获取请求行的内容

form.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="/request_demo/line" method="post">
		<input type="text" name="username"><br>
		<input type="password" name="password"><br>
		<button>登陆</button>
	</form>
</body>
</html>

servlet代码

package com.darksnow.api;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LineServlet extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//request对象获取请求的方式
		String method = request.getMethod();
		System.out.println("method:" + method);
		
		//request对象获取请求的资源相关的内容
		String requestURI = request.getRequestURI();
		StringBuffer requestURL = request.getRequestURL();
		System.out.println("uri:" + requestURI); //  /request_demo/line
		System.out.println("url:" + requestURL);	 //  http://localhost:8080/request_demo/line
	
		//request对象获取web应用的名称,也就是你的工程名(格式:/web应用名) 在JavaWeb中常用
		String contextPath = request.getContextPath();
		System.out.println(contextPath);
		
		//request对象获取地址后的参数的字符串,get请求方式就可以获取到,post会获取到null
		String queryString = request.getQueryString();
		System.out.println(queryString);
		
		//request对象获取客户端的信息,获取访问者的IP地址,但是此API与请求行无关。
		String ip = request.getRemoteAddr();
		System.out.println(ip);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

13.3 获取请求头的内容

referer_test.html前端

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<a href="/request_demo/header">访问HeaderServlet资源</a>
</body>
</html>

servlet代码

package com.darksnow.api;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HeaderServlet extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//request获取指定的请求头
		String header = request.getHeader("User-Agent");
		System.out.println(header);
		
		System.out.println("----------------------");
		
		//获得所有的请求头的名称
		Enumeration<String> headerNames = request.getHeaderNames();
		while(headerNames.hasMoreElements()){
			String headerName = headerNames.nextElement();
			String headerNameValues = request.getHeader(headerName);
			System.out.println(headerName  + ": " +headerNameValues);
		}
		
		System.out.println("----------------------");
		
		/*
		 * 请求头referer的作用:能够让你知道此次访问的来源
		 * 点击referer_test.html前端页面中的a标签可以看到请求头中包含
		 * Referer: http://localhost:8080/request_demo/referer_test.html
		 * 
		 * 这个头可以做防盗链
		 */
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

13.4 防盗链实现

news.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>DarkSnow热搜新闻网~</title>
</head>
<body>
	<a href="/request_demo/referer">独家新闻:震惊,某一明显竟然在酒店做出这样的事儿!</a>
</body>
</html>

servlet代码

package com.darksnow.exp;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class RefererServlet extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8");
		//对该新闻的来源进行判断
		String header = request.getHeader("Referer");
		if(header != null && header.contains("http://192.168.0.62:8080")) {
			response.getWriter().write("看到独家新闻是不是点进来了?其实明星也没干啥,老营销号了~");
		}else {
			response.getWriter().write("哼~ 盗链狗,可耻!!!");
		}
		
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

13.5 获取请求参数

前端代码

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="/request_demo/content" method="post">
		账号:<input type="text" name="username"><br>
		密码:<input type="password" name="password"><br>
		爱好:<input type="checkbox" name="hobby" value="zq">足球
		<input type="checkbox" name="hobby" value="lq">篮球
		<input type="checkbox" name="hobby" value="ppq">乒乓球
		<br><button>登陆</button>
	</form>
</body>
</html>

servlet代码

package com.darksnow.api;

import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ContentServlet extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//获取单个表单值
		String username = request.getParameter("username");
		System.out.println("账号:" + username);
		System.out.println("-----------------------");
		
		//获取所有的请求参数的名称
		Enumeration<String> parameterNames = request.getParameterNames();
		while(parameterNames.hasMoreElements()) {
			System.out.println(parameterNames.nextElement());
		}
		System.out.println("-----------------------");
		
		//获取所有请求参数的名称与其对应的值
		Map<String, String[]> parameterMap = request.getParameterMap();
		Set<Entry<String, String[]>> entrySet = parameterMap.entrySet();
		for(Entry<String, String[]> entry : entrySet) {
			String key = entry.getKey();  //请求参数的名称
			String[] value = entry.getValue(); //请求参数对应的值
			System.out.println(key + "---" + Arrays.toString(value));
		}
		System.out.println("-----------------------");
		
		//以上操作对post请求和get请求都有效
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

13.6 请求转发

图解

image-20220908174510685

ServletOne代码

package com.darksnow.exp;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ServletOne extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		/*
		 * 前面我们学习了ServletContext域对象,现在我告诉你,request对象其实也是一个域对象
		 * 所以它具备了同样的方法:
		 * 		向域以key-value的形式存储数据
		 * 			request.setAttribute(String name,Object obj);
		 * 		通过key从域中获取相应的value
		 * 			request.getAttribute(String name);
		 * 		通过key将域中相应的key-value数据给移除调
		 * 			request.removeAttribute(String name);
		 * 
		 * request域的作用范围只在一次请求中有效
		 * 
		 * 因此,一般request域和请求转发搭配使用,先设置域,再转发。
		 * (本质上我们这么玩儿就是为了将一个Servlet中的数据带到另外一个Servlet中,且保持一次请求)
		 */
		request.setAttribute("name", "darksnow");
		
		//转发在内部发生的,不需要写项目名,直接写资源就行了。 设置转发到哪里去!
		RequestDispatcher requestDispatcher = request.getRequestDispatcher("/two");
		//调用转发方法
		requestDispatcher.forward(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

ServletTwo代码

package com.darksnow.exp;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ServletTwo extends HttpServlet {

	private static final long serialVersionUID = 6468244120721891168L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8");
		String value = (String) request.getAttribute("name");
		response.getWriter().write("ServletTwo执行了..." + value);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

14.session和cookie

posted @   lkjlwq  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css
点击右上角即可分享
微信分享提示