Servlet入门及常遇问题

1 编写流程:简单例子

1)在网页端提交表单数据,根据url调用tomcat服务器中的Servlet程序(http://localhost:8080/ServletTest1/register),这个注册请求会调用tomcat中的名字为register的servlet程序
2)根据上一步所知,在web.xml中找到 /register,其对应的类是com.book.web.RegisterServle

	<servlet>
        <servlet-name>register</servlet-name>
        <servlet-class>com.book.web.RegisterServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>register</servlet-name>
        <url-pattern>/register</url-pattern>
    </servlet-mapping>     

3)编写相应的Servlet程序

public class RegisterServlet extends HttpServlet {
    
    
    // private   UserService userservice=new UserServiceImpl();  在这里声明变量会报错,因为在web下的jar没有导入到WEB-INF的lib中

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("doget");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        UserService userservice=new UserServiceImpl();      //
        //密码参数不显示
         //1.获取参数
       // Connection connetion = jdbcutils.getConnetion();
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String email = req.getParameter("email");
        String code = req.getParameter("code");
        System.out.println(username);
        //2.检查验证码合法与否
        if("abcde".equals(code)){
    
    
            if(userservice.existUsername("root"))
                System.out.println("用户名已经存在");
            else{
    
    //将用户数据保存进数据库,使用service,间接调用的Dao
                System.out.println("存进数据库");
                userservice.registerUser(new user(username,password,email));
                }
                RequestDispatcher requestDispatcher = req.getRequestDispatcher("/pages/user/regist_success.html");
                requestDispatcher.forward(req,resp);
            }
     else{
    
     //不合法,返回注册页面
            System.out.println("验证码错误");
            RequestDispatcher requestDispatcher = req.getRequestDispatcher("/pages/user/regist.html");
            requestDispatcher.forward(req,resp);
        }
    }
}

2 Servlet类的简介

1 Servlet类

public interface Servlet {
    
    
//生命周期方法:当Servlet第一次被创建对象时执行该方法,该方法在整个生命周期中只执行一次
    void init(ServletConfig var1) throws ServletException;
//生命周期方法:对客户端响应的方法,该方法会被执行多次,每次请求该servlet都会执行该方法
    ServletConfig getServletConfig();

    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}

2 GenericServlet类

在 GenericServlet 中,主要完成了以下任务:

  • 将init( )方法中的ServletConfig参数赋给了一个内部的ServletConfig引用从而来保存ServletConfig对象,不需要程序员自己去维护ServletConfig了。
  • 为 Servlet 所有方法提供默认实现,不再需要把所有的方法都自己实现了。
  • 可以直接调用 ServletConfig 中的方法
1)WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用
2ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象
3)由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。
abstract class GenericServlet implements Servlet,ServletConfig{
    
    
 
   //GenericServlet通过将ServletConfig赋给类级变量
   private transient ServletConfig config;
 
   public void init(ServletConfig servletConfig) throws ServletException {
    
    

      this.config=servletConfig;

      /*自定义init()的原因是:如果子类要初始化必须覆盖父类的init() 而使它无效 这样
       this.servletConfig=servletConfig不起作用 这样就会导致空指针异常 这样如果子类要初始化,
       可以直接覆盖不带参数的init()方法 */
      this.init();
   }
   
   //自定义的init()方法,可以由子类覆盖  
   //如果没有这个方法,子类继承的时候,会覆盖掉上述带参的init方法
   //导致config无法初始化
   //init()不是生命周期方法
   public void init(){
    
    
  
   }
 
   //实现service()空方法,并且声明为抽象方法,强制子类必须实现service()方法 
   public abstract void service(ServletRequest request,ServletResponse response) 
     throws ServletException,java.io.IOException{
    
    
   }
 
   //实现空的destroy方法
   public void destroy(){
    
     }
}

3 HttpServlet

       HttpServlet 中对原始的 Servlet 中的方法都进行了默认的操作,不需要显式的销毁初始化以及 service(),在 HttpServlet 中,自定义了一个新的 service() 方法其中通过 getMethod() 方法判断请求的类型,从而调用 doGet() 或者 doPost() 处理 get,post 请求,使用者只需要继承 HttpServlet,然后重写 doPost() 或者 doGet() 方法处理请求即可。
1)将ServletRequest类型请求转化为HttpServletRequest

abstract class HttpServlet extends GenericServlet{
    
    
 
   //HttpServlet中的service()
   protected void service(HttpServletRequest httpServletRequest,
                       HttpServletResponse httpServletResponse){
    
    
        //该方法通过httpServletRequest.getMethod()判断请求类型调用doGet() doPost()
   }
 
   //必须实现父类的service()方法
   public void service(ServletRequest servletRequest,ServletResponse servletResponse){
    
    
      HttpServletRequest request;
      HttpServletResponse response;
      try{
    
    
         request=(HttpServletRequest)servletRequest;
         response=(HttpServletResponse)servletResponse;
      }catch(ClassCastException){
    
    
         throw new ServletException("non-http request or response");
      }
      //调用service()方法
      this.service(request,response);
   }
}

2)在service方法中,根据http协议要求,将请求分为不同的方法,主要还是调用get和put方法

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
    String method = req.getMethod();
    long lastModified;
    if (method.equals("GET")) {
    
    
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
    
    
            this.doGet(req, resp);
        } else {
    
    
            long ifModifiedSince = req.getDateHeader("If-Modified-Since");
            if (ifModifiedSince < lastModified) {
    
    
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } else {
    
    
                resp.setStatus(304);
            }
        }
    } else if (method.equals("HEAD")) {
    
    
        lastModified = this.getLastModified(req);
        this.maybeSetLastModified(resp, lastModified);
        this.doHead(req, resp);
    } else if (method.equals("POST")) {
    
    
        this.doPost(req, resp);
    } else if (method.equals("PUT")) {
    
    
        this.doPut(req, resp);
    } else if (method.equals("DELETE")) {
    
    
        this.doDelete(req, resp);
    } else if (method.equals("OPTIONS")) {
    
    
        this.doOptions(req, resp);
    } else if (method.equals("TRACE")) {
    
    
        this.doTrace(req, resp);
    } else {
    
    
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[]{
    
    method};
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
    }
 
}

4 doGet和doPost

底层都是TCP连接
1)但GET一般用于获取/查询资源信息,而POST一般用于更新资源信息
2)GET参数通过URL传递,POST放在Request body中
3)由于上一个不同,所以两者传递的参数长度限制不同:其实两者都没有长度限制,但是url是在浏览器地址栏,所以会被浏览器限制;而post利用requestBody则没有限制(100K)
4)POST的安全性要比GET的安全性高,也是因为第2个不同导致的(url参数在地址栏可见)

      在jsp中的form中,默认的提交方式就是get

5 Filter

Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
1)编写Fliter程序

public class AdminFilter implements Filter {
    
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    
    

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        HttpServletRequest httpServletRequest= (HttpServletRequest) servletRequest;
        String username = (String) httpServletRequest.getSession().getAttribute("username");
        //System.out.println(username);
       // System.out.println(username!="admin");  对于String而言,使用==/!=判断的是地址
        if("admin".equals(username)){
    
    
            //链式处理,通过则放行
            filterChain.doFilter(servletRequest,servletResponse);
        }
        else {
    
    
            httpServletRequest.getRequestDispatcher("/pages/user/login.jsp").forward(servletRequest,servletResponse);

    }
    }

    @Override
    public void destroy() {
    
    

    }
}

2)在web.xml中配置

<filter>
        <filter-name>AdminFilter</filter-name>
        <filter-class>com.book.filter.AdminFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AdminFilter</filter-name>
        <url-pattern>/pages/manager/*</url-pattern>
    </filter-mapping>

/pages/manager/下的请求都要经过Fliter,当Filter对请求过滤后,依然将请求发送到目的地址。如果需要检查权限,可以在Filter中根据用户请求的HttpSession,判断用户权限是否足够。如果权限不够,直接调用重定向即可,无须调用chain.doFilter(request,reponse)方法。

可以通过Fliter给所有请求都加上try-catch来实现事务

6 常遇问题

1)Request乱码

解决post提交方式的乱码:

// 一定要在获取请求参数之前调用才有效
request.setCharacterEncoding("UTF-8");

解决get提交的方式的乱码:

parameter = newString(parameter.getbytes("iso8859-1"),"utf-8");

解决响应中文乱码问题

 resp.setContentType("test/html;charset=utf-8");

2)JavaWeb中的/

在这里插入图片描述

在浏览器端, /只能为http://ip:port/  ,因为工程名有很多,不可能知道是哪个

在服务器端,即在自己工程下,/肯定代表  http://ip:port/工程名/    因为是在自己工程下编写的,肯定知道工程名

第三个为请求重定向   在服务器端编写的将/传给浏览器  也还是识别为http://ip:port/     入请求重定向的test文件

总结:/分为在服务器端使用   不在服务器使用
请求转发是服务器端执行的,/包含工程名
请求重定向是服务器返回给客户端一个新的地址,/不包含工程名

3) getParameter方法

tomcat解析HttpRequestLine(HTTP请求行)和解析HttpHeader(HTTP请求头)等等,但事实上在Servlet之前,服务器只解析到Header就停了,剩下的请求体留在request.getInputStream的流里
1)因为流是不能往回读的,如果我在之前**先用request.getInputStream()**读取完了请求体,这时候我再来用request.getParameter(“name”)则获取不到
2)或者先调用了request.getParameter();再来request.getInputStream();尝试获取请求体内容页是null值
3)请求行里的URI部分这么写: /testServlet?name=baolin,接着又在请求体里附加name=baobao,然后post提交,这时候在后台Servlet调用request.getParameter(“name”);获取的是baolin
解释:tomcat会先解析URI里的queryString,然后判断如果是post请求,不管之前有没有获取到需要的参数都会去解析body
如果不是post请求而是delete或者其它请求,则不会去读取body,这种情况使用getInputSteam可以读取body会得到数据

用httpServletRequest.getParameter接收post请求参数,发送端content Type必须设置为application/x-www-form-urlencoded;否则会接收不到

猜你喜欢

转载自blog.csdn.net/weixin_43849906/article/details/121387910