在JavaEE的Servlet规范中,实现HTTP协议由“javax.servlet.http.HttpServlet类”实现。该类封装了一系列的请求和响应操作,使得开发者直接实现功能逻辑,而不必关乎网络连接、错误处理等。对于该类的众多API,仍需牢牢把握住请求和响应分别有三大组成部分,对相应的API进行分类记忆,十分清晰明了。
Table of Contents
Servlet的HTTP请求
获取客户机信息
获取请求行
获取请求头
获得实体内容(客户端提交的数据)
HSRequest是一个域对象
请求的包含&转发
Servlet的HTTP响应
设置响应行
重定向
设置响应头
页面乱码
设置响应体
向浏览器输出文本
向浏览器输出图片
通过本文,我将介绍使用频率最高的、请求与响应的三大组成部分的API用法与示例:
HTTP请求:javax.servlet.http.HttpServletRequest
- 获取请求行
- 获取请求头
- 获取实体内容
- 其他操作
HTTP响应:javax.servlet.http.HttpServletResponse
- 设置响应行
- 设置响应头
- 设置响应体
Servlet的HTTP请求
HTTP请求对象:
javax.servlet.http.HttpServletRequest对象,主要功能是获取请求信息。
获取客户机信息
- getRemoteAddr 方法返回发出请求的客户机的IP地址。
- getRemoteHost 方法返回发出请求的客户机的完整主机名。
- getRemotePort 方法返回客户机所使用的网络端口号。
- getLocalAddr 方法返回WEB服务器的IP地址。
- getLocalName 方法返回WEB服务器的主机名。
方法使用演示
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(
urlPatterns= {"/hello/http"},
name="ServletHTTP"
)
public class ServletHTTP extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String remoteAdd = req.getRemoteAddr(); //其他getXxxHost/Port等方法同理
String localAdd = req.getLocalAddr();
System.out.println("RemoteAddress"+remoteAdd+", LocalAddress"+localAdd);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
访问:http://localhost:8080/K-Servlet/hello/http
获取请求行
获取请求行的方法:
- getRequestURL 方法返回客户端发出请求时的完整URL。
- getRequestURI 方法返回请求行中的资源名部分。
- getQueryString 方法返回请求行中的参数部分。
- getPathInfo 方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Serv的路径之后和查询参数之前的内容,它以“/”开头。
- String getContextPath() web应用的名称
快速理解用法
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(
urlPatterns= {"/hello/http"},
name="ServletHTTP"
)
public class ServletHTTP extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
StringBuffer requestURL = req.getRequestURL();
String requestURI = req.getRequestURI();
String queryString = req.getQueryString();
String contextPath = req.getContextPath();
System.out.println("URL:"+requestURL+", URI:"+requestURI+", queryString"+queryString);
System.out.println("Context Path:"+contextPath);
//请求:http://localhost:8080/K-Servlet/hello/http?name=kyle
/*输出:
URL:http://localhost:8080/K-Servlet/hello/http, URI:/K-Servlet/hello/http, queryStringname=kyle
Context Path:/K-Servlet
*/
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
获取请求头
获取请求头:
- long getDateHeader(String name)
- String getHeader(String name) 根据KEY获取到Value部分
- Enumeration getHeaderNames() 获取所有请求头的KEY部分,返回枚举器
- Enumeration getHeaders(String name)
- int getIntHeader(String name)
快速理解用法
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(
urlPatterns= {"/hello/myhttp"},
name="ServletHTTP"
)
public class ServletHTTP extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Enumeration<String> enumeration = req.getHeaderNames();
while(enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = req.getHeader(key);
System.out.println("Key:"+key+" Value:"+value);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
//输入:http://localhost:8080/K-Servlet/hello/myhttp
/*输出:
Key:accept Value:image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/xaml+xml, application/x-ms-xbap,
Key:accept-language Value:zh-CN
Key:ua-cpu Value:AMD64
Key:accept-encoding Value:gzip, deflate
Key:user-agent Value:Mozilla/5.0 (Windows NT 6.2; Win64; x64; Trident/7.0; rv:11.0) like Gecko
Key:host Value:localhost:8080
Key:connection Value:Keep-Alive
Key:cookie Value:JSESSIONID=0940865D7181B9F84546B2B7C01EEA61
*/
获得实体内容(客户端提交的数据)
获得实体内容(客户端提交的数据)
- String getParameter(String name):name表示参数名,获取“参数”中的实体数据,即从用户提交上来的数据;
- String[] getParameterValues(String name):获取“参数”中的多个数据,一般是获取多选按钮的值;
- Enumeration getParameterNames():获取所有用户提交上来的所有数据;
- Map<String,String[]> getParameterMap()
获取实体数据
JSP:
<!-- HelloServlet.jsp -->
<%@ page language="java" import="java.util.Enumeration" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>KYLE</title>
</head>
<body>
<h2>Servlet获取实体域中的数据</h2>
<form action="<%=request.getContextPath()%>/hello/myhttp" method="post">
Username:<input type="text" name="username"><br/>
Password:<input type="password" name="password"><br/>
<input type="submit" value="提交"><br/>
</form>
</body>
</html>
Servlet:
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(
urlPatterns= {"/hello/myhttp"},
name="ServletHTTP"
)
public class ServletHTTP extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Enumeration<String> enumer = req.getParameterNames();
System.out.println("获取所有的参数:");
while(enumer.hasMoreElements()) {
String parameterName = enumer.nextElement(); //获取参数名
String parameterValue = req.getParameter(parameterName); //获取参数值
System.out.println("参数名:"+parameterName+" 值为:"+parameterValue);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
//请求:http://localhost:8080/K-Servlet/HelloServlet.jsp
/*输出:
获取所有的参数:
参数名:username 值为:kyle
参数名:password 值为:kkkk
*/
HSRequest是一个域对象
特性:
- request对象也是一个域对象
- request域的作用范围是一次请求中
方法:
- setAttribute(String name, Object o)
- getAttribute(String name)
- removeAttribute(String name)
注:具体实例在以后实际开发中再做展示。
请求的包含&转发
- 在Web程序中,经常是由多个Servlet来完成请求,需要在Servlet之间来回跳转。
- RequestDispatcher接口就是为了实现在多个Servlet之间来回跳转而设计的。
- 该接口由HttpServletRequest类的getRequestDispatcher()方法获得
- 该接口提供两种跳转方式:include()和forward()
- include():可以在当前的Servlet中显示另外一个Servlet的内容。
- forward():跳转到其他的Servlet。
请求转发的两个步骤:
- 获得请求转发器:RequestDispatcher rd = request.getRequestDispatcher(String path):path是转发的地址,要转发给谁
- 通过转发器对象转发:rd.forward/include(ServletRequest request, ServletResponse response)
include()包含
1) 写两个Servlet
2) 配置web.xml(也可以直接使用注解实现)
3) 在浏览器输入URL,查看控制台
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FirstServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
System.out.println("这是FirstServlet!");
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) {
doGet(request, response);
}
}
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 SecondServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("这是SecondServlet!");
RequestDispatcher rd = req.getRequestDispatcher("/first");
rd.include(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
web.xml
<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>hello.FirstServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/first</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>SecondServlet</servlet-name>
<servlet-class>hello.SecondServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SecondServlet</servlet-name>
<url-pattern>/second</url-pattern>
</servlet-mapping>
测试:
先输入:http://localhost:8080/K-Servlet/first
再输入:http://localhost:8080/K-Servlet/second
结果如下图:
forward()请求转发
javax.servlet.RequestDispatcher rd = req.getRequestDispatcher("/first");
rd.forward(req, resp);
另外一种写法:
request.getRequestDispatcher("/test.jsp").forward(request, response);
Servlet的HTTP响应
HTTP响应对象:
javax.servlet.http.HttpServletResponse,主要功能是设置响应信息。
设置响应行
向浏览器设置响应行:
- void setStatus(int sc); 给当前响应设置状态码
- void setError(int sc); 使用指定状态码发送一个错误到客户端(例如请求的地址不存在)
- 发送错误信息:response.sendError(HttpServletResponse.SC_NOT_FOUND, “你错了”);
- 响应状态码的常量:SC_NOT_FOUND 404;SC_OK 200;SC_INTERNAL_SERVER_ERROR 500;
重定向
在请求转发中:
- forward方法进行跳转后,浏览器中的地址依然还是老样子不变。
- 所以可以继续使用原来的request和response,可以把数据存入Request域对象中。
- 请求转发只发生在本个服务器的本个项目之中。
重定向(sendRedirect()方法)用于重定向到另外一个URL。可以重定向到任何地方。
response.sendRedirect("https://blog.hackyle.net"); //任意URL
转发与重定向的区别:
1)重定向两次请求,转发一次请求
2)重定向地址栏的地址变化,转发地址不变
3)重新定向可以访问外部网站 转发只能访问内部资源
4)转发的性能要优于重定向
设置响应头
设置响应头:
- addHeader(String name, String value)
- addIntHeader(String name, int value)
- addDateHeader(String name, long date)
- setHeader(String name, String value)
- setDateHeader(String name, long date)
- setIntHeader(String name, int value)
- boolean containsheader(java.lang.String name) 判断是否含有该响应头
- 注意:add系列的,如果遇到“name”重名的时候,value会自动叠加;set系列的,如果遇到“name”重名的时候,value会自动覆盖!
页面乱码
页面传输中文的乱码问题
- 原因:response缓冲区的默认编码是iso8859-1,此码表中没有中文;发送给浏览器后浏览器后,无论浏览器采用什么编码方式来解析,都是解析不出来来真正的内容。
- 解决方案:设置response的编码“setCharacterEncoding(String charset)”
如果发现客户端还是不能正常显示中文:
- 原因:我们将response缓冲区的编码设置成UTF-8,但浏览器的默认编码是本地系 统的编码,因为我们都是中文系统,所以客户端浏览器的默认编码是GBK,我们可以 手动修改浏览器的编码是UTF-8。
- 终极解决方案:在代码中指定浏览器解析页面的编码方式,通过response的setContentType(String type)方法指定页面解析时的编码是UTF-8:“response.setContentType("text/html;charset=UTF-8");”,不仅可以指定浏览器解析页面时的编码,同时也内含 setCharacterEncoding的功能;
- 所以在实际开发中只要编写:response.setContentType("text/html;charset=UTF-8");就可以解决页面输出中文乱码问题。
设置响应体
向浏览器(客户端)发送响应体:
- ServletOutputStream getOutputStream() 向浏览器输出非文本,例如输出图片等内容。通过该字节流的write(byte[] bytes)可以向response缓冲区中写入字节,在由Tomcat服务器将字节内容组成Http响应返回给浏览器。
- java.io.PrintWriter getWriter() 响应体设置文本,向浏览器输入文本。获得字符流,通过字符流的write(String s)方法可以将字符串设置到response 缓冲区中,随后Tomcat会将response缓冲区中的内容组装成Http响应返回给浏览器端。
向浏览器输出文本
向浏览器输出文本:getOutputStream、getWriter实现
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 HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//字符流实现
// response.setContentType("text/html;charset=UTF-8");
// PrintWriter pw = response.getWriter();
// pw.write("傻逼,中文");
//字节流实现
response.setHeader("Content-type", "text/html;charset=UTF-8");
String str = "我爱你";
byte[] byte_str = str.getBytes("UTF-8");
OutputStream output = response.getOutputStream();
output.write(byte_str); }
}
向浏览器输出图片
向浏览器输出图片
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
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 HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//显示图片
ServletOutputStream outStream = response.getOutputStream(); //使用response获得字节输出流
//获得服务器上的图片,图片组在WebContent目录下
String realPath = this.getServletContext().getRealPath("aa.jpg");
InputStream in = new FileInputStream(realPath);
int len = 0;
byte[] buffer = new byte[1024];
while((len=in.read(buffer))>0) {
outStream.write(buffer,0,len);
}
in.close();
outStream.close();
}
}
通过观察我对目录的设定,可以发现本篇文章的结构就是:
- 牢牢把握请求和响应的三大组成部分
- 所有HTTP的应用都是在这三个组成部分里面进行