B/S和C/S
C/S架构的程序:
B/S架构的程序:
Web服务器
对外提供web服务的软件。
我们要使用的web服务器:
- tomcat 不要钱。
- nginx 并发处理能力特强。
Tomcat
下载的网址:https://tomcat.apache.org/download-80.cgi
安装使用。 开箱即用…
在bin目录下双击:
启动
启动之后在浏览器访问: http://localhost:8080
Tomcat的端口修改:
端口被占用的情况:启动tomcat的时候会闪退。查看日志:
修改:打开
在tomcat上部署一个web程序:
将写好的web应用程序直接赋值到webapps中:
在浏览器中访问: http://localhost:8888/0107-demo/main.html
在IDEA中配置tomcat
在IDEA中创建web项目
创建新项目:
Servlet
创建servlet需要添加servlet需要的jar文件,和之前添加JDBC驱动的jar的操作方式一致。这次选择的是tomcat中的servlet-api.jar文件
自己定义一个类,继承HttpServlet
undefined
在项目的webapp/WEB-INF/web.xml中添加配置:
启动tomcat在浏览器中访问: http://localhost8080/web/hello
serlvet执行流程分析
[1]启动tomcat
tomcat已启动就会读取我们的WEB-INF/web.xml文件。
[2]在浏览器中访问:
http://localhost:8080/web/hello
localhost 指向本机
8080: 找到了tomcat
/web : 找到当前的项目
/hello : 要访问/web项目的 /hello
[3]tomcat通过”/hello” 找打对应的servlet
tomcat找到对应的servlet类,加载这个类,创建这个类的类对象。执行其中的doGet方法:
[4]浏览器接收到响应结果
认识一下servlet
狭义上:就是一个接口
package javax.servlet; import java.io.IOException; public interface Servlet {
广义上说servlet就是我们自己实现的servlet类:
tomcat其实本身只是认识Servlet接口而已,根本就不知道我们自己写的类。
我们自己写的servlet,当tomcat加载之后,会把我们的servlet看作Servlet接口来使用。所以我们写的类只要实现了Servlet接口,tomcat就认识,就能加载运行,并不一定要继承HttpServlet和GenericServlet类。
servlet的API”研究”
先看接口源码:
package javax.servlet;
import java.io.IOException; public interface Servlet {
tomcat加载和运行的过程:
[1]加载我们的HelloServlet类,根据这个类会创建一个HelloServlet对象。
[2]创建好对象之后立刻初始化。 执行方法:
void init(ServletConfig var1) throws ServletException;
这个方法本身是在GenericServlet中实现的。
[3]当请求到达这个HelloServlet的时候,默认会执行:
这个方法也是GenericServlet实现的
tomcat就是通过”反射”调用HelloServlet对象的service方法,并且创建了两个对象ServletRequest和ServletResponse。 ServletRequest封装了所有的客户端的请求信息。 ServletResponse使用响应客户端的一个对象。
init方法的实现
在GenericServlet中实现:
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {
init() : 空实现的方式,就让子类按需重写。
service方法的实现
在GenericServlet中并未实现这个方法:
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {
肯定是在HttpServlet中实现的:
public abstract class HttpServlet extends GenericServlet {
// 对父类/接口中的方法的实现
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
//HttpServletRequest 是 ServletRequest的子接口
//HttpServletResponse 是 ServletResponse的子接口
// 申明了两个对象。
HttpServletRequest request;
HttpServletResponse response;
try {
// tomcat传入的req对象本身就是子接口HttpServletRequest的对象
// 将父类对象强制类型转换为子类类型
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
// 如果没有异常,调用了另外一个重载的方法
service(request, response);
}
// 在HttpServlet中自己定义的一个重载的service的方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取HTTP的请求方法。HTTP请求有多个方法(GET,POST,PUT,DELETE,HEAD,TRACE,OPTIONS)
String method = req.getMethod();
// 如果请求方法是get
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
// 调用doGet
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
// 调用doGet
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
// 处理head请求
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
// 处理post请求
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
// 处理put请求
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
// 处理Delete请求
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
// doXxxx的实现
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// 这里的代码的作用是:当发送了get请求的时候,判断子类是否重写了这个方法,如果没有重写就报异常,如果重写了就执行子类的方法
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
}
所以我们自己的servlet只要根据需要实现对应的doXxx方法即可,如果没有按照需求实现,就会出现405错误
之前的案例中地址栏直接输入 http://localhost:8080/web/hello就是一个GET请求,我在servlet中要实现doGet处理GET请求。
destroy方法
destroy是在Servlet接口中申明的,使用卸载servlet对象之前调用的,专门用来释放资源优先象finally代码块。
这个方法在GenericServlet中实现的,是一个空实现。空实现,就是让我们自己按需重写的。
测试这些API的执行情况:
public class HelloServlet extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("初始化HelloServlet");
}
@Override
public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
System.out.println("执行了service方法");
// 通过super调用父类的service方法方法
super.service(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("执行了doGet方法");
resp.getWriter().write("Hello world!");
}
@Override
public void destroy() {
System.out.println("执行了destroy方法");
}
}
启动tomcat,并且 重复访问这个servlet两次:
tomcat启动的时候,这些方法都没有执行。
停止tomcat:
tomcat的生命周期:
当tomcat加载servlet之后, 创建servlet类对象,对象已创建,立刻执行init方法,进行初始化。
当请求到大servlet的时候,tomcat会调用servlet的service方法处理请求。(service内部会自动调用对应的doXxxx方法)
当我们停止tomcat/卸载servlet的时候,在卸载之前会执行destroy方法释放资源。
HTTP协议
所谓HTTP协议就是,规定客户端和服务器端发送消息的格式和内容。
本质上客户端和服务端都是一台机器,通过一个软件进行通信(浏览器和tomcat)。为了让这两个软件可以通信,就规定了一套通信的消息格式。有了这个格式,它们相互就能”理解”相互发的消息。
研究HTTP协议,就是稍微看看HTTP的协议中的消息格式大致是啥样子?
我们请求一个网站:
请求头消息:
请求行: GET / HTTP/1.1
分为3部分:
- GET 请求方法,请求方法分为7中:GET,POST,PUT,DELETE,OPTIONS,HEAD,TRACE
- / 请求资源的路径。 / 表示请求首页。 /login.html 表示登陆页面
- HTTP/1.1 请求协议版本
我们案例的请求头消息:
所有的请求头都是以 key : value的形式发送。具体的key : value是什么,是不确定。
响应头消息
响应行: HTTP/1.1 200
- HTTP/1.1 协议版本
- 200 响应状态码。
响应状态码的说明:
- 200 表示响应正常。
- 500 服务器内部错误,程序出异常,无法正常响应。
- 404 表示请求的资源不存在。
- 405 请求的方法不被支持。对应的doXxx方法没有被实现。
- 406 响应的数据类型不正确,
- 400 请求的数据类型不正确。
- 304 获取的数据是从缓存中获取的,没有向服务器发送请求。
Request
当然我们说的是HttpServletRequest这个类型。
在我们自己写的doXxx方法中有一个参数HttpServletRequest类型,这个参数一个对象, 这个对象封装了所有的请求内容。所谓请求内容,就是客户端发送的请求行信息,以及请求头信息。tomcat会将客户端的请求内容进行解析,并且封装到HttpServletRequest对象中,并且将这个对象传递到我们的doXxx方法内部。
我们通过request可以轻松获取所有的请求内容:
请求内容:
- 请求行中的内容: 请求方法,请求的资源的路径,协议版本。
- 请求头的所有内容。
- 请求参数
请求参数是什么?
我们在京东搜索一个商品
①search.jd.com 京东的域名。 域名就是我们写的 localhost:8080
② /Search 请求的资源路径 /web/hello
③ keyword=尤克里里&enc=utf-8
GET请求的请求参数是直接使用?连接在url后面的。 形式: key=value&key=value&….
POST请求的参数是放在一个专门的数据区域中,但是依然是key=value的形式。
使用tomcat传入的request对象获取所有的请求内容:
准备一个RequestTestServlet,在web.xml中添加配置:
RequestTest
com.qidian.web.servlet.RequestTestServlet
RequestTest
/requestTest
public class RequestTestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 从request中获取所有的请求内容
// 获取请求方法
String method = request.getMethod();
System.out.println("请求方法是:"+method);
// 获取请求路径
String url = request.getRequestURL().toString();
System.out.println("请求路径:"+url);
// 获取请求头
System.out.println("请求头:----------------");
Enumeration headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String name = headerNames.nextElement();
System.out.println(name+":"+request.getHeader(name));
}
// 获取请求参数 username,age
// 传入的是参数的名字,返回参数的值 这个方法返回的永远都是String类型
String username = request.getParameter("username");
String ageStr = request.getParameter("age");
int age = Integer.parseInt(ageStr);
System.out.println("请求参数:--------------");
System.out.println("username = "+username);
System.out.println("age = "+age);
}
}
请求测试:
http://localhost:8080/web/requestTest?username=qimukakaxi&age=185
控制台输出的内容:
请求方法是:GET
请求路径:http://localhost:8080/web/requestTest
请求头:----------------
host:localhost:8080
connection:keep-alive
sec-ch-ua:" Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
upgrade-insecure-requests:1
user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site:none
sec-fetch-mode:navigate
sec-fetch-user:?1
sec-fetch-dest:document
accept-encoding:gzip, deflate, br
accept-language:zh-CN,zh;q=0.9,en;q=0.8
请求参数:--------------
username = qimukakaxi
age = 185
Response
HttpServletResponse 是tomcat创建的一个对象,这个对象直接连接者客户端,我们可以通过这个对象响应客户端。
// 使用response响应客户端
response.setCharacterEncoding("utf-8");// 防止中文乱码
response.getWriter().write("这是响应给客户端的内容,它可以是一段HTML代码 你看!是吧!");
图书管理
添加图书
web项目添加驱动:在WEB-INF目录下创建一个lib文件夹,将驱动文件放在lib中,添加到classpath
添加一个POJO类:
public class Book {
// 图书编号,对应g_book的isbn
private String isbn;
// 图书标题,对应g_book的title
private String title;
// 图书成本,对应g_book的cost
private float cost;
// 图书售价,对应g_book的price
private float price;
// 图书出版社编号,对应g_book的pid
private String pid;
// 图书类别编号,对应g_book的categoryId
private int categoryId;
// setter和getter省略
}
添加BookDAO接口:
public interface BookDAO {
// 保存一本图书
int save(Book book);
// 根据isbn查询一本图书信息
Book getByIsbn(String isbn);
// 修改一本图书信息
int update(Book book);
// 查询所有的图书
ArrayList queryAll();
// 分页查询
ArrayList queryByPage(int page, int size);
// 查询总条数
int queryTotal();
}
在web目录下创建一个book-add.html页面
添加图书
图书编号:
图书标题:
成本:
价格:
出版社编号:
类别编号:
输入内容,点击提交按钮
请求头:
由于是POST请求,请求参数不在url后, 而是在一个专门的数据区域
使用一个Servlet获取这些请求参数,并且将这些数据保存到数据库
web.xml的配置:
在servlet中使用request.getParamter(” 参数名”); 这里的参数名,就是表单的input中的name属性的值。
public class BookSaveServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse resposne) throws ServletException, IOException {
// 获取所有的请求参数
String isbn = request.getParameter("isbn");
String title = request.getParameter("title");
String costStr = request.getParameter("cost");
float cost = Float.parseFloat(costStr);
String priceStr = request.getParameter("price");
float price = Float.parseFloat(priceStr);
String pid = request.getParameter("pid");
String categoryIdStr = request.getParameter("categoryId");
int categoryId = Integer.parseInt(categoryIdStr);
// 准备一个Book对象
Book book = new Book();
// 将获取的请求参数数据全部设置到book对象中
book.setIsbn(isbn);
book.setTitle(title);
book.setCategoryId(categoryId);
book.setCost(cost);
book.setPid(pid);
book.setPrice(price);
// 准备一个BookDAO对象
BookDAO bookDAO = new BookDAOImpl();
int result = bookDAO.save(book);
String info = "保存失败";
if(result == 1){
info = "保存成功";
}
resposne.setCharacterEncoding("gbk");
resposne.getWriter().write(info);
}
}
整个流程:
乱码问题
两种情况的乱码:
情况1:数据库与应用程序之间乱码。
解决方案:在数据库连接的url中添加字符集的设置。
情况2:web请求响应中的乱码问题
响应的乱码解决:
①如果响应的是HTML: resp.setContentType("text/html;charset=utf-8");
②如果响应的是字符串: resp.setCharacterEncoding("utf-8");
请求中的乱码:
产生的原因是:客户端提交的编码和服务的编码不一致导致的。
tomcat8以前 tomcat的默认编码是 iso8859-1
. 无论请求是什么格式,在请求中中文参数都会乱码。
tomcat8之后,tomcat的默认编码为utf-8。 我们现在使用的是8.5. 默认就是utf-8。
目前的tomcat8.5中 GET请求的中文不乱码。 POST请求会乱。
POST请求中乱码的解决:
在获取请求参数之前,设置request对象的编码为utf-8.
web项目打包发布
在idea中:
最终tomcat看到的是out这个文件夹下的内容。
Ajax
常说的异步请求。。
第一个ajax应用
我们使用ajax来完成一个简单的请求。
[1]准备一个web项目, 添加一个servlet,可以接收请求参数name和age,给客户端响应一个字符串。
[2]准备一个HTML页面, 添加下面的程序
我们向服务器发送请求的方式
- 浏览器地址栏直接输入访问地址,开始请求
- 超链接(和直接写地址是一样的)
- JS实现超链接
location.href=xxxx?xxx
- 提交表单
上面的所有的提交数据的方式都是由浏览器完成具体的请求。
Ajax的测试程序分析
[1]这个程序中Servlet有没有特殊的要求?
Ajax本身和后端的任何技术都是没有关系。
[2]Ajax不是通过浏览器发送的请求,而是通过XMLHttpRequest对象发送的请求。
[3]XMLHttpRequest的状态(设置回调函数)
XMLHttpRequest本身是有状态的,状态属性是:readyState,有如下几个值:
- 0 (uninitialized) or (request not initialized)(请求对象还没有被创建)
- 1 (loading) or (server connection established)(和服务器完成连接)
- 2 (loaded) or (request received)(请求已经发送)
- 3 (interactive) or (processing request)(正在请求中)
- 4 (complete) or (request finished and response is ready)(请求完成, 响应结果已经收到)
readyState的值一旦发送改变就会触发XMLHttpRequest上的一个事件:onreadystatechange。
所以我们通过设置onreadystatechange的函数,来设置回调函数。
[4]请求发送
请求方法一般都是GET/POST
GET请求,请求参数在url后面。
第三个参数表示是否异步。
如果是true:表示异步,请求发送之后,响应回来之前,后面的处理程序继续执行。
如果是false:表示同步,请求发送之后,响应回来之前,后面的处理程序不能执行。
// 发送请求 (POST请求的时候,这个参数才有用)
httpRequest.send(null);
post请求携带的参数: “name=kakaix&age=180”
httpRequest.send('name=kakaix&age=180');
Ajax的POST请求
按照GET请求的方式写POST请求:
这样的请求参数在后端无法获取。
如果参数依然写在URL后面,是可以携带的。
但是如果参数是在专门的数据区域 send(xxxx)。必须设置一个请求头:
在opne之后,send之前:
// 设置一个Header
httpRequest.setRequestHeader(‘Content-Type’, ‘application/x-www-form-urlencoded’);
跨域的问题
[1]将我们在idea中写的前端的程序直接复制到VSCode中。将url地址修改为完整的访问地址:
[2]在VSCode中或者其他的前端编辑器也一样:
确保我们的HTML页面是在服务器上运行的,不是本地文件打开的
[3]请求服务器测试
浏览器告诉我们,不允许跨域请求。
解释这个问题:
①域名
俗话说的网址: www.baidu.com
其中.com 顶级域名。 baidu 一级域名,从运行商哪里租的。 www是二级域名,二级域名是我们自己随便写的。
任何一个域名都要绑定到一个ip地址上,我们在浏览器中输入的域名,会被DNS服务器解析为IP地址。
②跨域 (一级域名)
所谓跨域请求的意思是:从a域名下的程序中发出ajax请求去请求b域名下的数据。
而跨域是不被允许的……… 浏览器不允许跨域请求数据。
JSONP
[1]script标签的跨域
浏览器不允许跨域请求,但是允许script标签跨域请求。
当前的服务器是:127.0.0.1:5500下
<script src="http://localhost:8080/web/abc.js">script>
这个可以请求。
在idea中添加一个JS文件
启动tomcat。
在VSCode的某一个页面中使用script引入上面写的JS文件
页面效果:
[2]利用jsonp实现跨域请求数据
需求,根据用户的id跨域请求用户的名字。在后端实现一个servlet:
public class UserGetNameServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userId = req.getParameter(“userId”);
System.out.println(“userId = “+userId);
// 响应客户端
resp.getWriter().write(“卡卡西”);
}
}
在前端的任何页面中加入如下的script标签:
tips:script标签的 src就是要请求的接口的url,并且get请求携带参数。
script理论获取的是script的代码,但是这个服务器端返回的数据是一个字符串。
如果我们返回一个可执行的script代码,是不是就可以直接执行了???
思路:
在前端定义一个js函数,这个函数接收一个参数,参数就是username,然后在后端返回调用这个函数的js代码,并且将后端的用户名传入即可。
前端的实现:
<script>
let uname = undefined;
function getUsername(username){
uname = username;
}
修改后端的返回值:
原理:
通过后端的设置允许跨域
前端请求:不需要做任何修改, 就按照常规的请求操作即可。
ajax相关请求的封装
[1]封装一个基础的ajax
在任意一个页面引入上面的js文件:
JSON的基本的语法:
所有的属性都应该使用””包裹。属性值:字符串必须使用””包裹,数字可以不使用””。
一个对象: {“name”:”卡卡西”,”age”:28,”skill”:”千年杀”}
[
{“name”:”卡卡西”,”age”:28,”skill”:”千年杀”},
{“name”:”卡卡西”,”age”:28,”skill”:”千年杀”},
….
]
一个复杂的对象:
{
“name”:”大锤”,
“age”: 28,
“dog”:{“name”:”大黄”,”age”:5},
“girlfriends”:[
{“name”:”如花”,”age”:18},
{“name”:”翠花”,”age”:19},
……
]
}
JS中JSON的处理
将JSON字符串转换为对象:
在java中json字符串和对象之间的转换
在java中将对象转换字符串:拼接字符串。 将字符串转换为对象:解析字符串,创建新的对象,并且赋值。
于是乎,我们考虑使用第三方工具:我们用jackson。
登陆和登陆状态校验
session知识点
一句话:HTTP协议是无状态的协议。所谓无状态就是服务器不记录客户端的访问状态。在通俗一些,就是你每次对服务器的访问,服务器都不知道你是谁。它只知道有一个请求,然后就按照请求给出正确响应。
一个现象:在网页上登陆的淘宝,选了商品加入购物车,准备结算。这时室友喊你去吃饭,吃完饭回来,发现这个淘宝需要重新登陆。
如果服务器不记录状态:你请求购物车页面是一次请求。请求登陆也是一次请求。对于服务器来说这两次请求是没有关系的。显示情况是有关系的。
web项目就是靠session解决HTTP协议无状态的问题。
session处理”会话跟踪”:
当客户端第一次请求服务器的时候,服务器会在服务器端创建一个session(会话)对象,并且给session一个编号(sessionid)。将个这个编号放在cookie中,并且将这个cookie发送给客户端(浏览器,手机端)。客户端下次来访问的时候会带上这个cookie,带上session编号。服务器就根据session编号找到之前的session对象。session本身是一个容器,可以存储一些信息。比如登陆状态信息。如果浏览器清空cookie,再访问服务器的时候,就是一个新的请求,之前的session就失效了。还有个问题,服务器无法检测客户端是否清理了cookie。所以session是否还能继续使用服务器不知道。所以为了防止session一致存在在服务中。如果超过了半小时没有使用的session,直接销毁。
cookie是不能跨域的。
Original: https://www.cnblogs.com/xiaoxiaodeboke/p/16031257.html
Author: 潇潇消消气
Title: javaWeb
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/581273/
转载文章受原作者版权保护。转载请注明原作者出处!