什么是Servlet?

Servlet是运行在Web服务器中的Java程序。Servlet通常通过HTTP(超文本传输协议)接收和响应来自Web客户端的请求。Java Web应用程序中所有的请求-响应都是由Servlet完成。

Servlet的工作流程

浏览器与服务器之间的请求和响应都是遵循HTTP协议的(上一篇有介绍HTTP)。Tomcat会接受并解析HTTP请求文本,然后封装成HttpServletRequest对象,所有的HTTP头数据都可以通过request相应的方法查询到。Tomcat同时会把响应的数据封装为HttpServletResponse类型的response对象,通过设置response属性可以控制输出内容,然后Tomcat会把request、respnse作为参数,调用Servlet的相关方法,例如doGet、doPost等。

Java Web应用程序请求-响应的典型过程如图:

    

编写Servlet

在编写Servlet时,需要继承HttpServlet类,还要考虑到如何让Web容器找到相应的Servlet。

在Servlet3.0之前编写Servlet时需要部署配置文件web.xml(在上一篇有详细介绍)。

Servlet类的编写:

package servlet;

import javax.Servlet.ServletException;
import javax.Servlet.http.HttpServlet;
import javax.Servlet.http.HttpServletRequest;
import javax.Servlet.http.HttpServletResponse;
import java.io.IOException; public class HelloServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { }
}

配置文件的编写:

  <servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>

有没有觉得这样非常麻烦。。。所以在Servlet3.0以后可以使用@WebServlet()注解的方式来告诉Tomcat哪些Servlet会提供服务以及额外信息,而不需要配置文件。

package servlet;

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(
name = "HelloServlet", //Servlet内部名,可以随便设置
urlPatterns = "/login", //用户请求用的URL名称
loadOnStartup = 1 //Servlet初始化顺序,默认为-1,设置为大于0点值,数字越小越先执行
)
public class HelloServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { }
}

Servlet的原理

Servlet API

    

  1. Servlet

    此接口定义了初始化Servlet的方法、为请求提供服务的方法和从服务器移除Servlet的方法,这些方法称为生命周期方法。此外该接口还提供了getServletConfig方法和getServletInfo方法,Servlet可使用前一种方法获得任何启动信息,而后一种方法允许Servlet返回有关其自身的基本信息,比如作者、版本和版权。

    • init(ServletConfig config)

      生命周期方法,由Web容器调用,初始化该Servlet。

    • service(ServletRequest req, ServletResponse res)

      生命周期方法,由Web容器调用,以允许Servlet响应某个请求。此方法仅在Servlet的 init() 方法成功完成之后调用。

    • destroy()

      生命周期方法,由Web容器调用,销毁该Servlet。

    • getServletConfig()

      返回ServletConfig对象,该对象包含此Servlet的初始化和启动参数。返回的ServletConfig对象是传递给 init()方法的对象。此接口的实现负责存储ServletConfig对象,以便此方法可以返回该对象。实现此接口的GenericServlet类已经这样做了。

    • getServletInfo()

      返回有关Servlet的信息,比如作者、版本和版权。此方法返回的是纯文本,不应该是任何种类的标记。

  2. GenericServlet

    定义一般的、与协议无关的Servlet。GenericServlet实现Servlet和ServletConfig接口。GenericServlet的主要目的就是将初始化Servlet的Init方法传入的ServletConfig对象封装起来,GenericServlet也包括了Servlet与ServletConfig所定义方法的简单实现,实现内容主要是通过ServletConfig来取得一些相关信息。GenericServlet在实现Servlet的init方法时,还调用了无参的init()方法,如果有一些初始时需要运行的操作,可以在你编写的Servlet中重写这个无参init方法(),而不是重写有参数的init()方法,原因在下面的Servlet生命周期里有详细的介绍。

       public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
    }
  3. HttpServlet

    继承于GenericServlet,提供将要被子类化以创建适用于 Web 站点的HTTP Servlet的抽象类。HttpServlet的子类至少必须重写一个方法,该方法通常是以下这些方法之一:

    • doGet,如果Servlet支持HTTP GET请求
    • doPost,用于HTTP POST请求
    • doPut,用于HTTP PUT请求
    • doDelete,用于HTTP DELETE请求
    • init 和 destroy,用于管理Servlet的生命周期内保存的资源
    • getServletInfo,Servlet使用它提供有关其自身的信息
  4. MyServlet

    这是你自己编写的Servlet,继承HttpServlet后重写其中的doGet方法或doPost方法即可接收和响应客户端的Http请求。

Servlet生命周期

生命周期:Servlet从创建到消亡的一段时间

Web容器为Servlet创建一个ServletConfig,调用init()时,Servlet才是一个真正的Servlet,之前还只是一个普通的对象(调用构造函数后)。在调用构造函数和init方法之间,Servlet处在薛定谔状态(介于两者之间的一种状态),如果在Servlet状态,你可能有一些Servlet初始化代码,比如得到Web应用配置信息,或请求信息,那么你就会失败。但是只要记住一点,Servlet的构造函数中不放任何东西即可。

   

  1. init()

    默认情况下,Servlet在第一次被访问的时候,才会执行init方法。 有的时候,我们可能需要在这个方法里面执行一些初始化工作,甚至是做一些比较耗时的工作。那么这个时候,初次访问,可能会在init方法中逗留太久的时间。 那么有没有方法可以让这个初始化的时机提前一点?

    • 在web.xml中配置, 使用load-on-startup元素来指定Servlet的启动时机, 初始值为-1,它的值必须是一个整数,我们设置的时候一般为0和大于0的整数,表示在容器启动的时候就加载和初始化Servlet,值越小,Servlet的优先级就越高,启动的时机就越早,当值相同时,由Web容器自己选择优先加载。

      <servlet>
      <servlet-name>HelloServlet</servlet-name>
      <servlet-class>servlet.HelloServlet</servlet-class>
      <load-on-startup>2</load-on-startup>
      </servlet>
    • 在注解中添加loadOnStartup属性,与上面一样。

      @WebServlet(
      name = "HelloServlet", //Servlet内部名,可以随便设置
      urlPatterns = "/login", //用户请求用的URL名称
      loadOnStartup = 1 //Servlet初始化顺序,默认为-1
      )
  2. service

    当一个请求到来的时候,容器会开始一个新线程,或者从线程池中分配一个线程,并调用service()方法,再由service()考虑调用哪一个方法响应请求。

  3. destroy

    当Servlet从服务器中移除或者服务器关闭的时候Servlet对象会被销毁,里面的destroy方法就会执行,然后垃圾回收就会将其回收掉。

四大对象

ServletConfig

Web容器使用的Servlet配置对象,该对象在初始化期间将信息传递给Servlet。通过ServletConfig可以得到一些初始化信息。每个Servlet都有一个属于自己的ServletConfig。

获取方式:getServletConfig()。

ServletConfig对象只有以下四个方法:

  • getInitParameter(String name):根据name返回指定初始化参数的值,如果参数不存在,则返回null。
  • getInitParameterNames():以 String对象的Enumeration的形式返回Servlet的初始化参数的名称,如果 Servlet 没有初始化参数,则返回一个空的Enumeration。
  • getServletContext():返回ServletContext对象的引用。
  • getServletName():返回此Servlet实例的名称,默认是类名。

在使用这些方法之前,需要知道怎么为Servlet添加初始化参数。

web.xml中添加参数:

<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>servlet.HelloServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>kindleheart</param-value>
</init-param>
<init-param>
<param-name>phone</param-name>
<param-value>110</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>

@WebServlet注解中添加参数:

@WebServlet(
name = "HelloServlet", //Servlet内部名,可以随便设置
urlPatterns = "/login", //用户请求用的URL名称
loadOnStartup = 1, //Servlet初始化顺序,默认为-1
initParams = { //添加初始化参数
@WebInitParam(name = "name", value = "kindleheart"),
@WebInitParam(name = "phone", value = "110")
}
)

使用上面四个方法:

public class HelloServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//得到ServletConfig对象
ServletConfig ServletConfig = this.getServletConfig();
//getInitParameter
System.out.println(ServletConfig.getInitParameter("name"));
System.out.println(ServletConfig.getInitParameter("phone"));
//getInitParameterNames
Enumeration<String> names = ServletConfig.getInitParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
System.out.println(ServletConfig.getInitParameter(name));
}
//getServletContext
ServletContext ServletContext = ServletConfig.getServletContext();
//getServletName
System.out.println(ServletConfig.getServletName());
} protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}

ServletContext

Servlet上下文,定义一组方法,Servlet使用这些方法与Web容器进行通信,例如获取文件的 MIME 类型、分发请求或写入日志文件。

每个Web工程下只有一个ServletContext,所有Servlet共用一个ServletContext,ServletContext对象包含在 ServletConfig对象中,ServletConfig对象在初始化Servlet时由Web容器提供给Servlet。

获取方式:getServletContext()、getServletConfig().getServletContext,这两种方式本质上是一样的,都是由ServletConfig在GenericServlet类中返回得到。

ServletRequest对象的应用:

  1. 获取全局参数

    上面的init-param是配置在Servlet标签下的,所以只能由这个Servlet来读取,如果想所有的Servlet都能读取,就需要用到上下文参数,context-param配置在web-app标签下,同样的在获取全局配置参数之前我们先在web.xml中添加全局参数。

      <context-param>
    <param-name>name</param-name>
    <param-value>kindleheart</param-value>
    </context-param>
    <context-param>
    <param-name>phone</param-name>
    <param-value>110</param-value>
    </context-param>

    读取方式与ServletConfig读取初始化参数完全一样。

  2. 获取Web应用中的资源

       protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 告诉客户端该文件不是直接解析,而是以附件形式打开(下载)
    response.setHeader("Content-Disposition", "attachment;filename=" + "tutu.jsp"); //获取文件的绝对路径,再得到输入流
    // String path = this.getServletContext().getRealPath("download/" + "tutu.jsp");
    // InputStream in = new FileInputStream(path);
    //直接得到输入流
    InputStream in = getServletContext().getResourceAsStream("download/tutu.jsp"); // 获得输出流,通过response获得的输出流,用于向客户端写内容
    ServletOutputStream out = response.getOutputStream();
    // 文件拷贝代码
    int len = 0;
    byte[] buffer = new byte[1024];
    while ((len = in.read(buffer)) != -1) {
    out.write(buffer, 0, len);
    }
    in.close();
    out.close();
    }
  3. 在Servlet上下文存取数据

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //在Servlet上下文添加属性
    getServletContext().setAttribute("name","图图");
    //在Servlet上下文获取这个属性值
    String name = (String) getServletContext().getAttribute("name");
    }

HttpServletRequest

客户端发来的请求被封装成一个HttpServletRequest对象。所有的信息包括请求的地址,请求的参数,提交的数据,上传的文件,客户端IP地址,甚至是客户端操作系统都包含在内。

HttpServletRequest对象的应用:

  1. 获取请求头信息

     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //获取所有请求头信息
    Enumeration<String> headerNames = request.getHeaderNames();
    while (headerNames.hasMoreElements()) {
    String name = headerNames.nextElement();
    String value = request.getHeader(name);
    System.out.println(name + ":" + value);
    }
    }
  2. 获取表单传来的数据

    <form action="login" method="post">
    姓名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="checkbox" name="hobby" value="打球">打球
    <input type="checkbox" name="hobby" value="游泳">游泳
    <input type="checkbox" name="hobby" value="滑雪">滑雪<br>
    <input type="submit" value="提交">
    </form>
     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //设置字符编码为UTF-8
    request.setCharacterEncoding("utf-8");
    response.setContentType("text/html;charset=utf-8");
    //一个name对应一个值
    String username = request.getParameter("username");
    String password = request.getParameter("password"); //一个name对应多个值
    Map<String, String[]> map = request.getParameterMap();
    Set<String> keySet = map.keySet();
    for(String key : keySet) {
    System.out.println("key=" + key + "的值的总数有" + map.get(key).length);
    for(String value : map.get(key)) {
    System.out.println(value);
    }
    }
    }
  3. 请求转发

    在Web应用中,经常需要多个Servlet共同处理请求,那么就需要使用请求转发。请求转发是通过RequestDispatcher对象的forword(req, res)来实现的。这个动作是在Web容器中进行的,浏览器并不知道请求被转发。当forward跳转时,地址栏的访问地址不改变,跳转后的Servlet或Jsp能使用上一个Servlet里的属性,经常用于Servlet处理业务逻辑,然后Jsp来显示处理结果。

       protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //在request域中写入属性
    request.setAttribute("username","kindleheart");
    request.setAttribute("password","123");
    //请求转发,index.jsp可以使用该Servlet里的属性
    request.getRequestDispatcher("index.jsp").forward(request, response);
    }

HttpServletResponse

服务器使用HttpServletResponse来对浏览器进行响应。

HttpServletResponse的应用:

  1. 设置响应头

    在前面的ServletContext上下文获取资源时,浏览器是默认直接显示该资源的,如果你想下载而不是直接显示,就会需要设置响应头,让浏览器知道你要下载而不是显示。

    // 告诉客户端该文件不是直接解析,而是以附件形式打开(下载)
    response.setHeader("Content-Disposition", "attachment;filename=" + "tutu.jsp");
  2. 使用getWriter输出字符

     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html;charset=utf-8");
    response.getWriter().println("输出字符");
    }
  3. 使用getOutputStream

    还是在前面的 ServletContext 获取资源中,需要把资源以输出流的形式进行下载,那就需要用到getOutputStream来以二进制形式写入。

    // 获得输出流,通过response获得的输出流,用于向客户端写内容
    ServletOutputStream out = response.getOutputStream();
  4. 重定向

    HttpServletRequence的sendRedirect要求浏览器重新请求一个URL,称为重定向。重定向是利用服务器返回的状态码决定的,浏览器在访问服务器的时候,服务器会返回一个状态码。可以使用HttpServletResponse的setStatus方法设置状态码。如果服务器返回301,或者302,那么浏览器会到新的网址请求资源,地址栏上的访问地址会改变。重定向和请求转发不一样。因为是重新访问地址,所以重定向后的Servlet或Jsp不能使用之前Servlet的属性。

    状态码 意义
    1xx 信息状态码。表示该请求已经被接受。正在被处理。
    2xx 正确状态码。表示该请求已经被正确接受且处理,没有错误发生。例如,200表示一切正确。
    3xx 重定向状态码。例如301,302表示该资源已经不存在或换了地址,需要重新定向到一个新的资源。
    4xx 请求错误。例如401表示没有访问权限,404表示资源不存在,405表示访问方式错误。
    5xx 服务器错误。例如500表示程序出现异常而中途停止运行。

    使用:

    //重定向到index.jsp
    response.sendRedirect("index.jsp");

    sendRedirect方法其实是在响应中设置了Status和Location标头,所以使用以下代码就可以实现重定向,这也是重定向的原理。

    //设置状态码
    response.setStatus(301);
    //定位跳转的位置
    response.setHeader("Location", "index.jsp");
  5. 自动刷新

    response对象可以使用setHeader设置方法响应头来实现自动刷新,自动刷新不仅可以实现一段时间跳转到其他页面,还可以实现一段时间后自动刷新本页面。

    response.getWriter().println("注册成功!3秒之后会跳转到主页");
    response.setHeader("refresh", "3;index.jsp");

    其中3代表3秒,index.jsp是指定的跳转网址的URL,如果URL设置的路径为Servlet自己的路径,就会每隔3秒刷新自己一次。

重定向与请求转发的区别

本质区别:请求转发只发出了一次请求,而重定向发出了多次请求。

  • 请求转发:地址栏是初次发出请求的地址

    重定向:地址栏不再是初次发出的请求地址,地址栏为最后响应的那个地址

  • 请求转发:在最终的Servlet中,request对象和中转的那个request是同一个对象

    重定向:在最终的Servlet中,request对象和中转的那个request不是同一个对象

  • 请求转发:只能转发给当前WEB应用的资源

    重定向:可以重定向到任何资源

    response.sendRedirect("http://www.baidu.com");是可以的,请求转发就不行。

  • 请求转发:/ 代表的是当前WEB应用的根目录(http://localhost:8080/项目名称/)

    重定向: / 代表的是当前WEB站点的根目录(http://localhost:8080/)

注意:这两条跳转语句不能同时出现在一个页面中,否则会报IllegalStateException - if the response was already committed的错误。

Servlet中文乱码问题

出现乱码问题的原因是Web容器默认是以ISO-8859-1进行字符编码的。而一般使用的过程中都是以UTF-8的格式,那么就会出现乱码问题。

  • HttpServletRequest出现中文乱码

    • POST请求

      //设置request的字符编码格式为utf-8
      request.setCharacterEncoding("utf-8");
    • GET请求

      String value = request.getParameter("value");
      value = new String(value.getBytes("iso-8859-1"), "utf-8");
      System.out.println(value);
  • HttpServletResponse出现中文乱码

    //设置response的字符编码格式为utf-8
    response.setContentType("text/html;charset=utf-8");

如果要接收中文数据,并在响应时通过浏览器正确显示中文,那么这两个方法需要同时进行设置。

Servlet是否为线程安全

线程安全问题指的是多线程在并发执行时会不会出现问题。由于Web容器只会创建一个Servlet实例,所以多个用户发起请求时,会有多个线程处理Servlet代码,因此Servlet是线程不安全的。

考虑以下代码:

@WebServlet(name = "ThreadSafeServlet", urlPatterns = "/ThreadSafeServlet")
public class ThreadSafeServlet extends HttpServlet {
private String name;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
name = request.getParameter("name");
try {
Thread.sleep(10000);//使线程沉睡10秒
} catch (Exception e) {
e.printStackTrace();
}
response.getWriter().println("name:" + name);
} protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}

10秒内在两个不同的浏览器窗口中的表单输入name并提交,假如在A浏览器中输入111,B浏览器中输入222,最后会发现A和B浏览器显示的name都是222。这是因为在第一个线程睡眠时,第二个线程修改了name的值,所有最后显示都是222,那么就产生了线程不安全问题。

实际上Servlet,Context上下文作用域,HttpSession都是线程不安全的,只有request请求和局部变量是线程安全的。

Java Web(二) Servlet详解的更多相关文章

  1. Java Web(一) Servlet详解!!

    这篇文章到上一篇,距离的有点遥远呀,隔了大概有两个月把,中间在家过了个年,哈哈~ 现在重新开始拾起,最近在看一本个人觉得很棒的书,<Java Web 整合开发王者归来>,现在写的这一系列基 ...

  2. (转)Java Web(一) Servlet详解!!

    https://www.cnblogs.com/whgk/p/6399262.html 这篇文章到上一篇,距离的有点遥远呀,隔了大概有两个月把,中间在家过了个年,哈哈~ 现在重新开始拾起,最近在看一本 ...

  3. Java Web(5)-Servlet详解(下)

    一.HttpServletRequest 类 1. HttpServletRequest 类作用? 每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解 ...

  4. Java Web(5)-Servlet详解(上)

    一.Servlet 1. 什么是Servlet Servlet 是 JavaEE 规范之一,规范就是接口 Servlet 就 JavaWeb 三大组件之一,三大组件分别是:Servlet 程序.Fil ...

  5. java web之Filter详解

    java web之Filter详解 2012-10-20 0 个评论 作者:chenshufei2 收藏 我要投稿 .概念: Filter也称之为过滤器,它是Servlet技术中比较激动人心的技术,W ...

  6. java web.xml配置详解(转)

    源出处:java web.xml配置详解 1.常规配置:每一个站的WEB-INF下都有一个web.xml的设定文件,它提供了我们站台的配置设定. web.xml定义: .站台的名称和说明 .针对环境参 ...

  7. 《Tomcat与Java Web开发技术详解》思维导图

    越想构建上层建筑,就越觉得底层基础很重要.补课系列. 书是良心书,就是太基础了,正适合补课. [纯文字版] Tomcat与Java Web开发技术详解 Servlet Servlet的生命周期 初始化 ...

  8. Java web.xml 配置详解

    在项目中总会遇到一些关于加载的优先级问题,近期也同样遇到过类似的,所以自己查找资料总结了下,下面有些是转载其他人的,毕竟人家写的不错,自己也就不重复造轮子了,只是略加点了自己的修饰. 首先可以肯定的是 ...

  9. java web.xml配置详解

    1.启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取<listener>和<context-param>两个结点. 2.紧急着,容创建一个Servl ...

随机推荐

  1. CentOS6.5下搭建ftp服务器(三种认证模式:匿名用户、本地用户、虚拟用户)

    CentOS 6.5下搭建ftp服务器 vsftpd(very secure ftp daemon,非常安全的FTP守护进程)是一款运行在Linux操作系统上的FTP服务程序,不仅完全开源而且免费,此 ...

  2. [osg][opengl]透视投影的参数Perspective

    gluPerspective这个函数指定了观察的视景体(frustum为锥台的意思,通常译为视景体)在世界坐标系中的具体大小,一般而言,其中的参数aspect应该与窗口的宽高比大小相同.比如说,asp ...

  3. opencv4.0 cuda10 编译速度太慢

    opencv4.0 cuda10 对比不带cuda的时候,编译速度太慢,主要卡在cuda的编译上. 参考http://answers.opencv.org/question/5090/why-open ...

  4. 修改Anaconda中的Jupyter Notebook默认工作路径

    这二天,安装了anaconda想更改jupyter的工作路径,在网上找了一下 方式1. 打开Windows的cmd,在cmd中输入jupyter notebook --generate-config如 ...

  5. 记一次RAID阵列的迁移经历

    xu言: 最近,某电信机房因为空调漏水问题导致了我司的Dell R430 服务器的主板及CPU不同程度受损.服务器已经不能正常开机.但是,又基于把服务器的数据需要最短时间进行恢复.抱着试试看的心里进行 ...

  6. LeetCode--374--猜数字大小

    问题描述: 我们正在玩一个猜数字游戏. 游戏规则如下: 我从 1 到 n 选择一个数字. 你需要猜我选择了哪个数字. 每次你猜错了,我会告诉你这个数字是大了还是小了. 你调用一个预先定义好的接口 gu ...

  7. 20181013xlVba计算优秀率及合格率

    Sub 计算高一优秀合格率() Dim Wb As Workbook Dim Sht As Worksheet Dim oSht As Worksheet Dim dOs As Object 'Out ...

  8. Django的form组件

    forms组件 forms组件,是一个类.在视图函数中创建一个类,类需要继承forms.Form from django import  forms 1.校验数据 步骤和语法: 1. 创建一个form ...

  9. tomcat8 tomcat-users相关配置

    第一步:修改账号密码 vim conf/tomcat-users.xml <role rolename="manager-gui"/> <role rolenam ...

  10. JS实现下拉单的二级联动

    因工作需要,做了一个下拉单的二级联动. 第一级是固定的选项,有A.B两个选项,第二级的选项随着第一级选项的变化而变化. 一开始是这样的: HTML代码 <html> <head> ...