- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
前言:
Servlet 中的 doXXX 方法的目的就是根据请求计算得到相应,然后把响应的数据设置到 HttpServletResponse 对象中;然后 Tomcat 就会把这个 HttpServletResponse 对象按照 HTTP 协议的格式, 转成一个字符串,并通过 Socket 写回给浏览器
方法 | 描述 |
---|---|
void setStatus(int sc) | 为该响应设置状态码 |
void setHeader(String name,String value) | 设置一个带有给定的名称和值的 header,如果 name 已经存在,则覆盖旧的值 |
void addHeader(Stringname, String value) | 添加一个带有给定的名称和值的 header,如果 name 已经存在,不覆盖旧的值,并列添加新的键值对 |
void setCharacterEncoding(Stringcharset) | 设置被发送到客户端的响应的字符编码(MIME 字符集)例如,UTF-8 |
void sendRedirect(String location) | 使用指定的重定向位置 URL 发送临时重定向响应到客户端 |
void setContentType(Stringtype) | 设置被发送到客户端的响应的内容类型 |
PrintWriter getWriter() | 用于往 body 中写入文本格式数据 |
OutputStreamgetOutputStream() | 用于往 body 中写入二进制格式数据 |
注意:
前端代码:
<body>
<h3>设置状态码</h3>
<input type="text" id="status">
</br>
<button onclick="setStatus()">提交</button>
</body>
<script>
function setStatus() {
// js中发请求: 1)ajax; 2)直接修改url
let status = document.querySelector("#status");
// 后端会设置这个文本框输入的值为响应状态码: 严格来说 需要验证,此处省略
window.location.href = "response?status=" + status.value;
}
</script>
后端代码:
@WebServlet("/response")
public class ResponseServletStudy extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求发送的 queryString数据: status=xxx
String status = req.getParameter("status");
resp.setStatus(Integer.parseInt(status));
resp.getWriter().write("设置响应状态码成功");
}
}
启动之后,打开页面:
(提交之后出现乱码,暂时不管)
使用 fiddler 抓包:
变换不同的 status 的值,就可以看到不同的响应结果
注意: 如果没有调用这个方法,默认返回200状态码(若正常执行,则没有异常;若出现异常,就返回500)
修改前端代码:
<body>
<h3>设置状态码</h3>
<input type="text" id="status">
</br>
<button onclick="setStatus()">提交</button>
<h3>设置响应头</h3>
<a href="response">设置</a>
</body>
<script>
function setStatus() {
// js中发请求: 1)ajax; 2)直接修改url
let status = document.querySelector("#status");
// 后端会设置这个文本框输入的值为响应状态码: 严格来说 需要验证,此处省略
window.location.href = "response?status=" + status.value;
}
</script>
修改后端代码:
@WebServlet("/response")
public class ResponseServletStudy extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求发送的 queryString数据: status=xxx
String status = req.getParameter("status");
// 若请求数据包含需要设置的状态码,才执行
if(status != null) {
resp.setStatus(Integer.parseInt(status));
resp.getWriter().write("设置响应状态码成功");
}
// 设置响应头的键值对,键可以是标准的Http响应头的键,也可以是自定义的
// 响应状态码是301,302,307,响应头有Location字段,才是重定向
resp.setHeader("Location","http://www.baidu.com");
resp.setHeader("username","花花");
}
}
重新启动,刷新页面:
点击后,发现我们设置了 location,但并没有跳转,使用 fiddler 抓包:
发现状态码是200
注意: 若响应头 name 键已有,就会覆盖原有的键值对
和 setHeader 唯一不同的是:name键已有,不会影响添加一个新的
设置响应头 Content-Type 的值,等同于 setHeader(“Content-Type”, String type)
因为 Content-Type 是标识 body 的数据格式,所以调用该方法还需要设置 body 的内容
修改前端代码:
<body>
<h3>响应正文为简单的html网页</h3>
<a href="html?type=1">查看</a>
</body>
后端代码:
@WebServlet("/html")
public class HtmlTypeServlet extends HttpServlet {
// html?type=1...
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 响应html: 设置响应的Content-Type
resp.setContentType("text/html;charset=utf-8");
PrintWriter pw = resp.getWriter();
// 获取queryString中type的值
String type = req.getParameter("type");
// 返回简单的 html
if("1".equals(type)) {
pw.println("<h3>获取网页成功</h3>");
}
}
}
重新启动,刷新页面,点击跳转:
前端代码:
<body>
<h3>响应正文为复杂的html(动态变化的)</h3>
<input type="text" id="username" placeholder="输入用户名">
</br>
<button onclick="toWelcome()">跳转</button>
</body>
<script>
function toWelcome() {
let username = document.querySelector("#username");
window.location.href = "html?type=2&username=" + username.value;
}
</script>
后端代码:
@WebServlet("/html")
public class HtmlTypeServlet extends HttpServlet {
// html?type=1...
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 响应html: 设置响应的Content-Type
resp.setContentType("text/html;charset=utf-8");
PrintWriter pw = resp.getWriter();
// 获取queryString中type的值
String type = req.getParameter("type");
// 返回简单的 html
if("1".equals(type)) {
pw.println("<h3>获取网页成功</h3>");
}
// 返回复杂的动态html
else if("2".equals(type)) {
// html>type=2&username=
String username = req.getParameter("username");
pw.println("<p>");
pw.println("欢迎你!!" + username);
pw.println("</p>");
}
}
}
重新启动,刷新页面:
输入别的用户名尝试:
关于动态网页,在 Java 代码中,写很多的 html 代码,耦合性太强(两个完全不同的编程语言,放在一起开发),维护性、扩展性很差
解决方法: ①模板技术 (也存在一些问题,进一步发展就有了 ②ajax 技术的产生) (模板技术后边继续讲解)
前端代码:
<body>
<h3>重定向到hello.html</h3>
<a href="goto?type=1">跳转</a>
<h3>转发到hello.html</h3>
<a href="goto?type=2">跳转</a>
</body>
后端代码:
@WebServlet("/goto")
public class GoToServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// goto?type=xxx
String type = req.getParameter("type");
// 重定向
if("1".equals(type)) {
resp.setStatus(301);
resp.setHeader("Location","hello.html");
// 以上代码可以简化为 sendRedirect
// resp.sendRedirect("hello.html");
}
// 转发
else if("2".equals(type)) {
req.getRequestDispatcher("hello.html").forward(req,resp);
}
}
}
重新启动服务,刷新页面:
点击重定向的跳转,使用 fiddler 抓包:
我们可以看到 url 地址栏的改变:
特点: ①url 地址栏会变;②发送两次请求
原理:
第一次返回 301 / 302 / 307 响应状态码,以及响应头 Location: 网页的地址;
第二次浏览器自动跳转到 Location 设置的地址
前后端代码同上
点击转发的跳转,使用 fiddler 抓包:
观察 url 地址栏:
**特点:**①url 地址栏不变;②只有一次请求
原理: 当此请求 Servlet 时,由 Servlet 获取到转发路径的资源,把这个路径的内容设置到响应正文
设置一下 Content-Type 和 Content-Length,然后把文件的二进制数据,放在响应正文即可
以图片和音乐文件为例
前端代码:
<body>
<h3>获取一个图片(渲染展示)</h3>
<img src="file?type=photo&show=1">
<h3>获取一个音乐(渲染展示)</h3>
<audio src="file?type=music&show=1" controls></audio>
</body>
后端代码:
@WebServlet("/file")
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取响应对象的字节输出流
OutputStream os = resp.getOutputStream();
// 返回的文件类型: photo-图片 music-音乐
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)) {
resp.setContentType("image/jpeg"); //jpg格式
file = new File("保存在 resources 里的完整路径");
}
// <audio src="file?type=music&show=1" controls></audio>
else if("music".equals(type)) {
resp.setContentType("audio/mp3"); // MP3格式
file = new File("保存在 resources 里的完整路径,如: D:\\xxx\\xxx\\Servlet-Study\\src\\main\\resources\\love.mp3");
}
// 返回一个文件类型: Content-Length, body
byte[] data = Files.readAllBytes(file.toPath());
resp.setContentLength(data.length); // 等同于 setHeader("Content-Length",xxx)
os.write(data);
}
}
可在 菜鸟教程 里查看 content-type 对照表:
启动服务,打开页面:
(此处 gif 格式,听不到声音)
修改前端代码:
<body>
<h3>获取一个图片(下载)</h3>
<a href="file?type=photo&show=2">下载</a>
<h3>获取一个音乐(下载)</h3>
<a href="file?type=photo&show=2">下载</a>
</body>
修改后端代码:
@WebServlet("/file")
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取响应对象的字节输出流
OutputStream os = resp.getOutputStream();
// 返回的文件类型: photo-图片 music-音乐
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 if("2".equals(show)) {
resp.setContentType("application/octet-stream");
}
file = new File("D:\\Bite\\JAVA\\JavaCode\\Servlet-Study\\src\\main\\resources\\doge.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 if("2".equals(show)) {
resp.setContentType("application/octet-stream");
}
file = new File("D:\\Bite\\JAVA\\JavaCode\\Servlet-Study\\src\\main\\resources\\love.mp3");
}
// 返回一个文件类型: Content-Length, body
byte[] data = Files.readAllBytes(file.toPath());
resp.setContentLength(data.length); // 等同于 setHeader("Content-Length",xxx)
os.write(data);
}
}
重新启动,刷新页面:
打开文件:
修改文件后缀名:
**思考:**图片、音乐、视频是静态文件,直接放在 webapp 下,就可以直接访问,还需要Servlet来返回吗?这样是否多此一举?
.
若文件总的大小非常大,放在 web 应用的 webapp 下就不合适了(之后打包比较费劲),使用 Servlet 去读取本地其他地方的文件来返回,就比较合适
常用于 ajax 请求,返回一些数据;用于动态的填充网页
后端代码:
@WebServlet("/ajax-response")
public class AjaxJsonServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
List<Message> messages = new ArrayList<>();
Message m1 = new Message("杰杰子","花花","你一定能找到好工作的!");
Message m2 = new Message("花花","杰杰子","我一定可以的!");
messages.add(m1);
messages.add(m2);
ObjectMapper mapper = new ObjectMapper();
// 把 java对象转换为 json字符串; List和数组会转换为[],一个对象{成员变量名: 值}
String json = mapper.writeValueAsString(messages);
System.out.println("转化的json字符串:" + 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;
}
}
}
启动服务,打开网页,查看:
后端输出:
加上前端代码:
<body>
<h3>获取ajax响应数据,动态生成网页内容</h3>
<button onclick="generate()">试试丫</button>
<div id="content"></div>
</body>
<script>
function generate() {
let content = document.querySelector("#content");
ajax({
url: "ajax-response",
method: "get",
callback: function(status,resp){
console.log(resp);
// resp 是一个字符串
//转换为 json对象
let array = JSON.parse(resp);
// 遍历
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){//var ajax = function(){}
let xhr = new XMLHttpRequest();
// 设置回调函数
xhr.onreadystatechange = function(){
// 4: 客户端接收到响应后回调
if(xhr.readyState == 4){
// 回调函数可能需要使用响应的内容,作为传入参数
args.callback(xhr.status, xhr.responseText);
}
}
xhr.open(args.method, args.url);
//如果args中,contentType属性有内容,就设置Content-Type请求头
if(args.contentType){//js中,if判断,除了判断boolean值,还可以判断字符串,对象等,有值就为true
xhr.setRequestHeader("Content-Type", args.contentType);
}
//如果args中,设置了body请求正文,调用send(body)
if(args.body){
xhr.send(args.body);
}else{//如果没有设置,调用send()
xhr.send();
}
}
</script>
刷新页面:
客户端发起 Http 请求:
几种请求数据对应可以使用的数据格式:
数据格式 | queryString | 表单 | from-data | json |
---|---|---|---|---|
js: window.location.href = " " | √ | |||
html: <a href=" " / <img src=" " / … | √ | |||
浏览器地址栏直接输入 url | √ | |||
< form> 表单标签 | √ | √ (post) | √ (post,form-data) | |
js: ajax | √ | √ | √ | √ |
服务端处理:
获取方式 | queryString | 表单 | from-data | json |
---|---|---|---|---|
getParameter | √ | √ | 简单类型可以获取 | |
getPart | √ (上传的文件) | |||
getInputStream (可以获取任意格式的请求正文的数据) | × (自己解析比较复杂,一般不用) | × | √ |
**注意:**json 字符串中的键需要和自定义类型中的成员变量名一致(一般解析为自定义类型)
校验: 如,账号手机号的格式
业务逻辑处理:根据请求数据的某个字段,执行不同的逻辑,如 type=1 返回登陆页面;type=0 返回首页,还可能执行数据库操作等
客户端处理 Http 响应:
我有一个关于 Java Servlet 的问题。 假设我在 servlet 网页“somePage”上。我想登录(使用另一个 servlet,“登录”servlet)。所以我点击“somePage”上
如何将变量数组从一个 servlet 传递到另一个 servlet? 最佳答案 如果您要将当前请求传递给另一个servlet,则只需将其设置为请求属性即可。 request.setAttribute(
什么可能导致此错误? Caused by: jakarta.servlet.UnavailableException: Servlet class org.restlet.ext.servle
我的maven依赖树是这样的 我想问我maven如何解决这个冲突,有两个servlet-api.jar?提前谢谢你。 最佳答案 如果您想从 Velocity 工具中删除 servlet-api,您可以
config ProcessReg ProcessReg text HelloWorld1 public class config implements Serv
您好,我有一个关于 servlet 调用另一个 servlet 的问题 我有一个名为 Relay 的主 servlet,它将负责控制其他 servlet 用户将点击并将转发到 Relay servle
在我的 REST API 项目中,我已将 /* 映射到 RESTServlet,并且需要在同一 WAR 中托管静态内容。我更愿意将 /static/* 映射到 WAS liberty 提供的默认 se
响应映射在 Servlet 中如何工作? 每个响应如何知道清除特定 HTML 或 Handlebars 上的输出? 最佳答案 有一个 ember-java带有 Jersey REST 服务 的 git
有一个 @WebServlet(urlPatterns = "/myServlet/") .如果用户转到 myapp/myServlet/other ,我仍然希望我的 servlet 能够捕获。也就是
我正在使用 Filter 在我的所有页面中插入反点击劫持 header - 这工作正常,除了 JBoss EAP 6.3 容器管理的登录页面,这是更重要的页面之一拥有它。 登录页面根本不调用过滤器,登
我正在尝试使用 RequestDispatcher 将数据从一个 servlet 传递到另一个 servlet。这是我的调度程序代码。 String address; address = "/Java
我刚刚开始使用 Servlet,并设法让一些 Servlet 充当单独的 URL,用于填充数据库以进行一些虚拟测试。某种形式: public class Populate_ServletName ex
我是否需要同时配置app.servlet.version 和 grails.servlet.version? 前者在application.properties中,后者在BuildConfig.gro
在Myeclipse中我创建了一个名为web1的Web项目,并添加了一个名为servlet1的servlet,web.xml如下: servlet1 servlet1
这个问题在这里已经有了答案: How to run a background task in a servlet based web application? (5 个回答) 6年前关闭。 是否可以在
在我的 ManagedBean 中,如果我将范围从 @RequestScoped 更改为 @ViewScoped,我将收到以下错误堆栈。我该如何解决这个问题?不过,当我运行应用程序时,我可以在页面中看
这个问题已经有答案了: How do I execute multiple servlets in sequence? (2 个回答) 已关闭 3 年前。 我已经构建了jdbc-Servlet的代码,
我一直在徒劳地尝试实现 tomcat 9 的 jakarta servlet,而不是以前的 javax.servlet 实现(因为我的理解是 jakarta 包是前进的方向)。问题是,当我将浏览器指向
我是 Spring 新手,正在尝试使用 Maven 部署和运行我的第一个 Spring Web 应用程序。有关更多详细信息,请参阅 here (我昨天发布的一个问题): 现在,我遇到的问题是:当我尝试
我正在尝试学习如何使用 JSP。我遇到了一些毫无意义的事情,至少在我看来是这样: 当我尝试运行时: response.getWriter().println(m.getDb().printAll())
我是一名优秀的程序员,十分优秀!