Servlet
Posted ohana!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet相关的知识,希望对你有一定的参考价值。
目录
一,概念
1.什么是Servlet
- Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app
- 为不同的Java web服务器规定了相应的编程规范(官方规范),定义好了统一的编程规范(统一的类,接口,方法)
- 屏蔽了不同web服务器的细节(实现的细节,体现在对请求的解析,响应的封装)
- 更换一个web服务器,web应用程序还可以使用
2.servlet能干什么
servlet就是以编程的方式,提供对请求的处理,以及响应的内容(状态码,header,body)
- html是静态文件,要想访问动态页面,servlet才可以
- 文件下载,servlet也可以
- 用户登录,服务端需要对账号密码进行验证,验证成功才可以跳转,servlet才可以
二,访问出错
1.404
404 表示用户访问的资源不存在. 大概率是 URL 的路径写的不正确
1)少写了 Context Path(应用上下文路径/应用名)
2)少写了Servlet Path(资源路径)
3)Servlet Path 写的和 URL 不匹配
4)web.xml 写错了
2.405
405 表示对应的 HTTP 请求方法没有实现
1)没有实现 doGet 方法.
2)如果是post请求,没有实现post方法,也会报405的错
3.500
往往是 Servlet 代码中抛出异常导致的(后端代码有问题)
4.出现空白页面
有实现方法,但是没有任何返回内容(空数据)
5.无法访问此网站
servlet内部有问题,资源路径写错了
或者是在资源路径前加空格等等
6.小结
- 4xx 的状态码表示路径不存在, 往往需要检查 URL 是否正确, 和代码中设定的 Context Path 以及Servlet Path 是否一致.
- 5xx 的状态码表示服务器出现错误, 往往需要观察页面提示的内容和 Tomcat 自身的日志, 观察是否存在报错.
- 出现连接失败往往意味着 Tomcat 没有正确启动, 也需要观察 Tomcat 的自身日志是否有错误提示.
- 空白页面这种情况则需要我们使用抓包工具来分析 HTTP 请求响应的具体交互过程
三,Servlet的API详解
1.HttpServletRequest
1)核心方法
方法 | 描述 |
String getProtocol() | 返回请求协议的名称和版本。 |
String getMethod() | 返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。 |
String getRequestURI() | 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请 求的 URL 的一部分。 |
String getContextPath() | 返回指示请求上下文的请求 URI 部分。 |
String getQueryString() | 返回包含在路径后的请求 URL 中的查询字符串。 |
Enumeration getParameterNames() | 返回一个 String 对象的枚举,包含在该请求中包含的参数的名 称。 |
String getParameter(String name) | 以字符串形式返回请求参数的值,或者如果参数不存在则返回 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 对象 |
2)方法示例(以queryString形式演示)
package 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 RequestStudyServlet extends HttpServlet
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
doGet(req,resp);
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
System.out.println("请求协议名:"+req.getProtocol());
System.out.println("请求方法:"+req.getMethod());
System.out.println("应用上下文路径/应用名:"+req.getContextPath());
System.out.println("资源路径/servletPath路径:"+req.getServletPath());
req.setCharacterEncoding("utf-8");
System.out.println("username:" + req.getParameter("username"));
System.out.println("password:" + req.getParameter("password"));
resp.getWriter().println("请求信息已经获取成功,在idea控制台查看");
3)getParameter方法
主要是以,queryString,form表单格式,form-data简单数据类型为主
queryString
<!--链接跳转-->
<a href="request?username=abc&password=123">get</a><br>
<hr>
form表单
<!--form表单-->
<!--form表单提交,若没有设置请求方法,默认是get,会把form中的控件以name为键,输入/选择的内容为值
设置到queryString中-->
<form action="request" method="post">
<input type="text" name="username" placeholder="输入用户名"><br>
<input type="password" name="password" placeholder="输入密码"><br>
<input type="submit">
</form>
<hr>
form-data简单数据类型格式
<h3>form-data格式</h3>
<form action="form-data-servlet" enctype="multipart/form-data" method="post">
<input type="text" name="username" placeholder="输入用户名"><br>
<input type="password" name="password" placeholder="输入密码"><br>
<input type="submit">
</form>
但是需要注意,以form-data形式请求数据时,需要@MultipartConfig
@WebServlet("/form-data-servlet")
@MultipartConfig
public class FormData extends HttpServlet
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
req.setCharacterEncoding("utf-8");
System.out.println("username:" + req.getParameter("username"));
System.out.println("password:" + req.getParameter("password"));
4)getPart
获取form-data上传的文件
关于文件的获取,有两种途径:
- 可以读取,通过part对象的输入流
- 可以保存
读取:
Part head = req.getPart("head");
InputStream is = head.getInputStream();
byte[] bytes = new byte[is.available()];
is.read(bytes);
System.out.println(new String(bytes,"utf-8"));
保存:
Part head = req.getPart("head");
head.write("D://"+head.getSubmittedFileName());
5)getInputStream
获取body中的数据(常用于json格式)
前端:
<h3>ajax提交</h3>
<input type="text" id="ajax_username" placeholder="输入用户名"><br>
<input type="password" id="ajax_password" placeholder="输入密码"><br>
<button onclick="ajaxSubmit()">提交</button>
<script>
function ajaxSubmit()
let username = document.querySelector("#ajax_username");
let password = document.querySelector("#ajax_password");
let json =
username:username.value,
password:password.value
ajax(
method:"post",
url:"ajax-json-servlet",
contentType: "application/json",
body:JSON.stringify(json),
callback:function (status,responseText)
alert("后端返回的是:"+status+responseText);
)
function ajax(args)
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function ()
if(xhr.readyState == 4)
args.callback(xhr.status,xhr.responseText);
xhr.open(args.method,args.url);
if(args.contentType)
xhr.setRequestHeader("Content-Type",args.contentType);
if(args.body)
xhr.send(args.body);
else
xhr.send();
</script>
后端:
package request;
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("/ajax-json-servlet")
public class AjaxJsonServlet extends HttpServlet
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
req.setCharacterEncoding("utf-8");//获取body数据,要设置编码
InputStream is = req.getInputStream();
// int len = req.getContentLength();
// byte[] bytes = new byte[len];
// is.read(bytes);
// System.out.println(new String(bytes,"utf-8"));
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(is,User.class);
System.out.println(user);
static class User
private String username;
private String password;
@Override
public String toString()
return "User" +
"username='" + username + '\\'' +
", password='" + password + '\\'' +
'';
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;
2.HttpServletResponse
1)核心方法
方法 | 描述 |
void setStatus(int sc) | 为该响应设置状态码。 |
void setHeader(String name, String value) | 设置一个带有给定的名称和值的 header. 如果 name 已经存在, 则覆盖旧的值. |
void addHeader(String name, String value) | 添加一个带有给定的名称和值的 header. 如果 name 已经存在, 不覆盖旧的值, 并列添加新的键值对 |
void setContentType(String type) | 设置被发送到客户端的响应的内容类型。 |
void setCharacterEncoding(String charset) | 设置被发送到客户端的响应的字符编码(MIME 字符集)例如, UTF-8。 |
void sendRedirect(String location) | 使用指定的重定向位置 URL 发送临时重定向响应到客户端。 |
PrintWriter getWriter() | 用于往 body 中写入文本格式数据. |
OutputStream getOutputStream() | 用于往 body 中写入二进制格式数据 |
2)setStatus方法
前端:
<h3>设置响应状态码</h3>
<input type="text" id="Status"><br>
<button onclick="SetStatus()">提交</button>
function SetStatus()
let status = document.querySelector("#Status");
window.location.href = "response?status="+status.value;
后端:
String status = req.getParameter("status");
resp.setStatus(Integer.parseInt(status));
resp.getWriter().write("设置成功");
3)重定向与转发
前端:
<h3>重定向</h3>
<a href="goto?type=1">跳转</a>
<h3>转发</h3>
<a href="goto?type=2">跳转</a>
后端:
package 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("/goto")
public class GotoServlet extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
String type = req.getParameter("type");
if("1".equals(type))
//重定向
resp.setStatus(301);
resp.setHeader("Location","hello.html");
//以上代码可以简化为
// resp.sendRedirect("hello.html");
else if("2".equals(type))
//转发
req.getRequestDispatcher("hello.html").forward(req,resp);
区别 | 转发forward() | 重定向sendRedirect() |
---|---|---|
根目录 | 包含项目访问地址 | 没有项目访问地址 |
地址栏 | 不会发生变化 | 会发生变化 |
哪里跳转 | 服务器端进行的跳转 | 浏览器端进行的跳转 |
请求域中数据 | 不会丢失 | 会丢失 |
4)返回一个文件
前端:
<h3>获取一个图片(渲染展示)</h3>
<img src="file?type=photo&show=1">
<h3>获取一个音乐(渲染展示)</h3>
<audio src="file?type=music&show=1" controls></audio>
<h3>获取一个图片(下载)</h3>
<a href="file?type=photo&show=0">下载</a>
<h3>获取一个音乐(下载)</h3>
<a href="file?type=music&show=0">下载</a>
后端:
package 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.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
@WebServlet("/file")
public class FileServlet extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
// 获取响应对象的字节输出流
OutputStream os = resp.getOutputStream();
// 返回的文件类型:1.图片
String type = req.getParameter("type");
// 返回时的操作:1.渲染 2.下载
String show = req.getParameter("show");
File file = null;
// <img src="file?type=photo&show=1">
if("photo".equals(type))//返回图片
if("1".equals(show))
resp.setContentType("image/jpeg");//jpg格式
else
//这样只是没有设置下载的文件名
resp.setContentType("application/octet-stream");
file = new File("E:\\\\Workspace\\\\bite\\\\java43\\\\servlet-study\\\\src\\\\main\\\\resources\\\\head2.jpg");
//<audio src="file?type=music&show=1" controls></audio>
else if("music".equals(type))//返回音乐
if("1".equals(show))
resp.setContentType("audio/mp3");//mp3格式
else
resp.setContentType("application/octet-stream");
file = new File("E:\\\\Workspace\\\\bite\\\\java43\\\\servlet-study\\\\src\\\\main\\\\resources\\\\周杰伦 - 晴天.mp3");
//返回一个文件类型:Content-Length,body
byte[] data = Files.readAllBytes(file.toPath());
resp.setContentLength(data.length);//=setHeader("Content-Length", xxx)
os.write(data);
5)返回json数据
常用于ajax请求,返回一下数据,动态的填充网页
前端:
<h3>获取ajax内容</h3>
<button onclick="reach()">ajax</button>
<div id="content"></div>
<script>
function reach()
//获取content元素,准备添加内容(json)
let content = document.querySelector("#content");
ajax(
method:"get",
url:"ajax-json",
callback:function (status,responseText)
console.log(responseText);
//转化为json对象
let array = JSON.parse(responseText);
for (json of array)
//每一个json元素创建一个dom来保存信息
let p = document.createElement("p");
p.innerHTML = json.from + " 对 " + json.to + " 说 " + json.info;
content.appendChild(p);
);
function ajax(args)
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function ()
if(xhr.readyState == 4)
args.callback(xhr.status,xhr.responseText);
xhr.open(args.method,args.url);
if(args.contentType)
xhr.setRequestHeader("Content-Type",args.contentType);
if(args.body)
xhr.send(args.body);
else
xhr.send();
</script>
后端:
package response;
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.util.ArrayList;
import java.util.List;
@WebServlet("/ajax-json")
public class AjaxJsonServlet extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
List<Message> list = new ArrayList<>();
Message m1 = new Message("小明","小红","我喜欢你!");
Message m2 = new Message("小红","小明","你是个好人!");
list.add(m1);
list.add(m2);
ObjectMapper mapper = new ObjectMapper();
//把Java对象,转换为一个字符串
String json = mapper.writeValueAsString(list);
System.out.println(json);
//设置json可以不设置Content-Length,tomcat会设置
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().println(json);
static class Message
private String from;
private String to;
private String info;
public Message(String from, String to, String info)
this.from = from;
this.to = to;
this.info = info;
public String getFrom()
return from;
public void setFrom(String from)
this.from = from;
public String getTo()
return to;
public void setTo(String to)
this.to = to;
public String getInfo()
return info;
public void setInfo(String info)
this.info = info;
四,session和cookie
制作一个简单的登录页面,和一个敏感文件(登陆成功才可以访问)
前端:
<h3>登录页面</h3>
<form action="login" method="post" >
<input type="text" name="username" placeholder="输入用户名"><br>
<input type="password" name="password" placeholder="输入密码"><br>
<input type="submit" value="提交">
</form>
后端:
登录页面的后端设计(校验账号密码)
package session;
import javax.servlet.ServletException;
import javax.servlet.SessionCookieConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
//登陆成功:跳转到hello.html,并且可以访问敏感资源/secret
//登陆失败:返回错误信息,不允许访问敏感资源
String username = req.getParameter("username");
String password = req.getParameter("password");
//模拟验证账号密码
if("abc".equals(username) && "123".equals(password))
//创建一个session,为登录成功的用户创建一个session
//tomcat启动时维护了一个数据结构Map<String,Session>
//用来保存多个用户的会话信息,键为sessionId的值(随机字符串)
//获取当前请求的session,如果没有获取到,那就创建一个
HttpSession session = req.getSession(true);
//session对象本身也是一个Map结构,可以保存多组键值对的数据
session.setAttribute("name",username);
//跳转到登陆后的页面
resp.sendRedirect("hello.html");
else
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().println("登陆失败");
敏感文件访问的设计
package session;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/secret")
public class SecretServlet extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
resp.setContentType("text/html;charset=utf-8");
//获取不到session的时候,不会自动创建session,而是会返回null
HttpSession session = req.getSession(false);
if(session != null)
//在session中寻找相应的用户信息,如果能找到就不为空,说明登陆成功
//反之,就说明,没有这个用户,没有登陆成功
String username = (String) session.getAttribute("name");
if(username != null)
resp.getWriter().println("登陆成功");
return;
resp.getWriter().println("登陆失败");
以上是关于Servlet的主要内容,如果未能解决你的问题,请参考以下文章
为啥 HttpServletRequest 对象在从一个 servlet 转发到另一个 servlet 时会发生变化?