在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请求

servletrequest修改parameter_ide

HTTP请求对象:

javax.servlet.http.HttpServletRequest对象,主要功能是获取请求信息。

获取客户机信息

  1. getRemoteAddr 方法返回发出请求的客户机的IP地址。
  2. getRemoteHost 方法返回发出请求的客户机的完整主机名。
  3. getRemotePort 方法返回客户机所使用的网络端口号。
  4. getLocalAddr 方法返回WEB服务器的IP地址。
  5. 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

获取请求行

获取请求行的方法:

  1. getRequestURL 方法返回客户端发出请求时的完整URL。
  2. getRequestURI 方法返回请求行中的资源名部分。
  3. getQueryString 方法返回请求行中的参数部分。
  4. getPathInfo 方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Serv的路径之后和查询参数之前的内容,它以“/”开头。
  5. 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);
	}
}

获取请求头

获取请求头:

  1. long getDateHeader(String name)
  2. String getHeader(String name)  根据KEY获取到Value部分
  3. Enumeration getHeaderNames()  获取所有请求头的KEY部分,返回枚举器
  4. Enumeration getHeaders(String name)
  5. 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
*/

获得实体内容(客户端提交的数据)

获得实体内容(客户端提交的数据)

  1. String getParameter(String name):name表示参数名,获取“参数”中的实体数据,即从用户提交上来的数据;
  2. String[] getParameterValues(String name):获取“参数”中的多个数据,一般是获取多选按钮的值;
  3. Enumeration getParameterNames():获取所有用户提交上来的所有数据;
  4. 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是一个域对象

特性:

  1. request对象也是一个域对象
  2. request域的作用范围是一次请求中

方法:

  1. setAttribute(String name, Object o)
  2. getAttribute(String name)
  3. removeAttribute(String name)

注:具体实例在以后实际开发中再做展示。

请求的包含&转发

  1. 在Web程序中,经常是由多个Servlet来完成请求,需要在Servlet之间来回跳转。
  2. RequestDispatcher接口就是为了实现在多个Servlet之间来回跳转而设计的。
  3. 该接口由HttpServletRequest类的getRequestDispatcher()方法获得
  4. 该接口提供两种跳转方式:include()和forward()
  1. include():可以在当前的Servlet中显示另外一个Servlet的内容。
  1. forward():跳转到其他的Servlet。

请求转发的两个步骤:

  1. 获得请求转发器:RequestDispatcher rd = request.getRequestDispatcher(String path):path是转发的地址,要转发给谁
  2. 通过转发器对象转发:rd.forward/include(ServletRequest request, ServletResponse response)

servletrequest修改parameter_HTTP_02

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
	结果如下图:

servletrequest修改parameter_ide_03

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,主要功能是设置响应信息。

设置响应行

向浏览器设置响应行:

  1. void setStatus(int sc);  给当前响应设置状态码
  2. void setError(int sc); 使用指定状态码发送一个错误到客户端(例如请求的地址不存在)
  • 发送错误信息:response.sendError(HttpServletResponse.SC_NOT_FOUND, “你错了”);
  • 响应状态码的常量:SC_NOT_FOUND 404;SC_OK 200;SC_INTERNAL_SERVER_ERROR 500;

servletrequest修改parameter_ide_04

重定向

在请求转发中:

  • forward方法进行跳转后,浏览器中的地址依然还是老样子不变。
  • 所以可以继续使用原来的request和response,可以把数据存入Request域对象中。
  • 请求转发只发生在本个服务器的本个项目之中。

重定向(sendRedirect()方法)用于重定向到另外一个URL。可以重定向到任何地方。

   response.sendRedirect("https://blog.hackyle.net"); //任意URL

转发与重定向的区别:

1)重定向两次请求,转发一次请求

2)重定向地址栏的地址变化,转发地址不变

3)重新定向可以访问外部网站 转发只能访问内部资源

4)转发的性能要优于重定向

设置响应头

设置响应头:

  1. addHeader(String name, String value)
  2. addIntHeader(String name, int value)
  3. 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");就可以解决页面输出中文乱码问题。

设置响应体

向浏览器(客户端)发送响应体:

  1. ServletOutputStream getOutputStream()  向浏览器输出非文本,例如输出图片等内容。通过该字节流的write(byte[] bytes)可以向response缓冲区中写入字节,在由Tomcat服务器将字节内容组成Http响应返回给浏览器。
  2. 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();
	}
}

通过观察我对目录的设定,可以发现本篇文章的结构就是:

  1. 牢牢把握请求和响应的三大组成部分
  2. 所有HTTP的应用都是在这三个组成部分里面进行