【数据库06】web应用程序开发的任督二脉 1

简介: 【数据库06】web应用程序开发的任督二脉

1.应用程序和用户界面

互联网用户和数据库往往不会直接打交道,而是通过应用程序对数据库进行间接访问。

在计算机发展早期,应用程序在大型主计算机上运行,用户通过终端与应用程序交互。

个人计算机的发展导致了带有图形的用户界面GUI的数据库应用的发展。程序在个人计算机上运行,这些代码直接与一个共享的数据库进行通信。这种模式被称为客户-服务器体系结构

ff422b2e49414acf8f3e998e6fdeabf0.jpg

这种模式至少有两个问题:


用户机器可以直接访问数据库,从而带来安全性问题。

维护困难。对应用程序或数据库的任何更改(扩展、更新、修改等)都要求位于客户计算机上的应用程序的所有副本一起更改(重新部署软件)。

现在有两种方法用于避免上述问题。

-browser/server。web浏览器提供前端,通过前端访问后端。这样就不需要单独在客户机安装、维护软件。同时,与c语言编写的程序不同,前端的脚本语言JavaScript可以运行在安全模式下,保证不会导致安全问题。

d0d60f9101c845ee82f86464564c69db.jpg


应用程序安装在独立设备上。这些设备主要是移动设备,它们通过API与后端应用程序进行通信,并不能直接访问数据库。

2.Web基础

2.1 同一资源定位符

统一资源定位符(Uniform Resource Locator)是web上可以访问的每份文档的全球唯一名称。比如:

http://www.acm.org/sigmod

上面的URL由三部分组成,http是超文本传输协议,“https”是“http”的安全版本,并且是当今的首选模式。第二部分是一台具有web服务器的机器名称。第三部分是该机器上文档的路径或者唯一标识。


URL还可以包含位于web服务器上程序的标识,以及传递给该程序的参数。并由该程序返回一个html文档给web服务器。

http://www.google.com/search?q=silberschatz

2.2 超文本标记语言

下图就是一个html创建一个表单的过程

25032837186440efb863b62801d35fbf.jpg

Http定义了两种请求方式,上图所示的get请求将请求参数作为URL的一部分,另外一种请求Post请求则发送一个请求,将参数值作为web服务器和浏览器之间交换的HTTP协议的一部分发送。


有些编辑器支持使用图形界面来直接创建HTML文本编辑器。


CSS支持给html提供样式。


HTML5支持多种形式的输入类型,比如日期和时间选择,文件选择,还支持对输入采取限制(最大值,最小值等)


2.3 web服务器和会话

Web服务器是运行于服务机上的程序,它接收浏览器的请求,根据其提供的参数执行程序,最后将结果以HTML文档的形式将结果传送回去。


下图显示了一个使用三层体系结构搭建的web应用程序。通用网关接口(CGI)标准定义了web服务器如何与应用程序进行通信。使用多层服务器增加了系统的开销,CGI接口为每个请求都启动一个新的进程为之服务,这导致了更大的开销。


因此目前大部分的应用程序将web服务器和应用服务器合二为一,采用两层web应用程序体系结构。

4380898c18b14fb89431ea4f31750d85.jpg


用户通过JDBC或者ODBC来访问数据库时,则会建立一个会话,会话信息会一直保存,直到该会话终止。但是客户端和web服务器之间不存在长连接,往往是连接-请求-响应-关闭连接的方式,这是为了更多的容载海量的用户访问,降低连接限制带来的影响。


尽管连接会关闭,但是web应用程序也需要会话信息来允许有意义的用户交互,对用户进行认证等。它的策略是,会对每一次会话进行一次用户认证,会话的进一步交互无需进行认证。


为了实现会话,需要在客户端存储额外的信息。这些额外的信息通常以cookie的形式维护在客户端,一个cookie是一段包含标识信息的文本,并且与一个名称相关联。例如,google.com可能设置一个名为prefs的cookie,它对用户的偏好设置进行编码,比如语言偏好和每页显示的结果数目,对于每个搜索请求,google.com都能够从用户的浏览器得到这个名为prefs的cookie,然后根据其指定的偏好来显示结果。一个域(Web站点)只允许获取它自己设置的cookie,而不能获取其他域所设置的cookie,而且cookie的名称可以跨域重用。

45aa1f86c1bc41a58f9037d65be0ba07.png

为了跟踪用户会话,服务端会生产一个名为seesionid的cookie,这是一个特殊的cookie,它用于区分不同的会话,因此也会在服务端存储。当一个请求进来时,应用服务器从客户端请求名为seesionid的cookie,如果客户端没有存储该cookie,或者返回的值与服务端存储的有效会话标识不同,就认为该请求不是当前会话的一部分。


a11d2c68f38947f0b8aa1a1e7f475d26.png

对于安全性要求不高的应用,比如公共新闻站点,cookie可以永久的存储在浏览器端和服务器段。他们识别初用户对一个站点的后续访问,而不需要输入任何验证信息。


对于安全性高的应用,则可能会设置时间限制,在超时后或者用户注销(退出登录)时使会话失效,使会话失效其实就是将会话标识从服务端删除。


3.Servlet

java servlet(Java服务端程序)规范定义了一种用于在WEB/应用服务器与应用程序之间进行通信的应用编程接口。Java的HttpServlet类实现了Servlet API的规范。


3.1 Servlet示例

我们卡妈妈提到了如下的一个表单请求。

25032837186440efb863b62801d35fbf.jpg

现在假设该请求被提交给后端,我们编写下对应的后端处理逻辑代码,看看后端要怎么应对前端的请求

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
@WebServlet("PersonQuery")
public class PersonQueryServlet extends HttpServlet {
  public void doGet(HttpServletRequest request,
           HttpServletResonse response)
      throws ServletException, IOException
      {
        response.setContentType("text/html");
        PrintWriter out = resonse.getWriter();
        ...检查用户是否已登录...
        out.println("<HEAD><TITLE>Query Result</TITLE></HEAD>");
        out.println("<BODY>");
        String persontype = request.getParameter("persontype");
        String name = request.getParameter("name");
        if(persontype.equals("student")) {
          ...寻找具有指定姓名的学生的代码...
          ...使用JDBC与数据库进行通信...
          ...假设已获取到ResultSet rs,并且...
          ...包含属性:ID、姓名与系名...
          String headers = new String[]{"ID","Name","Department Name"};
          Util::resultSetToHtml(rs,headers,out);
        }
        else {
          ...同上,但是对于教师的...
        }
        out.println("</Body>");
        out.close();
      }
}

我们终于知道服务器对HTTP请求的原理了,当服务器接收到请求来执行一个特定的servlet时,servlet的代码被加载到Web/应用服务器中,servlet的任务就是处理这种请求,访问数据库以检索出必要的信息,并动态生成一个HTML页面返回给客户端浏览器。


我们注意到,前端指定了ation = "PersonQuery",而后端则使用注解@WebServlet("PersonQuery")来显示的注释当前servlet是用来处理对PersonQuery的请求的。而且前端的表单指定使用HTTP的Get机制,因此servlet的doGet()方法将会被执行。


每次servlet请求都导致在执行调用的内部生成一个新的线程,因此多个请求就可以被并行处理。


请求将cookie和参数放入一个HttpServletRequest对象 中,后端通过api对其进行提取,根据提取参数执行数据库的查询工作,最后将其通过HttpServletResonse 对象返回。


结果是这样输出给response的,我们通过它获取了一个PrintWriter 对象,将要返回的html通过该对象输出,其中查询到的数据输出的方式是Util.resultSetToHtml()实现的。其参考代码如下。

825e8bbd284344f5b47482330b035df6.jpg


3.2 Sevlet会话

cookie可以用来识别一个请求与前一个请求是否来自同一个浏览器会话。其在后端servlet处理的逻辑是怎么样的呢?


servlet的API中提供了跟踪会话技术的方法。调用HttpServletRequest中的getSession(false)可以获取来自浏览器的HttpSession对象。当该方法被调用是,将会首先要求哦i客户端返回一个具有指定名称的cookie,如果没有该cookie,则说明该请求不是正在进行的会话的一部分。


此时getSession会返回一个空值,引导用户到登入页面。登录页面允许用户提供用户名和密码,登录页面所对象的servlet会验证用户的信息。


如果用户通过认证,登录servlet会话会执行getSession(true),这个方法会创建一个新的会话。为了创建一个新的会话,服务器内部会执行如下任务:在客户端浏览器中设置一个cookie(比如名为sessionId),该cookie用会话标识作为它所关联的值。创建一个新的会话对象,并将会话标识的值与该会话对象相关联。


servlet代码还能够在HttpSession对象中存储和查找(属性,值)对,以便在一个会话内的多个请求之间维持状态。比如,用户通过登录认证并且会话对象被创建之后,可以将userid存储为会话的一个参数,标记该用户已经通过了登录认证:

session.setAttribute("userid",userid)

那么我们只需要取出userid,就可以判断会话用户有没有通过登录认证。如果没有,就可以通过下面的方式让其回到登录页。

b864293aab014934b8080f7096453690.jpg


为了详细说明上面的内容,我们看如下登录案例(转载)。


需要的页面:


login.jsp:登录页面,提供登录表单;


index1.jsp:主页,显示当前用户名称,如果没有登录,显示您还没登录;


index2.jsp:主页,显示当前用户名称,如果没有登录,显示您还没登录;


Servlet:


LoginServlet:在login.jsp页面提交表单时,请求本Servlet。在本Servlet中获取用户名、密码进行校验,如果用户名、密码错误,显示“用户名或密码错误”,如果正确保存用户名session中,然后重定向到index1.jsp;


当用户没有登录时访问index1.jsp或index2.jsp,显示“您还没有登录”。如果用户在login.jsp登录成功后到达index1.jsp页面会显示当前用户名,而且不用再次登录去访问index2.jsp也会显示用户名。因为多次请求在一个会话范围,index1.jsp和index2.jsp都会到session中获取用户名,session对象在一个会话中是相同的,所以都可以获取到用户名!

login.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>login.jsp</title>
  </head>
  <body>
    <h1>login.jsp</h1>
    <hr/>
    <form action="/day06_4/LoginServlet" method="post">
    用户名:<input type="text" name="username" /><br/>
        <input type="submit" value="Submit"/>
    </form>
  </body>
</html>

index1.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>index1.jsp</title>
  </head>
  <body>
<h1>index1.jsp</h1>
<%
    String username = (String)session.getAttribute("username");
    if(username == null) {
       out.print("您还没有登录!");
    } else {
       out.print("用户名:" + username);
    }
%>
<hr/>
<a href="/day06_4/index2.jsp">index2</a>
  </body>
</html>

index2.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>index2.jsp</title>
  </head>
  <body>
<h1>index2.jsp</h1>
<%
    String username = (String)session.getAttribute("username");
    if(username == null) {
       out.print("您还没有登录!");
    } else {
       out.print("用户名:" + username);
    }
%>
<hr/>
<a href="/day06_4/index1.jsp">index1</a>
  </body>
</html>

LoginServlet

public class LoginServlet extends HttpServlet {
    public void doPost(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
       request.setCharacterEncoding("utf-8");
       response.setContentType("text/html;charset=utf-8");
       String username = request.getParameter("username");
       if(username.equalsIgnoreCase("cloud")) {
           response.getWriter().print("用户名或密码错误!");
       } else {
           HttpSession session = request.getSession();
           session.setAttribute("username", username);
           response.sendRedirect("/day06_4/index1.jsp");
       }
    }
}

3.3 Servlet的生命周期

b7562756fdbf449c9419dcc4cab925f6.jpg


servlet的生命周期由部署它的web/应用服务器来控制,当由客户端请求一个特定的servlet时,服务器首先检查是否存在该servlet的一个实例。如果不存在,web服务器就将servlet类加载进java虚拟机,并创建一个servlet类的实例。另外,服务器调用init()方法来初始化该servlet实例。每个servlet实例仅在它被加载的时候被初始化一次。


在确定servlet实例存在后,服务器调用servlet的service方法,并以一个request对象和一个response对象作为参数,在缺省的情况下,服务器创建一个新的线程执行service方法,因此,一个servlet上的多个请求就可以并行执行,而不必等待之前的线程执行完。service方法视情况调用doGet或doPost.


当不再需要的时候,可以通过调用destroy()方法来关闭一个servlet。服务器可以设置一个超时时限,如果在超时时限内没有对一个servlet进行过一个请求,则自动关闭该servlet。超时实现是一个参数,可以根据应用来适当的对它进行设置。

3.4 应用服务器

最知名的servlet应用服务器是Apache的Tomcat。


321d1b88d1074da48b19df407ffb78f3.jpg


开发Servlet的应用程序的最佳方式是使用Idea,eclipse等Ide编辑器,他们内置有Tomcat服务器。


除了最基本的servlet支持之外,应用服务器通常还提供了各种有用的服务,它们允许应用程序被部署或者被停止,并且它们提供了监控应用服务器的功能,包括性能监控。还支持跨多个应用服务器的并行处理,处理对象等。


相关文章
|
2月前
|
存储 缓存 NoSQL
在Python Web开发过程中:数据库与缓存,Redis在Web开发中的常见应用场景有哪些?
Redis在Python Web开发中常用于缓存、会话管理、分布式锁、排行榜、消息队列和实时分析。作为内存数据存储,它提供高效的数据结构(如字符串、哈希、列表、集合、有序集合),支持会话存储、互斥操作、计数与排名、队列实现及实时数据处理。其高速性能和丰富功能使其成为多场景下的理想选择。
26 5
|
10天前
|
SQL Java 数据库连接
17:数据库连接池与Servlet整合-Java Web
17:数据库连接池与Servlet整合-Java Web
22 3
|
12天前
|
API 数据库 Python
Python web框架fastapi数据库操作ORM(二)增删改查逻辑实现方法
Python web框架fastapi数据库操作ORM(二)增删改查逻辑实现方法
|
13天前
|
关系型数据库 MySQL API
Python web框架fastapi数据库操作ORM(一)
Python web框架fastapi数据库操作ORM(一)
|
17天前
|
缓存 NoSQL 关系型数据库
在Python Web开发过程中:数据库与缓存,MySQL和NoSQL数据库的主要差异是什么?
MySQL与NoSQL的主要区别在于数据结构、查询语言和可扩展性。MySQL是关系型数据库,依赖预定义的数据表结构,使用SQL进行复杂查询,适合垂直扩展。而NoSQL提供灵活的存储方式(如JSON、哈希表),无统一查询语言,支持横向扩展,适用于处理大规模、非结构化数据和高并发场景。选择哪种取决于应用需求、数据模型及扩展策略。
26 0
|
17天前
|
SQL 缓存 数据库
在Python Web开发过程中:数据库与缓存,如何使用ORM(例如Django ORM)执行查询并优化查询性能?
在Python Web开发中,使用ORM如Django ORM能简化数据库操作。为了优化查询性能,可以:选择合适索引,避免N+1查询(利用`select_related`和`prefetch_related`),批量读取数据(`iterator()`),使用缓存,分页查询,适时使用原生SQL,优化数据库配置,定期优化数据库并监控性能。这些策略能提升响应速度和用户体验。
18 0
|
1月前
|
SQL Java 数据库连接
使用JDBC进行数据库操作:Java Web开发的数据库连接
【4月更文挑战第3天】Java Web开发中,JDBC是与数据库交互的关键,提供统一访问关系型数据库的规范。核心组件包括DriverManager、Connection、Statement和ResultSet。使用流程涉及加载驱动、建立连接、创建Statement、执行SQL及处理结果,最后关闭资源。最佳实践包括使用try-with-resources、PreparedStatement、事务管理等。在Web开发中,JDBC用于用户认证、数据持久化、检索和事务管理。虽然有ORM工具,但掌握JDBC基础仍然重要。
|
2月前
|
缓存 NoSQL Redis
在Python Web开发过程中:数据库与缓存,除了Redis是内存数据库以外,还有哪些原因使其运行速度快?
Redis在Python Web开发中快速的原因:内存存储、多样化数据结构(如字符串、哈希、列表等)简化数据模型,单线程处理提高效率,结合非阻塞I/O;RDB和AOF提供持久化保障;TCP+二进制协议减少网络开销;管道技术提升通信效率。这些设计使Redis能高效处理高并发请求。
20 3
|
2月前
|
NoSQL 关系型数据库 Linux
Star 1.6k!当Web遇上Linux和数据库!一站式管理平台的开源之旅!
Star 1.6k!当Web遇上Linux和数据库!一站式管理平台的开源之旅!
|
2天前
|
关系型数据库 MySQL 数据库
docker MySQL删除数据库时的错误(errno: 39)
docker MySQL删除数据库时的错误(errno: 39)