🍎道阻且长,行则将至。🍓
本文是javaweb的第二篇,介绍了Servlet,Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术。概述了Request请求与响应,并且主要分析了Request请求。
上一篇: JavaWeb《一》概念、服务器部署及servlet
下一篇: JavaWeb《三》Request请求转发与Response响应
一、Servlet🍏
Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术。使用Servlet就可以实现,根据不同的登录用户在页面上动态显示不同内容。上一篇通过seevlet的入门案例,实现了浏览器发出访问请求,我们的idea中可以有所表示。我们编写一个Servlet就必须要实现Servlet接口,重写接口中的5个方法,但是这样是比较麻烦的,这个过程中我们关注的其实只有service方法。
1.Servlet的体系结构
开发B/S架构的web项目,都是针对HTTP协议,所以我们自定义Servlet可以通过继承HttpServlet
来完成。
2.HttpServlet
在上一个基础案例后,继续写一个Servletdemo类继承HttpServlet
,重新启动服务器后会自动打包,在浏览器中访问http://localhost:8080/Webapp2_war/demo2
。
@WebServlet("/demo2")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//TODO GET 请求方式处理逻辑
System.out.println("get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//TODO Post 请求方式处理逻辑
System.out.println("post...");
}
}
浏览器页面因为本来没内容就是空白,刷新一次在控制台就会get。
而想发送一个post请求servlet,需要使用form
表单了。可以直接在webapp下创建一个index.html
页面,在里面加入表单使用post方法,设置action
为/Webapp2_war/demo2
(和servlet对应),在浏览器页面访问这个页面提交内容就可以看到post了。
<body>
<form action="/web-demo/demo4" method="post">
<input name="username"/><input type="submit"/>
</form>
</body>
发送GET和POST请求的时候,参数的位置不一致,GET请求参数在请求行中,POST请求参数在请求体中,所以HttpServlet会根据请求方式的不同,调用不同的方法。
我们可以打开HttpServlet
看到他实现上层的GenericServlet
的service方法,然后调用自己的service:
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
this.service(request, response);
} else {
throw new ServletException("non-HTTP request or response");
}
}
继续看到service,首先获取到了请求的方法,然后判断类型:
3.urlPattern配置
Servlet类编写好后,要想被访问到,就需要配置其访问路径: ==urlPattern==
urlPattern可以配置多个,配置规则也有多种
- 在此前我们一个Servlet只写了一个访问路径,其实它是可以配置多个urlPattern的。
只需要参数:urlPatterns = {"/demo1","/demo2"},就可以输入不同链接来访问servlet了。
注意这个路径是不能和别人重复了,会报错:
Caused by: java.lang.IllegalArgumentException: 名为 [ServletDemo1]和 [ServletDemo2] 的servlet不能映射为一个url模式(url-pattern) [/demo1]
- 精确匹配
我们此前使用的就是精确匹配,服务路径和配置一致。
- 目录匹配
配置为:/user/*
访问:***/user/aaa
、***/user/aab
或者***/user/abc
都可以,就是说配置中的*
可以任意替代。
==该方法的优先级低于精确匹配==。
- 扩展名匹配
配置为:*.do
访问:***/aaa.do
或者***/abc.do
都可以,配置中的*
可以任意替代。
- 任意匹配
配置为:/
、/*
访问:任意都可以。但是会导致你的静态资源html都不能访问了。
4.XML配置
在Servlet的配置中我们都使用的是@WebServlet
注解方法,这个是Servlet 3.0
版本后开始支持注解配置,3.0版本前只支持XML配置文件的配置方法。所以可以了解一下xml配置。
xml配置方法完成Servlet类就不需要加注解了,然后打开WEB_INF
里面的web.xml,
<!-- Servlet 全类名 -->
<servlet>
<!-- servlet的名称,名字任意-->
<servlet-name>demo3</servlet-name>
<!--servlet的类全名-->
<servlet-class>Webapp2_war.ServletDemo3</servlet-class>
</servlet>
<!-- Servlet 访问路径 -->
<servlet-mapping>
<!-- servlet的名称,要和上面的名称一致-->
<servlet-name>demo3</servlet-name>
<!-- servlet的访问路径-->
<url-pattern>/demo3</url-pattern>
</servlet-mapping>
二、Request🍉
在前面的Servlet
或者HttpServlet
中,我们都可以看到:ServletRequest req, ServletResponse res
参数,这两个参数的作用是什么?
1.request和response
Request
是请求对象,Response
是响应对象。
request:获取请求数据
- 浏览器会发送HTTP请求到后台服务器;
- HTTP的请求中会包含很多请求数据:请求行、请求头、请求体;
- 后台服务器会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中,即request对象,我们可以从request对象中获取请求的相关参数;
- 获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务。
response:设置响应数据
- 业务处理完后,后台就需要给前端返回业务处理的结果即响应数据;
- 把响应数据封装到response对象中;
- 后台服务器会解析response对象,按照[响应行+响应头+响应体]格式拼接结果;
- 浏览器最终解析结果,把内容展示在浏览器给用户浏览。
2.request和response对象的使用
我们再写一个Servlet类,在get里面获取请求数据和设置响应数据。
@WebServlet("/demo8")
public class ServletDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用request对象 获取请求数据
String name = request.getParameter("name");
//使用response对象 设置响应数据
response.setHeader("content-type","text/html;charset=utf-8");
response.getWriter().write("<h1>"+name+",欢迎您!</h1>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Post...");
}
}
访问的时候在链接后面加上?name=zhangsan
,因为这就是直接的get方法。
当然你也可以重写Post方法,然后在静态页面用post表单传入数据。
3.Request对象
HTTP请求数据总共分为三部分内容,分别是请求行
、请求头
、请求体
,这三部分内容的数据该如何获取。
- 获取==请求行==数据
请求行包含三块内容,分别是请求方式
、请求资源路径
、HTTP协议及版本
这三部分内容,request对象都提供了对应的API方法来获取:
获取的内容 | 方法 |
---|---|
请求方式 | String getMethod() |
虚拟目录(项目访问路径) | String getContextPath() |
URL(统一资源定位符) | StringBuffer getRequestURL() |
URI(统一资源标识符) | String getRequestURI() |
请求参数 | String getQueryString() |
get一下:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// String getMethod():获取请求方式: GET
String method = req.getMethod();
System.out.println(method);//GET
// String getContextPath():获取虚拟目录(项目访问路径):/request-demo
String contextPath = req.getContextPath();
System.out.println(contextPath);
// StringBuffer getRequestURL(): 获取URL(统一资源定位符):http://localhost:8080/request-demo/req1
StringBuffer url = req.getRequestURL();
System.out.println(url.toString());
// String getRequestURI():获取URI(统一资源标识符): /request-demo/req1
String uri = req.getRequestURI();
System.out.println(uri);
// String getQueryString():获取请求参数(GET方式): username=zhangsan
String queryString = req.getQueryString();
System.out.println(queryString);
}
- 获取==请求头==数据
请求头的数据,格式为key: value
,根据请求头名称获取对应值的方法为:
String getHeader(String name)
- 获取==请求体==数据
GET请求没有请求体,所以使用请求体需要把请求方式变更为POST。对于请求体中的数据,Request对象提供了如下两种方式来获取其中的数据,分别是:
- 1、
ServletInputStream getInputStream()
获取字节输入流,如果前端发送的是字节数据,比如传递的是文件数据,则使用该方法;
- 2、
BufferedReader getReader()
获取字符输入流,如果前端发送的是纯文本数据,则使用该方法。
4.POST参数获取
参数在请求体中,请求参数获取
BufferedReader getReader()
1.准备一个静态页面,添加form表单发送post请求
<form action="/19Webapp2_war/demo8" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit">
</form>
2.doPost方法获取数据
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取post 请求体:请求参数
//1. 获取字符输入流
BufferedReader br = req.getReader();
//2. 读取数据
String line = br.readLine();
System.out.println(line);
}
点击页面的提交
按钮后,就可以在控制台看到前端所发送的请求数据。
3.post具有多个值的参数
如下的hobby就具有两个参数:
<form action="/19Webapp2_war/demo8" method="post">
<input type="text" name="username"> <br>
<input type="password" name="password"> <br>
<input type="checkbox" name="hobby" value="1">swim
<input type="checkbox" name="hobby" value="2">run<br>
<input type="submit">
</form>
request
对象为我们提供了如下方法,后面还会提到这里:
- 获取所有参数Map集合
Map<String,String[]> getParameterMap()
- 根据名称获取参数值(数组)
String[] getParameterValues(String name)
- 根据名称获取参数值(单个值)
String getParameter(String name)
例如:
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post~");
Map<String, String[]> map = req.getParameterMap();
for (String key : map.keySet()) {
System.out.print(key + ":");
String[] values = map.get(key);
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();
}
}
5.GET参数
前面我们进行了POSTt的请求参数获取(BufferedReader getReader()
),GET请求和POST请求获取请求参数的方式不一样,GET请求的请求参数在请求行中,在获取请求参数这块该如何实现呢》》》String getQueryString();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String result = req.getQueryString();
System.out.println(result);
}
6.POST&GET参数
GET请求方式和POST请求方式区别主要在于获取请求参数的方式不一样,可以采取一种统一获取请求参数的方式,从而统一doGet和doPost方法内的代码。
- 1、根据请求方式的不同分别获取请求参数值
写到一起,然后使用request的getMethod()
来获取请求方式。
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求方式
String method = req.getMethod();
//获取请求参数
String params = "";
if("GET".equals(method)){
params = req.getQueryString();
}else if("POST".equals(method)){
BufferedReader reader = req.getReader();
params = reader.readLine();
}
System.out.println(params);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
2、request对象为我们提供了一些方法:前面提到☝
方法 M 获取所有参数Map集合 Map<String,String[]> getParameterMap()
根据名称获取参数值(数组)| String[] getParameterValues(String name)
根据名称获取参数值(单个值) | String getParameter(String name)
request对象已经将上述获取请求参数的方法进行了封装,并且request提供的方法实现的功能更强大,以后只需要调用request提供的方法即可:
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//采用request提供的获取请求参数的通用方式来获取请求参数
//编写其他的业务代码...
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
三、中文乱码🍍
提交中文内容可能在 控制台出现乱码。浏览器把中文参数按照
UTF-8
进行URL编码,Tomcat对获取到的内容进行了ISO-8859-1
的URL解码,编解码不一致因此出现乱码。
- POST请求
设置输入流的编码 : request.setCharacterEncoding("UTF-8");
- GET/POST通用
需要先解码,再编码 :new String(username.getBytes("ISO-8859-1"),"UTF-8");
POST请求参数一般都会比较多,采用这种方式解决乱码起来比较麻烦,所以POST请求建议使用设置编码的方式进行。
在Tomcat8.0之后,已将GET请求乱码问题解决,设置默认的解码方式为UTF-8。
/**
* 中文乱码问题解决方案
*/
@WebServlet("/req4")
public class RequestDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 解决乱码:POST,getReader()
//request.setCharacterEncoding("UTF-8");//设置字符输入流的编码
//2. 获取username
String username = request.getParameter("username");
System.out.println("解决乱码前:"+username);
//3. GET,获取参数的方式:getQueryString
// 乱码原因:tomcat进行URL解码,默认的字符集ISO-8859-1
/* //3.1 先对乱码数据进行编码:转为字节数组
byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);
//3.2 字节数组解码
username = new String(bytes, StandardCharsets.UTF_8);*/
username = new String(username.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
System.out.println("解决乱码后:"+username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
解决:
☕物有本末,事有终始,知所先后。🍭