作者
:学Java的冬瓜
博客主页
:☀冬瓜的主页
专栏
:【JavaEE】
分享
:寂寞会发慌,孤独是饱满的。——史铁生《命若琴弦》
主要内容
:HttpServlet的方法,init,service,destroy,doGet/doPost/doPut/doDelete…,servlet的生命周期、1>使用地址栏输入URL发送get请求,2>使用postman发送http请求,3>使用ajax发送http请求,4>使用JavaScript发送body为json格式的post请求。HttpServletRequest的相关方法的使用,前后端交互的方式,1>get请求,query string 传参,2> post请求,使用form表单传参,3> post请求,body使用json格式传参,req.getParameter(String name),req.getInputStream()。
HttpServlet方法 | 调用时机 |
---|---|
init | 在HttpServlet被实例化之后被调用一次 |
destroy | 在HttpServlet不再使用的时候调用一次,具体的不使用时机下面会讨论 |
service | 收到Http请求的时候调用 |
doGet | 收到 Get请求的时候,由service方法调用 |
doPost | 收到Post请求的时候,由service调用 |
doPut / doDelete / doOptions… | 收到对应方法的时候,由service方法对应调用 |
1> 首次请求时,创建servlet,执行init方法。在第一次访问时,servlet容器中会创建对应的servlet类的实例,每个servlet类只有一个实例,但一个servlet容器可以包含多个servlet实例对应多个servlet类。
2> 每次收到请求,都会执行 service方法,通过service方法去调用请求对应的方法。
3> 在servlet销毁前,调用destroy方法。
注意:一个servlet程序可以包含多个servlet,单个的servlet的生死并不影响整个servlet程序。
以下三种方式复用这同一份服务器端的servlet代码:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/method")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Get");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Post");
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Delete");
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Put");
}
}
前端不传数据给后端:此时Content-Length=0,Content-Type未指定
另外使用一个html文件来写ajax,从而构造各种请求,发送给后端。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>构造ajax请求访问服务器title>
head>
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js">script>
<script>
$.ajax({
type: 'post',
url: 'method', //使用相对路径
success: function(body){
console.log(body) //此处的body即是HTTP响应报文里的body
}
})
script>
body>
html>
127.0.0.1:8080/servletHello2/method
进阶:前端需要使用json格式传数据给后端,后端对请求进行处理:此时Content-Length != 0,Content-Type指定为 application/json 格式
客户端中,使用 testJS.html来构造body为json格式的post请求,在JavaScript中使用XMLHttpRequest对象来发送http请求。(使用ajax构造body为json格式的post请求后面再讲)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript构造body为json格式的post请求title>
head>
<body>
<script>
// 1.构造body对象
let body = {
username: "zhangsan",
password: "123"
}
// 2.将body对象转化为json字符串
let jsonBody = JSON.stringify(body);
// 3.获取XMLHttpRequest对象,在ajax中使用XMLHttpRequest对象来发送http请求
// 然后设置请求头的方式,设置请求的body为 json格式。
let xhr = new XMLHttpRequest();
xhr.open('post', 'getParameter2', true); //设置请求方法和请求地址
xhr.setRequestHeader('Content-type','application/json; charset=utf-8'); //设置数据格式
// 4.设置回调函数
xhr.onreadystatechange = function(){
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200){
// 请求成功,设置响应的打印到浏览器的数据
let response = JSON.parse(xhr.responseText);
console.log(response);
}
};
// 5.发送请求
let date = xhr.send(jsonBody);
script>
body>
html>
在后端服务器中,使用GetParameterServlet2.java来处理前端使用JavaScript构造body为json格式的post请求。
注意:由于 JavaScript构造的body为json格式的post请求可能格式上与后端的jackson不匹配,所以可能会出问题。我这里就直接使用InputStream把post的整个body取出来,然后把他写回浏览器中。
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
@WebServlet("/getParameter2")
public class GetParameterServlet2 extends HttpServlet {
//获取post请求,且body的类型为json的数据
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取postman构造的post请求报文的header中描述 body长度的字段Content-Length,根据字段创建缓冲数组
int length = req.getContentLength();
byte[] buffer = new byte[length];
// 2.获取InputStream对象,并且将body的内容读入buffer中。
InputStream inputStream = req.getInputStream();
inputStream.read(buffer);
// 3.把字符数组转换成字符串,在服务器控制台打印字符串且也在浏览器上(postman)打印
String body = new String(buffer,0,length,"utf-8");
System.out.println("body = " + body);
resp.getWriter().write(body);
}
}
响应发回testJS.html回调函数这里时,在客户端的控制台打印body。因为地址栏上的地址没有变成
127.0.0.1:8080/servlet/getParameter2
,所以使用XMLHttpRequest对象send发送请求,是请求转发。
HttpServletRequest方法 | 描述 |
---|---|
String getProtocol() | 返回协议的名称和版本 |
String getMethod() |
返回 http方法 请求的名称 |
String getRequestURI() | 返回URL的一部分,即从从URL开头到查询字符串之前的部分 |
String getContextPath() |
返回URL的一部分,即从开头到第二级路径servlet Path之前的部分,也就是URL从开头到项目根目录的部分 |
String getQueryString() | 返回包含在路径后的请求 URL 中的查询字符串 |
Enumeration getParameterNames() | 获取queryString里(get请求地址栏上)的键,返回一个 String 对象的枚举,包含在该请求中包含的参数的名称。 |
String getParameter(String name) |
queryString(get请求地址栏上)根据键获取值,以字符串形式返回请求参数的值,或者如果参数不存在则返回null。 |
String[] getParameterValues(String name) | 返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回 null。 |
Enumeration getHeaderNames() | 返回一个枚举,包含在该请求中包含的所有的头名。 |
String getHeader(String name) |
以字符串形式返回指定的请求头的值。 |
String getCharacterEncoding() | 返回请求主体中使用的字符编码的名称。 |
String getContentType() | 返回请求主体的 MIME 类型,如果不知道类型则返回 null。 |
int getContentLength() | 以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回 -1。 |
InputStream getInputStream() |
用于读取请求的 body 内容. 返回一个 InputStream 对象。 |
getParameter(String name)
方法可以获取地址栏输入地址时, get请求的 query String的键值对中的 值,以字符串的方式返回。getHeader(String name)
方法与上述两个方法相似。getInputStream()
,获取输入流对象,通过这个输入流对象,就可以把body的内容读出来。1> 列表中前五个方法的使用:
@WebServlet("/showRequest")
public class ShowRequest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 需要设置ContextType为text/html,下面的代码才有效
resp.setContentType("text/html; charset=utf-8");
// 使用StringBuilder,把内容拼接在一起,然后写回resp的body中
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(req.getProtocol());
stringBuilder.append("
");
stringBuilder.append(req.getMethod());
stringBuilder.append("
");
stringBuilder.append(req.getRequestURI());
stringBuilder.append("
");
stringBuilder.append(req.getContextPath());
stringBuilder.append("
");
stringBuilder.append(req.getQueryString());
// 把内容写入resp
resp.getWriter().write(stringBuilder.toString());
}
}
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("/showRequest")
public class ShowRequest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 需要设置ContextType为text/html,下面的html代码才有效
resp.setContentType("text/html; charset=utf-8");
// 使用StringBuilder,把内容拼接在一起,然后写回resp的body中
StringBuilder stringBuilder = new StringBuilder();
// 获取header里面的键headerName(使用getHeaderNames())和值headerValue(使用getHeader(键))
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()){
// 获取枚举中的header每个键值对的的键
String headerName = headerNames.nextElement();
// 获取枚举中的header每个键值对的的值
String headerValue = req.getHeader(headerName);
stringBuilder.append(headerName+":"+headerValue);
stringBuilder.append("
");
}
resp.getWriter().write(stringBuilder.toString());
}
}
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("/getParameter")
public class GetParameterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 地址栏路径:/getParameter?user=lisi&age=20
String user = req.getParameter("user");
String age = req.getParameter("age");
resp.setContentType("text/html; charset=utf-8");
resp.getWriter().write("user:"+user+" age:"+age);
}
}
test.html中使用form表单构造http请求,get/post/put…
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>前端给后端传参方式title>
head>
<body>
<form action="getParameter" method="get">
<span>username:span><input type="text" name="username">
<br>
<span>password:span><input type="password" name="password">
<br>
<input type="submit" value="submit">
form>
body>
html>
后端服务器收到form表单的请求后,根据action属性的url定位到GetParameterServlet.java,然后执行对应的get/post/put…方法,然后使用req.getParameter获取到前端发送过来的数据。
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("/getParameter")
public class GetParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 地址栏路径:/getParameter
String username = req.getParameter("username");
String password = req.getParameter("password");
resp.setContentType("text/html; charset=utf-8");
resp.getWriter().write("username:"+username+" password:"+password);
}
}
第一种方式:直接获取到body:
postman作为浏览器发送post请求,经过服务器处理,在服务器控制台打印这个post请求的body,且原样返回了post请求中body的json格式的数据,打印到postman(浏览器)上。如下图:
代码如下:
客户端利用postman发送body为json格式的post请求数据,服务器端处理post请求的代码 :GetParameterServlet2.java
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
@WebServlet("/getParameter2")
public class GetParameterServlet2 extends HttpServlet {
//获取post请求,且body的类型为json的数据
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取postman构造的post请求报文的header中描述 body长度的字段Content-Length,根据长度字段创建缓冲数组
int length = req.getContentLength();
byte[] buffer = new byte[length];
// 2.获取InputStream对象,并且将body的内容读入buffer中。
InputStream inputStream = req.getInputStream();
inputStream.read(buffer);
// 3.把字符数组转换成字符串,在服务器控制台打印字符串且也在浏览器上(postman)打印
String body = new String(buffer,0,length,"utf-8");
System.out.println("body = " + body);
resp.getWriter().write(body);
}
}
第二种方式:使用jackson将body键值对封装成java对象
使用postman构造http的post请求,然后这个post请求的body使用json格式,服务器需要对json格式的 post请求的body进行处理。
使用jackson把json格式的键值对封装成Java对象,从而对Java对象的属性可以做访问操作。
注意:
- 应该将创建的User类的成员变量进行封装,提供get和set方法,这里为了方便没有封装,而是直接将成员变量名设置为public。
- body中的键值对个数和Java对象成员变量不匹配的影响:
1> 如果post请求中的body的键值对个数 小于等于 服务器中的Java对象成员变量个数,但是内容是匹配的(键和成员变量名),那么post中未传键值对对应 的Java对象的成员变量值为null;
2> 如果post请求键值对个数等于 服务器中Java对象成员变量的个数,但是post请求的body中键名和 后端中的java对象的成员变量名不同,那就是500错误,或者post请求的键值对个数 大于 服务器端的Java对象的成员变量个数,也是500错误。
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
// 使用jackson时,封装一个类,用于对应post请求的body的 json类型的键值对数据,即使用jackson把json格式的键值对封装成Java对象。
class User {
public String username;
public String password;
}
@WebServlet("/getParameter2")
public class GetParameterServlet2 extends HttpServlet {
//获取post请求,且body的类型为json的数据
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 使用jackson涉及到的核心对象:ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
// 把传入的数据 body的json类型键值对数据封装成一个类,.readValue()方法就是读取post请求中的数据
// 然后根据第二个参数的属性依次填入对象,得到一个Java对象。得到Java对象后,属性值就随便取了。
User user = (User) objectMapper.readValue(req.getInputStream(), User.class);//需要强制类型转换一下,因为返回的是一个object对象。
// 把获取到的内容改变格式 到浏览器输出
resp.getWriter().write("username=" + user.username + " password=" + user.password);
}
}
req.getParameter(String name)
可以获取 非form表单和form表单 get请求的 query string;也可以获取 form表单中post请求 的body中的键值对根据键获得值。req.getInputStream()
可以读取body中的内容,但是只能完整读取整个body,想要获取json格式的请求的body的键值对,可以引入第三方库,可以使用jackson
。sendRedirect(String location)
方法是重定向,第一次请求时,服务器会给浏览器返回一个临时重定向状态码302,然后浏览器重新发送请求 访问 location。setStatus(int sc)
方法设置响应的 header的 status为302,setHeader(String name,String value)
方法 设置location为目标访问地址。setStatus(int sc)
方法,使用setStatus(404),则浏览器显示一个默认的404页面;加上resp.getWriter().write("404")
,则显示我们后端写的这个错误页面;使用resp.setError(404),则显示通常访问是404的页面,这个页面和浏览器有关。HttpServletResponse方法 | 描述 |
---|---|
void setStatus(int sc) | 为该响应设置状态码。 |
void setHeader(String name,String value) | 在响应的header中添加键值对,如果 name已经存在,则覆盖旧的值 |
void addHeader(Stringname, String value) | 在响应的header末尾添加键值对,添加一个带有给定的名称和值的 header. 如果 name已经存在,不覆盖旧的值, 并列添加新的键值对 |
void setContentType(String type) |
设置被发送到客户端的响应的内容类型 |
void setCharacterEncoding(String charset) | 设置被发送到客户端的响应的字符编码(MIME 字符集)例如,UTF-8。 |
void sendRedirect(String location) | 使用指定的重定向位置 URL 发送临时重定向响应到客户端。 |
PrintWriter getWriter() |
用于往 body 中写入文本格式数据 |
OutputStream getOutputStream() |
用于往 body 中写入二进制格式数据. |