1.IDEA常见问题
如何查看部署目录:
在Module servletRequest上右击----open Module settings----Artifacts
场景:在开发目录的web目录下新建了一个静态资源文件,访问该文件,404.
应当去部署目录去看一下,有没有这个文件。IDEA有时候可能会出现这种bug。
这个时候应该:
如果还是不行,那么可以采用下面这种方式
直接把部署目录里面的文件全部删了,然后再次rebuild project。
2.ServletRequest
其实就是对于请求报文的封装。
HttpServletRequest—子接口。
请求报文:
请求行:请求方法 请求资源 版本号
请求头
请求体
request对象中分别提供了哪些API,用来获取请求报文的各个部分。
RequestServlet:
package com.cskaoyan.request;
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("/request")
public class RequestServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//请求行:请求方法 请求资源 版本号
String method = request.getMethod();
//请求资源的获取有两种形式
String requestURI = request.getRequestURI();
String requestURL = request.getRequestURL().toString();
String protocol = request.getProtocol();
System.out.println(method);
// URI:统一资源标识符 URL:统一资源定位符
System.out.println(requestURI);
System.out.println(requestURL);
System.out.println(protocol);
//获取请求头
String host = request.getHeader("Host");
System.out.println(host);
//获取请求体
//request.getInputStream();
System.out.println("====================");
String remoteAddr = request.getRemoteAddr();
int remotePort = request.getRemotePort();
String localAddr = request.getLocalAddr();
int localPort = request.getLocalPort();
System.out.println(remoteAddr);
System.out.println(remotePort);
System.out.println(localAddr);
System.out.println(localPort);
}
}
2.1.其他比较有意思的API
除此之外,request还提供了一些其他比较有趣的API。
2.2.获取请求参数
Form.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--action就表示你需要向哪个地址发起一个请求-->
<!--submit一个表单一个,就构建一个请求报文,由于post请求方式,所以在请求体中,-->
<!--参数的组织形式是这样的:username=xx&password=xx&gender=xx&-->
<!--所以我们就可以通过name取到用户所输入的值-->
<form action="http://localhost:8080/servletRequest01_war_exploded/submit" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
性别:男<input type="radio" name="gender" value="male">
女<input type="radio" name="gender" value="female"><br>
爱好:java<input type="checkbox" name="hobby" value="java">
c++<input type="checkbox" name="hobby" value="c++">
python<input type="checkbox" name="hobby" value="python"><br>
简介<textarea name="description"></textarea><br>
<input type="submit">
</form>
</body>
</html>
SubmitServlet:
package com.cskaoyan.request;
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.Arrays;
@WebServlet("/submit")
public class SubmitServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// key就是form表单的name属性的值
String username = request.getParameter("username");
String password = request.getParameter("password");
String gender = request.getParameter("gender");
//只能获取到单个参数,如果想获取多个,可以使用另外一个API
String[] hobbies = request.getParameterValues("hobby");
//String hobby = request.getParameter("hobby");
String description = request.getParameter("description");
System.out.println(username);
System.out.println(password);
System.out.println(gender);
System.out.println(Arrays.toString(hobbies));
System.out.println(description);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
然后浏览器输入:
http://192.168.1.4:8080/servletRequest01_war_exploded/form.html,
填写表单后,服务器就能获取到对应的数据。
2.2.1.如果前端页面提交的参数非常多,这个时候怎么办
SubmitServlet02:
package com.cskaoyan.request;
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.Arrays;
import java.util.Enumeration;
/**
* @author shihao
* @create 2020-06-23 17:47
*/
@WebServlet("/submit2")
public class SubmitServlet02 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Enumeration<String> names = request.getParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String[] values = request.getParameterValues(name);
System.out.println(name + " " + Arrays.toString(values));
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
2.2.2.如果想把这些数据封装到一个对象中,应该怎么办
比如用户通过注册把这些数据上传到服务器了,接下来就应该把这些数据保存到数据库中,就需要写JDBC的那套代码,就需要把这些参数传给JDBC,一般传的时候会把这些数据封装成一个user对象,把这个user对象交给JDBC来处理。
Java Bean(就像一个豌豆荚,里面有豌豆)。就是表示一个对象。成员变量应当是private。然后提供相应的get和set方法。
第一种方式:利用反射。这个作为晚上作业。
SubmitServlet3:
package com.cskaoyan.request;
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.Arrays;
import java.util.Enumeration;
import java.util.Map;
@WebServlet("/submit3")
public class SubmitServlet3 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, String[]> parameterMap = request.getParameterMap();
//可不可以利用反射将这些参数封装到user对象中
//思路:首先提供一个user对象,拿到它所有的set方法
//parameterMap可不可以迭代出键值对name-value
// set + name的首字母大写, setXxxx
//set方法迭代,setXxx是否与某个set方法的名称一致,如果一致
//则调用method.invoke完成当前参数的赋值
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
第二种方式:导包。导包一定要注意:根据昨天我们讲的web的目录结构,lib目录一定要放在WEB-INF目录下。
package com.cskaoyan.request;
import com.cskaoyan.bean.User;
import org.apache.commons.beanutils.BeanUtils;
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;
import java.util.Map;
@WebServlet("/submit4")
public class SubmitServlet4 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, String[]> parameterMap = request.getParameterMap();
//EE阶段如何导包
//lib目录一定要放在WEB-INF目录下
User user = new User();
try {
//它是怎么实现的?反射
BeanUtils.populate(user, parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println(user);
//JDBC代码
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println(username);
}
}
2.3.中文乱码问题
在某个方法的首行打个断点,然后选择debug模式,浏览器访问:
http://localhost/servletRequest01_war_exploded/form.html,填写表单后,页面会处于阻塞状态,只有后台把断点放行后才能继续执行。
Step into表示进入方法内部。
step over表示下一步。
Resume program,继续程序,如果下面还有断点,会跳转到下一个断点,如果没有,就继续走完整个代码。
2.3.1.Post
解决办法:
Tomcat默认采用ISO-8859-1的编码格式。
这个API对于get请求有没有效果?
没有效果,因为这个API仅针对请求体。
因为get方法是请求行,没有请求体。(也就是说没有中文部分)
SubmitServlet4:
package com.cskaoyan.request;
import com.cskaoyan.bean.User;
import org.apache.commons.beanutils.BeanUtils;
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;
import java.util.Map;
@WebServlet("/submit4")
public class SubmitServlet4 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置utf-8是因为前端页面用的也是utf-8
request.setCharacterEncoding("utf-8");
Map<String, String[]> parameterMap = request.getParameterMap();
//EE阶段如何导包
//lib目录一定要放在WEB-INF目录下
User user = new User();
try {
//它是怎么实现的?反射
BeanUtils.populate(user, parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println(user);
//JDBC代码
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println(username);
}
}
2.3.2.Get
对于tomcat 8.5版本来说,没有乱码问题,默认采用的就是utf-8.
摘自http那节:
Get和post的区别
1: 请求参数
Get的参数, 放在url之后, 能放1k的内容
Post的参数, 一般放到正文里 没有限制大小
2: 安全
Get: 安全问题, 可以直接看到用户输入参数
Post: 别人无法直接观察到
3: 语义化的区别
Get: 一般用于获取数据
Post: 一般用于提交数据
有许多web浏览器不发送带有“content-type”头信息的字符编码限定符,而由读取http请求的代码来决定字符的编码方式。
默认情况下,如果客户端请求未定义编码限定符,容器(如tomcat)会用“ISO-8859-1”去创建request reader和解析POST的数据
注意:自从Tomcat5.x开始,GET和POST方法提交的信息,tomcat采用了不同的方式来处理编码,对于POST请求,Tomcat会仍然使用request.setCharacterEncoding方法所设置的编码来处理,如果未设置,则使用默认的iso-8859-1编码。
而GET请求则不同,Tomcat对于GET请求并不会考虑使用request.setCharacterEncoding方法设置的编码,而会永远使用iso-8859-1编码
2.4.form表单路径写法
1.全路径(学习前端时使用的路径写法)
http://localhost:8080/app/submit4
2.相对路径(提交的地址相对于当前页面的相对路径)
首先看一下你当前页面路径:
http://localhost:8080/app/form.html
你需要提交的路径
http://localhost:8080/app/submit4
所以相对路径写法就是如何从当前路径变成提交路径
submit4
3./应用名/资源名 路径写法
/app/submit4
第三种是推荐的方式。
取公司开发,会遇到三个环境:开发环境(本地)、测试环境(局域网内某个ip地址)、生产环境
所以第一种路径写法不是非常推荐。会涉及到路径的频繁变更。但是也并不是绝对不可以用,可以将这些以配置文件的形式配置下来。
第二种路径写法:提交的路径会很依赖于当前页面路径,比如当前页面路径发生变更,那么提交的路径就会跟着变更。
2.5.转发和包含
如果一个请求需要用到多个servlet共同来处理,怎么做?
为什么需要用到多个servlet来参与?代码全部写在一个servlet中不就可以了吗?
分工协作的思想。每一个模块只负责一个功能点,每个模块功能尽可能单一,那么它的
复用性就越好。
需求1:功能1、功能2、功能3
需求2:功能2、功能3、功能4
请求首先访问到servlet1,接下来请求转发或者包含servlet2,那么这两个组件之间有一个称呼,servlet1叫做源组件、servlet2叫做目标组件
路径写法:
1.全路径(不可行,因为路径只会被当做当前应用下的一个资源)
2.相对路径 ok
3./应用名/资源名
如果不加应用名呢?OK
以/开头的路径写法:有的地址需要加/应用名,有的不需要加,直接/资源名即可,那么有什么规律吗?
看执行主体是服务器还是浏览器(这个路径是给谁使用的),如果是服务器处理,那么就不需要加,如果是浏览器使用的,那么就需要加。
浏览器是不知道你的应用名叫啥,但是服务器很清楚的知道你的应用名叫啥。
也就是说转发和包含的时候,直接/资源名即可,因为转发和包含的时候是交给服务器处理的。首先调用request.getRequestDispatcher("/dispatcher2"),然后requestDispatcher.include(request, response),这个时候就是去告诉服务器去调用哪一个Servlet,让服务器找到这个Servlet,所以这个过程的执行主体就是服务器。
而form表单中的action是交给浏览器处理的,因为浏览器得知道你提交之后访问的是哪个路径才能发起一个请求报文,虽然最终是发给服务器的,但这个路径是给浏览器来处理的。
2.5.1.转发包含之间的区别
首先,两者都是用来介导多个servlet参与到一个请求中的,但是两者之间也存在很多差异。
转发:
源组件处理部分请求之后,接下来将请求交给目标组件全权处理,自己不再过多干预响应。
包含:
源组件将目标组件包含到自身中,然后一并做出响应。
看主动权的问题。代码上面的体现是什么样的呢?
对于转发来说,源组件转发过后,在最终的响应中只能留下响应头,无法留下响应体
但是包含来说,源组件包含过后,在最终的响应中不仅可以留下响应头,还可以留下响应体
使用场景:
转发一般用在servlet和页面之间(首先将请求跳转到servlet,比如到数据库中获取一些数据,接下来再转发给页面,由页面来显示结果)(转发用的多一点,比如登录,把用户名和密码传到服务端,服务端拿到用户名和密码后去校验,校验成功后跳转到一个页面)
包含一般用在页面和页面之间(比如淘宝下面这一栏,页面和页面之间一般是前端的东西)
转发:留头不留体
包含:留头也留体
转发:
DispatcherServlet1:(源组件)
package com.cskaoyan.dispatcher;
import javax.servlet.RequestDispatcher;
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("/dispatcher1")
public class DispatcherServlet1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//往响应头里面写数据
response.setHeader("content-type","text/html;charset=utf-8");
//往响应体里面写数据
response.getWriter().println("dispatcher1");
//输入目标组件的地址:1.全路径 2.相对路径 3.以/开头的路径写法
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/dispatcher2");
//转发是forward 包含是include
requestDispatcher.forward(request, response);
}
}
DispatcherServlet2:(目标组件)
package com.cskaoyan.dispatcher;
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("/dispatcher2")
public class DispatcherServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//往响应体里面写东西
response.getWriter().println("dispatcher2");
}
}
看PPT中请求转发的运行流程,会发现浏览器值发送了一次请求,接收到了一个响应。而服务器可能访问了两个servlet。这要和之后讲的重定向要有区分。
看包含:只需要把转发的代码中DispatcherServlet1 的代码中forward改成include即可。
2.6.request域
Context域说白了就是你能拿到一个context对象,这个context对象里都是键值对。
Context域的作用范围是整个应用,同一应用的任一servlet都能拿到这个context对象。
Request域说白了就是你能拿到一个request对象,这个request对象里都是键值对。
Request域的作用范围是转发包含中的源组件和目标组件。
域:共享数据的场所。context域。
如何共享呢?
context域,只要拿到的是同一个context对象,那么就可以共享同一个context域
request域,只要拿到的是同一个request对象,那么就可以共享同一个request域
哪些是同一个request?频繁刷新浏览器的地址,每次请求时一个request对象吗?
每次请求都会重新构建一个请求报文,每构建一个请求报文就会重新创建一个request对象。
转发包含的源组件和目标组件之间是同一个request对象(共享同一个request对象)。频繁刷新一个地址,会每次都创建一个新的request和response,没有任何关系。
而request域的API和context域的API一模一样。
DomainServlet1(源组件):
package com.cskaoyan.domain;
import javax.servlet.RequestDispatcher;
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("/domain1")
public class DomainServlet1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//request域
request.setAttribute("name", "zhangsan");
RequestDispatcher dispacher = request.getRequestDispatcher("domain2");
dispacher.forward(request, response);
}
}
DomainServlet2:(目标组件)
package com.cskaoyan.domain;
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("/domain2")
public class DomainServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = (String) request.getAttribute("name");
System.out.println(name);
}
}
这些知识点,比如转发、request域、jsp等,这些在前后端还未分离的时代是比较有用的,但是现在前后端分离,使用比较有限。
比如要显示出页面里面的这些数据
在jsp时代,可以这么做:
首先先访问到servlet,servlet去查询数据库,得到List集合信息,接下来,将list数据放入request域中,同时转发给jsp页面(本质来说就是servlet),jsp拿到数据以后,取出里面的数据,然后渲染出来。
前后端分离时代:
用户去请求前端的页面,然后又发起了一个请求,去请求数据,拿到list集合信息返回,接下来,前端页面完成数据的渲染。
以上红色部分表示我们后端程序员需要做的事,随着前后端分离,我们所做的事反而更少了。
转发最常用的使用方式就是搭配着request域一起使用,而现在jsp不怎么用了,相应的转发和request也不怎么用了。
下面来写一个简易的登录界面来感受一下:
loginServlet:
package com.cskoyan.login;
import javax.servlet.RequestDispatcher;
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;
/**
* @author shihao
* @create 2020-06-24 11:38
*/
@WebServlet("/login")
public class loginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if (username == null || "".equals(username)) {
response.getWriter().println("invalid parameter");
return;
}
if (password == null || "".equals(password)) {
response.getWriter().println("invalid parameter");
return;
}
//JDBC
if ("admin".equals(username) && "admin".equals(password)) {
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/info.html");
requestDispatcher.forward(request, response);
} else {
request.getRequestDispatcher("/error.html").forward(request, response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
Login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/servletRequest02_war_exploded/login" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="submit">
</form>
</body>
</html>
Info.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
welcome info
</body>
</html>
Error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
wrong username or password
</body>
</html>
1.ServletResponse
就是对于响应报文的封装。
HttpServletResponse----父子接口。
response就代表了将来的响应报文
响应报文:
响应行:版本、状态码(setStatus)、描述
响应头(response.setHeader(key,value))
响应体(response.getWriter().println())
响应体显示在浏览器正文里
ResponseServlet:
package com.cskaoyan.response;
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")
public class ResponseServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置响应报文 响应行
response.setStatus(404);
// 响应头
response.setHeader("content-type","text/html;charset=utf-8");
//响应体
response.getWriter().println("<h1 style='color:red'>File Not Found!!!</h1>");
}
}
1.1.输出数据到客户端
Response.getWriter().println
输出中文。
CharacterServlet:
乱码的原因去分析?
编解码不一致。
服务器构建响应报文的时候,中文用哪种编码格式?不清楚。ISO-8859-1。
响应报文到达客户端之后,客户端怎么去处理的?客户端使用的是GBK。
response.setCharacterEncoding(“utf-8”);
此时乱码发生了一些变化,乱码的原因在于客户端使用的不是utf-8,而是GBK。
只需要解决一个问题,怎么让客户端主动去使用utf-8?
也可以直接response.setCharacterEncoding(“GBK”);但是你这是瞎猫碰上死耗子,万一有的浏览器用的不是GBK呢?
1.1.1.乱码解决方案一
CharacterServlet:
package com.cskaoyan.response.character;
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("/char")
public class CharacterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
//将浏览器的解码格式设置为utf-8
response.getWriter().println("<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>Title</title>\n" +
"</head>\n" +
"<body>");
//前后是html页面的标准格式,中间写数据
response.getWriter().println("hello world");
response.getWriter().println("您好 中国!!!!");
response.getWriter().println("</body>\n" +
"</html>");
}
}
1.1.2.乱码解决方案二
CharacterServlet2:
package com.cskaoyan.response.character;
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("/char2")
public class CharacterServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//该响应头有两层含义:
//1.设置响应体的编码格式为utf-8
//2.发送content-type响应头,告诉浏览器使用哪种编码格式
response.setContentType("text/html;charset=utf-8");
response.getWriter().println("hello world");
response.getWriter().println("您好 中国!!!!");
}
}
1.1.3.乱码解决方案三(其实和方案二一样)
Response.setHeader(“content-type”,”text/html;charset=utf-8”)
为啥request需要只需要设置request.setCharacterEncoding(“utf-8”);即可,因为浏览器端的编码格式已经是utf-8了
只需要设置服务器端就好了。
1.2.输出字节数据
文件的传输。比如显示图片、比如显示html里面的内容。
显示WEB-INF目录下的1.html内容
StreamServlet:
package com.cskaoyan.response.stream;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@WebServlet("/stream")
public class StreamServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//将WEB-INF目录下1.html显示到页面
//本质上来说不就是IO流
ServletOutputStream os = response.getOutputStream();
String path = getServletContext().getRealPath("WEB-INF/1.html");
InputStream is = new FileInputStream(new File(path));
int length = 0;
byte[] bytes = new byte[1024];
while ((length = is.read(bytes)) != -1) {
os.write(bytes, 0, length);
}
//关闭流 request提供的输入流以及response提供的输出流可以关 也可以不关
//不关的话,tomcat也会在响应结束时给关闭
is.close();
}
}
作业:要求你自己实现一个当前应用下的Default servlet。
要求:这个servlet的url-pattern必须要为/,
介绍三个API:比如访问http://localhost:8080/app/api,得到的结果分别如下:
ApiServlet:
package com.cskaoyan.response.stream;
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("/api")
public class ApiServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
String servletPath = request.getServletPath();
String contextPath = request.getContextPath();
// /app/api 从应用名开始的一个完整路径
System.out.println(requestURI);
// /api 返回的是去掉应用名之外的一个资源路径
System.out.println(servletPath);
// /app 返回的是应用名
System.out.println(contextPath);
}
}
requestURI = contextPath + servletPath
1.3.定时刷新
refresh响应头
RefreshServlet:
package com.cskaoyan.response.refresh;
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.Date;
@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用方式一 每隔指定秒数刷新一下当前页面
response.getWriter().println(new Date());
//response.setHeader("refresh","1");
//使用方式二 后面会带着一个url,表示的是经过指定秒数之后,跳转至url
//response.setHeader("refresh","2;url=stream");
// /应用名/资源名路径写法
//response.setHeader("refresh","2;url=" + request.getContextPath() + "/stream");
//可以跳转至外部
response.setHeader("refresh","2;url=http://www.cskaoyan.com");
}
}
定时刷新也可以用来进行页面跳转。
1.4.重定向
访问某一个地址,服务器返回一个302或者307的状态码,表示需要重定向,响应头还包含一个Location头,指明了下次重定向的路径。
方式一:自己利用现有的API来实现。
RedirectServlet:
package com.cskaoyan.response.redirect;
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("/redirect")
public class RedirectServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setStatus(302);
response.setHeader("Location",request.getContextPath() + "/stream");
}
}
方式二,也可以利用服务器提供的api
RedirectServlet2:
package com.cskaoyan.response.redirect;
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("/redirect2")
public class RedirectServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// response.setStatus(302);
// response.setHeader("Location",request.getContextPath() + "/stream");
response.sendRedirect(request.getContextPath() + "/stream");
}
}
1.5.转发、定时刷新、重定向区别联系
共同点:都可以用来进行页面跳转
不同点:
1.转发只有一次请求;刷新和重定向发起两次请求
2.转发是request介导的;刷新和重定向是response介导的
3.转发可以共享request域;刷新和重定向不可以
4.转发只能应用内跳转;刷新和重定向没有限制
5.转发是服务器介导;刷新和重定向是浏览器介导(/开头路径写法不同)
6.重定向状态码302、307;其他是200
1.6.下载
比如导出数据到excel。最后一步会利用该API。
对于浏览器来说,浏览器可以打开的文件,默认会执行打开操作,对于无法打开的文件,默认会执行下载操作。
但是,对于那些可以打开的文件,如果不想让它打开,可以设置一个响应头,直接让文件下载下来。
DownloadServlet:
package com.cskaoyan.response.download;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//Content-Disposition这个内容怎么处置呢?
//attachment以附件的形式处置
response.setHeader("Content-Disposition","attachment;filename=1.jpg");
ServletOutputStream outputStream = response.getOutputStream();
String path = getServletContext().getRealPath("1.jpg");
FileInputStream fileInputStream = new FileInputStream(new File(path));
int length = 0;
byte[] bytes = new byte[1024];
while ((length = fileInputStream.read(bytes)) != -1){
outputStream.write(bytes, 0, length);
}
//关闭流 request提供的输入流以及response提供的输出流可以关 也可以不关
//不关的话,tomcat也会在响应结束时给关闭
fileInputStream.close();
}
}
2.html:
作业1: 实现将表单页面的数据都提交给服务器,然后在服务器端的servlet中保存。 PS:表单中要求可以输入中文字符。 (提示,可以新建一个对应的类保存所有数据,服务器端接受完毕后可以调用该类的toString方法输出里面的所有成员,通过response方法打印在浏览器页面上。可以尝试用反射动态获取表单的内容,并封装进java bean中。)
User:
package com.cskaoyan.response.Homework;
import java.util.Arrays;
public class User {
private String username;
private String password;
private String gender;
private String[] hobby;
private String description;
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 getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", gender='" + gender + '\'' +
", hobby=" + Arrays.toString(hobby) +
", description='" + description + '\'' +
'}';
}
}
BeanUtils:
package com.cskaoyan.response.Homework;
import com.sun.xml.internal.ws.util.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
public class BeanUtils {
private static final String PRE_FIX = "set";
/**
* 传入一个map,传入一个对象,将map中的键值对赋值给对象对应的属性
* map中的key的名称要与对象中对应的属性名称相同
* @param parameterMap
* @param user
*/
public static void toBean(Map<String, String[]> parameterMap, Object user) throws InvocationTargetException, IllegalAccessException {
Set<String> keySet = parameterMap.keySet();
Class<?> userClass = user.getClass();
for (String key : keySet) {
String[] values = parameterMap.get(key);
Field field = null;
Method method = null;
String simpleName = "";
try {
field = userClass.getDeclaredField(key);
//得到该参数的简单数据类型
simpleName = field.getType().getSimpleName();
//调用相应的set方法,比如setUsername,但是U是大写的 将字符串的首字母大写
method = userClass.getDeclaredMethod(PRE_FIX + StringUtils.capitalize(key), field.getType());
} catch (NoSuchFieldException | NoSuchMethodException e) {
e.printStackTrace();
}
if("String".equalsIgnoreCase(simpleName)){
method.invoke(user,values[0]);
}else if("int".equalsIgnoreCase(simpleName)){
method.invoke(user, Integer.parseInt(values[0]));
}else if("double".equalsIgnoreCase(simpleName)){
method.invoke(user, Double.parseDouble(values[0]));
}else if("String[]".equalsIgnoreCase(simpleName)){
//这个地方需要注意,要用object数组包装一下,否则会出现参数个数不匹配
//invoke接收object[]类型参数,会把数组中的每一个元素作为方法的一个参数
//如果直接传values过去,相当于传入多个参数,而用object数组包一下
//则相当于只传入一个参数,参数是一个数组
method.invoke(user,new Object[]{values});
}else{
throw new UnsupportedOperationException("暂不支持除了简单数据类型以外的封装");
}
}
}
}
HomeworkServlet01:
package com.cskaoyan.response.Homework;
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;
import java.util.Map;
/**
* @author shihao
* @create 2020-06-24 21:59
*/
@WebServlet("/HomeworkServlet01")
public class HomeworkServlet01 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, String[]> parameterMap = request.getParameterMap();
User user = new User();
try {
BeanUtils.toBean(parameterMap, user);
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
response.getWriter().println(user);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
作业2: 自己尝试实现一个简易版的Default Servlet,可以简单处理html页面,图片等。
一定一定要记住,将1.jpg、2.txt这些文件放在web目录下后,一定一定要Rebuild Project,否则不会导入到部署目录中!!!!!!!!!!!
package com.cskaoyan.response.Homework;
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.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author shihao
* @create 2020-06-24 17:52
*/
@WebServlet("/")
public class HomeworkServlet02 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
/**
* 首先DefaultServlet的作用要知道,处理静态资源文件,那么url-pattern就应当是/
* 本题的意图就是覆盖tomcat提供的默认静态处理servlet,然后自己加以实现简易版的
* 接下来,本次作业仅要求实现当前应用下的静态处理servlet即可,其他应用无需考虑
* 根据用户请求的url,知道用户所需要访问的是哪个静态资源
* 存在则以流的形式写出去,不存在则报404错误信息
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String servletPath = request.getServletPath();
String realPath = getServletContext().getRealPath(servletPath);
File file = new File(realPath);
if (file.exists()) {
FileInputStream fileInputStream = new FileInputStream(file);
byte[] bytes = new byte[1024];
int length = 0;
while ((length = fileInputStream.read(bytes)) != -1) {
response.getOutputStream().write(bytes, 0, length);
}
fileInputStream.close();
return;
}
response.setStatus(404);
response.getWriter().println("<h1>File Not Found</h1>");
}
}