• 第三章 请求与响应


    • 3.1 从容器到 HttpServlet

    Web容器做了什么 

    当浏览器请求HTTP服务器时,是使用HTTP来传送请求与相关信息(标头、请求、参数、Cookie等)。HTTP是基于TCP/IP之上的协议,信息基本上都是通过文字信息来传送的,然而Servlet本质上市个Java对象,运行于Web容器中。

    当请求来到HTTP服务器,而HTTP服务器转交给容器时,容器会创建一个代表当次请求的HttpServletRequest对象,并将请求相关信息设置给该对象。同时容器会创建一个HttpServletResponse对象,作为稍后要对客户端进行响应的Java对象。

    接着,容器会根据读取的@WebServlet标注或web.xml的设置,找出处理该请求的Servlet,调用它的service()方法,将创建的HttpServletRequest对象、HttpServletResponse对象传入作为参数,service方法中会根据HTTP请求的方式,调用对应的doXXX()方法。例如,若为GTE则调用doGet()。

    接着在doGet()中,可以使用HttpServletRequest对象、HttpServletResponse对象。例如使用getParameter()取得请求参数,使用getWriter()取得输出用的PrintWriter对象,并进行各项响应处理。对PrintWriter做的输出操作,最后由容器转换为HTTP响应,再由HTTP服务器对浏览器进行响应。之后容器将HttpServletRequest对象、HttpServletResponse对象销毁回收,该次请求响应结束。

    像这类请求/响应对象的创建与销毁,也就是有关请求/响应对象的生命周期管理,也是Web容器提供的功能。

    doXXX()方法

    Servlet接口的service()方法签名其实接受的是ServletRequest、ServletResponse:

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;

    请求/响应对象的基本行为是规范在ServletRequest、ServletResponse(包是javax.servlet),而与HTTP相关行为则分别由两者的子接口HttpServletRequest、HttpServletResponse(包是javax.servlet.http)定义。

    Web容器创建的确实是HttpServletRequest、HttpServletResponse的实现对象,而后调用Servlet接口的service()方法。在HttpServlet中实现service()如下:

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        } catch (ClassCastException var6) {
            throw new ServletException("non-HTTP request or response");
        }
        this.service(request, response);
    }
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long errMsg;
        if(method.equals("GET")) {
            errMsg = this.getLastModified(req);
            if(errMsg == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader("If-Modified-Since");
                if(ifModifiedSince < errMsg / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, errMsg);
                    this.doGet(req, resp);//处理HTTP GET请求
                } else {
                    resp.setStatus(304);
                }
            }
        } else if(method.equals("HEAD")) {
            errMsg = this.getLastModified(req);
            this.maybeSetLastModified(resp, errMsg);
            this.doHead(req, resp);//处理HTTP HEAD请求
        } else if(method.equals("POST")) {
            this.doPost(req, resp);//处理HTTP POST请求
        } else if(method.equals("PUT")) {
            this.doPut(req, resp);//处理HTTP PUT请求
        } 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 errMsg1 = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg1 = MessageFormat.format(errMsg1, errArgs);
            resp.sendError(501, errMsg1);
        }
    }

     如果客户端发出了没有实现的请求:

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if(protocol.endsWith("1.1")) {
            resp.sendError(405, msg);
        } else {
            resp.sendError(400, msg);
        }
    }

     如果在继承HttpServlet之后,没有重新定义doGet()方法,而客户端对该servlet发出了GET请求,则会收到错误信息。

    在GET与POST都要相同处理的情境下,通常可以继承HttpServlet之后,在doGet()、doPost()中都调用一个自定义的processRequest()。如:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        processRequest(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        processRequest(req, resp);
    }
    protected void processRequest(HttpServletRequest req, HttpServletResponse resp) throws  ServletException,IOException {
        //处理请求
    }
    • 3.2 关于HttpServletRequest

    处理请求参数与标头

    getParameter():指定请求参数名称来取得对应的值。

    String username = request.getParameter("name");

    getParameter()返回的是String对象,若传来的是像“123”这样的字符串值,而需要的是基本数据类型,则必须使用Integer.parseInt()这类的方法将之剖析为基本类型。若请求中没有所指定的请求参数名称,则返回null。

    getParameterValue():如果窗体上有可复选的元件,如复选框、列表等,则同一个请求参数名称会有多个值(param=10&param=20&param=30),此时可以用可以用getParameterValues()取得一个String数组,数组元素代表所有被选取选项的值。

    String[] values = request.getParameterValues("param");

    getParameterNames():请求中有多少个请求参数,返回一个Enumeration对象,其中包括所有的请求参数名称。

    Enumeration<String> e = req.getParameterNames();
    while(e.hasMoreElements()) {
      String param = e.nextElement();
          ...
    }

     getParameterMap():将请求参数以Map对象返回,Map中的键(Key)是请求参数名称,值(Value)的部分是请求参数值,以字符串数组类型String[]返回。

    对于HTTP的标头(Header)信息

    getHeaders():使用方式与getParameterValues()类似,指定标头名称后可返回Enumeration,元素为字符串

    getHeaderNames():使用方式与getParameterValues()类似,取得所有标头名称,以Enumeration返回,内含所有标头字符串名称。

    package cc.openhome;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Enumeration;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet("/header.view")
    public class HeaderServlet extends HttpServlet{
    /* (non-Javadoc)
    * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
    */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // TODO Auto-generated method stub
    PrintWriter out = resp.getWriter();
    out.println("<html>");
    out.println("<head>");
    out.println("<title>HeaderServlet</title>");
    out.println("</head>");
    out.println("<body>");
    //取得应用程序环境路径
    out.println("<h1>HeaderServlet at " + req.getContextPath() + "</h1>");
    //取得所有标头名称
    Enumeration<String> names = req.getHeaderNames();
    while (names.hasMoreElements()) {
    String name = names.nextElement();
    //取得标头值
    out.println(name + ":" + req.getHeader(name) + "<br>");
    }
    out.println("</body>");
    out.println("</html>");
    out.close();
    }
    }

    请求参数编码处理

    1、POST请求参数编码处理

    如果客户端没有在Content-Type标头中设置字符编码信息,此时使用HttpServletRequest的getCharacterEncoding()返回值回事null。这个情况下,容器若使用的是默认编码处理是ISO-8859-1,而客户端使用UTF-8发送非ASCII字符的请求参数,Servlet直接使用getParameter()等方法取得该请求参数值,就会乱码。可以使用HttpServletRequest的setCharacterEncoding()方法指定取得POST请求参数时使用的编码(req.setCharacterEncoding("UTF-8")相当于String text = java.net.URLDecoder.decode("%E6%9E%97", "UTF-8"))

    2、GET请求参数编码处理

    在HttpServletRequest的API文件中,对setCharacterEncoding()的说明清楚提到:

    Overrides the name of the character encoding used in the body of this request.

    这个方法对于请求Body中的字符编码才有作用,也就是基本上这个方法只对POST产生作用,当请求时用GET发送时,则没有定义这个方法是否会影响Web容器处理编码的方式。(主要是因为处理URL的是HTTP服务器,而非Web容器)。若使用Tomcat并采用GET,或并没有设置setCharacterEncoding(),且已取得一个请求参数字符串,另外一个处理编码的方式,则是通过String的getBytes()指定编码来取得该字符串的字节数组,然后再重新构造为正确编码的字符串。

    例如,若浏览器使用UTF-8处理字符,Web容器默认使用ISo-8859-1编码,则正确处理编码的方式为:

    String name = req.getParameter("name");
    String name = new String(name.getBytes("ISO-8859-1"), "UTF-8");

    相当于

    String text = java.net.URLDecoder.encode("林", "UTF-8");
    package cc.openhome;
    
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet("/encoding")
    public class EncodingServlet extends HttpServlet{
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //GET的编码处理
            String name = req.getParameter("nameGet");
            name = new String(name.getBytes("ISO-8859-1"), "UTF-8");
            System.out.println("GET: " + name);
        }
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //POST的编码处理
            resp.setCharacterEncoding("UTF-8");
            String name = req.getParameter("namePost");
            System.out.println("POST:" + name);
        }
    }

    getReader()、getInputStream()读取Body内容

    package cc.openhoem;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * Created by Administrator on 2016/4/14.
     * 上传文件
     */
    public class BodyServlet extends HttpServlet{
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String body = readBody(req);
            PrintWriter out = resp.getWriter();
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet BodyView</title>");
            out.println("<head>");
            out.println("<body>");
            out.println(body);
            out.println("</body>");
            out.println("/html");
        }
    
        private String readBody(HttpServletRequest req) throws IOException {
            BufferedReader reader = req.getReader();
            String input = null;
            String requestBody = "";
            while(null != (input = reader.readLine())) {
                requestBody = requestBody + input + "<br>";
            }
            return requestBody;
        }
    }

    对应的窗体

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title></title>
      </head>
      <body>
        Hello
        <form action="BodyServlet" method="post" enctype="multipart/form-data">
          选择文件:<input type="file" name="filename" value="" /><br>
          <input type="submit" value="Upload" name="upload">
        </form>
      </body>
    </html>

    getPart()、getParts()取得上传文件

    <html>
      <head>
        <title></title>
      </head>
      <body>
        Hello
        <form action="UploadServlet" method="post" enctype="multipart/form-data">
          上传照片:<input type="file" name="photo" /><br><br>
          <input type="submit" value="上传" name="upload">
        </form>
      </body>
    </html>
    /**
     * @Title: UploadServlet.java
     * @Package cc.openhome
     * @Description: TODO(文件上传)
     * @author rocky
     * @date 2016年4月18日 下午5:22:42
     */
    package cc.openhoem;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.MultipartConfig;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.Part;
    import java.io.*;
    
    
    @MultipartConfig    //使用getPart()处理上传的文件
    public class UploadServlet extends HttpServlet{
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            Part part = req.getPart("photo");    //使用getPart()取得Part对象
            String filename = getFileName(part);
            writeTo(filename, part);
        }
    
        //取得上传文件名存在问题
        private String getFileName(Part part) {
            String header = part.getHeader("Content-Disposition");
            String fileName = header.substring(header.indexOf("fileName="") + 10, header.lastIndexOf("""));
            return fileName;
        }
    
        //存储文件
        private void writeTo(String fileName, Part part) throws IOException, FileNotFoundException{
            InputStream in =part.getInputStream();
            OutputStream out = new FileOutputStream("D:/ProgramData/" + fileName);
            byte[] buffer = new byte[1024];
            int length = -1;
            while (-1 != (length = in.read(buffer))) {
                out.write(buffer, 0, length);
            }
            out.close();
            in.close();
        }
    }

     @MultipartConfig标注可用来设置Servlet处理上传文件的相关信息,属性如下:

    location:字符串设置,设置写入文件时的目录,如果设置这个属性,则缓存文件就是写到指定的目录,也可以搭配Part的write()方法使用,默认为空字符串。

    maxFileSize:限制上传文件大小,默认值为-1L,表示不限制大小。

    maxRequestSize:限制multipart/form-data请求个数,默认值为-1L,表示不限制个数。

    fileSizeThreshold:整数值设置,若上传文件超过设置门槛,会写入缓存文件,默认值为0。

    multipart/form-data发送的每个内容区段,都会有以下的标头信息:

    Content-Disposition:form-data;name="fileName";fileName="caterpillar.jpg"

    Content-Type:image/jpeg......

    如果想取得这些标头信息,可以使用Part对象的getHeader()方法,指定标头名称来取得对应的值。所以想要取得上传文件名称,就是取得Content-Disposition标头的值,然后取得filename属性的值。最后,再利用Java I/O API写入文件中。

    Part有个方便的write()方法,可以直接将上传文件指定文件名写入磁盘中,write()可指定文件名,写入的路径是相对于@MultipartConfig的location设置的路径。

    多个文件上传:

    <form action="UploadServlet3" method="post" enctype="multipart/form-data">
            文件1:<input type="file" name="file1" /><br><br>
            文件2:<input type="file" name="file2" /><br><br>
            文件3:<input type="file" name="file3" /><br><br>
            <input type="submit" value="上传" name="upload">
        </form>
    /**
     * @Title: UploadServlet.java
     * @Package cc.openhome
     * @Description: TODO(文件上传)
     * @author rocky
     * @date 2016年4月18日 下午5:22:42
     */
    package cc.openhoem;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.MultipartConfig;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.Part;
    import java.io.IOException;
    
    @MultipartConfig(location = "D:/ProgramData")    //使用getPart()处理上传的文件
    @WebServlet("/upload3.do")
    public class UploadServlet3 extends HttpServlet{
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            req.setCharacterEncoding("UTF-8");
            for (Part part : req.getParts()) {  //迭代Collection中所有Part对象
                if (part.getName().startsWith("file")) { //只处理上传文件区段
                    String fileName = getFileName(part);
                    part.write(fileName);
                }
            }
        }
    
        //取得上传文件名
        private String getFileName(Part part) {
            String header = part.getHeader("Content-Disposition");
            String fileName = header.substring(header.indexOf("fileName="") + 10, header.lastIndexOf("""));
            return fileName;
        }
    }
    <!--web.xml-->
        <servlet>
            <servlet-name>UploadServlet3</servlet-name>
            <servlet-class>cc.openhoem.UploadServlet3</servlet-class>
            <!--设置路径-->
            <multipart-config>
                <location>D:/ProgramData</location>
            </multipart-config>
        </servlet>

    使用RequestDispatcher调派请求

    在Web应用程序中,经常需要多个Servlet来完成请求。例如,将另一个Servlet的请求处理流程包含(Include)进来,或将请求转发(Forward)给别的Servlet处理。如果有这类的请求,可以使用HttpServletRequest的getRequestDispatcher()方法取得RequestDispatcher接口的实现对象实例,调用时指定转发或包含的相对URL网址。

    RequestDispatcher dispatcher = req.getRequestDispatcher("upload.do");

    1、使用include()方法

    可以将另一个Servlet的操作流程包括至目前Servlet操作流程中

    package cc.openhoem;
    
    import javax.servlet.RequestDispatcher;
    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;
    import java.io.PrintWriter;
    
    /**
     * Created by Administrator on 2016/4/19.
     */
    @WebServlet("/some.view")
    public class Some extends HttpServlet{
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            PrintWriter out = resp.getWriter();
            out.println("Some do one...");
            RequestDispatcher dispatcher = req.getRequestDispatcher("other.view?data=123456");
            dispatcher.include(req, resp);
            out.println("Some do two...");
            out.close();
        }
    }

    2、请求范围属性

    在include()或forword()时包括请求参数的做法,仅适用于传递字符串给另一个Servlet,在调派请求的过程中,如果有必须共享的“对象”,可以设置给请求对象称为属性,称为请求范围属性(Request Scope Attribute)。

    setAttribute():指定名称与对象设置属性

    getAttribute():指定名称取得属性

    getAttributeNames():取得所有属性名称

     

    3、使用forward()方法

    调用时同样传入请求与响应对象,这表示你要将请求处理转发给别的Servlet,“对客户端的响应同时也转发给另一个Servlet”

    package cc.openhoem;
    
    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;
    import java.io.PrintWriter;
    
    /**
     * Created by Administrator on 2016/4/20.
     */
    @WebServlet("/helloController.do")
    public class HelloController extends HttpServlet{
        private HelloModel model = new HelloModel();
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //收集请求参数
            String name = req.getParameter("user");
            //PrintWriter out = resp.getWriter();
            //out.print(name);
            //委托HelloModel对象处理
            String message = model.doHello(name);
            //将结果信息设置至请求对象成为属性
            req.setAttribute("message", message);
            //转发给hello.view进行响应
            req.getRequestDispatcher("hello.view").forward(req, resp);
        }
    }
    package cc.openhoem;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * Created by Administrator on 2016/4/20.
     */
    public class HelloModel {
        private Map<String, String> messages= new HashMap<String, String>();
    
        public HelloModel() {
            messages.put("caterpillar", "Hello");
            messages.put("Justin", "Welcome");
            messages.put("momor", "Hi");
        }
        public String doHello(String user) {
            //取值
            String message = messages.get(user);
            return message + ", " + user + "!";
        }
    }
    package cc.openhoem;
    
    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;
    
    /**
     * Created by Administrator on 2016/4/20.
     */
    @WebServlet("/hello.view")
    public class HelloView extends HttpServlet{
        private String htmlTemplate =
                "<html>"
                +"  <head>"
                +"      <meta http-equiv='Content-Type'"
                +"          content='text/html; charset=UTF-8'"
                +"      <title>%s</title>"
                +"  <body>"
                +"      <h1>%s</h1>"
                +"  </body>"
                +"</html>";
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //取得请求参数
            String user = req.getParameter("user");
            //取得请求属性
            String message = (String) req.getAttribute("message");
            //产生HTML结果
            String html = String.format(htmlTemplate, user, message);
            //输出HTML结果
            resp.getWriter().print(html);
        }
    }
    • 3.3 关于HttpServletResponse

    可以使用HttpServletResponse来对浏览器进行响应。大部分情况使用setContentType()设置响应类型,使用getWriter()取得PrintWriter对象,然后使用PrintWriter的println()等方法输出HTML内容。

    还可以进一步使用setHeader()、addHeader()等方法进行响应标头的设置,或者是使用sendRedirect()、sendError()方法,对客户端要求重定向网页,或是传送错误状态信息。若必要,也可以使用getOutputStream()取得ServletOutputStream,直接使用串流对象对浏览器进行字节数据的响应。

    设置响应标头、缓冲区

    setHeader()、addHeader()来设置相应标头,setHeader()设置标头名称与值,addHeader()则可以在同一个标头名称上附加值。如果标头的值是整数,则可以使用setIntHeader()、addIntHeader()方法,如果标头的值是日期,则使用setDateHeader()、addDateHeader()方法。

    容器可以对响应内容进行缓冲,通常默认缓冲。缓冲方法:

    getBufferSize()、setBufferSize()、isCommited()、reset()、resetBuffer()、flushBuffer()

    在缓冲区未满之前,设置的响应相关内容都不会真正传至客户端,可以使用isCommitted()看看是否响应已确认。如果想要重置所有响应内容,但不会清除已设置的标头内容。

    flushBuffer()会清楚(flush)所有缓冲区中已设置的响应信息至客户端,reset()、resetBuffer()必须在未响应前调用。

    使用getWriter()输出字符

    如果要对浏览器输出HTML,在先前的范例中都会通过HttpServletResponse的getWriter()取得PrintWriter对象,然后指定字符串进行输出。

    PrintWriter out = resp.getWriter();
    out.println("<html>");
    out.println("<head>");

    1、设置Locale

    浏览器如果有发送Accept-Language标头,则可以使用HttpServletRequest的getLocale()来取得一个locale对象,代表客户端可接受的语系。

    可以使用HttpServletResponse的setLocale()来设置地区(locale)信息,地区信息就包括了语系与编码信息。语系信息通常通过响应标头Content-Language来设置,而setLocale()也会设置HTTP响应的Content-Language标头

    resp.setLocale(Locale.TAIWAN);

    这会将HTTP响应的Content-Language设置为zh-TW,而字符编码处理设置为BIG5.可以使用HttpServletResponse的getCharacterEncoding()方法取得编码设置。

       <!--web.xml-->
       <locale-encoding-mapping-list>
            <locale-encoding-mapping>
                <locale>zh_TW</locale>
                <encoding>UTF-8</encoding>
            </locale-encoding-mapping>
        </locale-encoding-mapping-list>

    2、使用setCharacterEncoding()或setContentType()

    也可以调用HttpServletResponse的setCharacterEncoding()设置字符编码:

    resp.setCharacterEncoding("UTF-8");

    或者在使用HttpServletResponse的setContentType()时,指定charset,charset的值会自动用来调用setCharacterEncoding()。例如,以下不仅设置内容类型为text/html,也会自动调用setCharacterEncoding(),设置编码为UTF-8:

    resp.setContentType("text/html; charset=UTF-8");

    如果使用了setCharacterEncoding()或setContentType()时指定了charset,则setLocale()就会被忽略。

    因为浏览器需要知道如何处理你的响应,所以必须告知内容类型,setContentType()方法在响应中设置content-type响应标头,你只要指定MIME(multipurpose Internet Mail Extensions)类型就可以了。由于编码涉资与内容类型通常都要设置,所以调用setContentType()设置内容类型时,同时指定charset属性是个方便且常见的做法。

    常见的设置有text/html、application/pdf、application/jar、application/x-zip、image/jpeg等。

        <mime-mapping>
            <!--设置文件后缀-->
            <extension>pdf</extension>
            <!--设置对应的MIME类型名称-->
            <mime-type>application/pdf</mime-type>
        </mime-mapping>

    发送中文请求参数值,Servlet可正确接收处理并显示在浏览器中。

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
            "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title>宠物类型大调查</title>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    </head>
    <body>
        <form action="pet.do" method="post">
            姓名:<input type="text" name="user"><br>
            邮件:<input type="text" name="email"><br>
            你喜爱的宠物代表:<br>
            <select name="type" size="6" multiple="true">
                <option value="猫"></option>
                <option value="狗"></option>
                <option value="鱼"></option>
                <option value="鸟"></option>
            </select><br>
            <input type="submit" value="送出">
        </form>
    </body>
    </html>
    package cc.openhoem;
    
    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;
    import java.io.PrintWriter;
    
    /**
     * Created by Administrator on 2016/4/20.
     */
    @WebServlet("/pet.do")
    public class Pet extends HttpServlet{
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //设置请求对象字符编码
            req.setCharacterEncoding("UTF-8");
            //设置内容类型
            resp.setContentType("text/html; cahrset=UTF-8");
            //取得输出对象
            PrintWriter out = resp.getWriter();
            out.println("<html>");
            out.println("<head>");
            out.println("<title>感谢填写</title>");
            //取得请求参数值
            out.println("联系人:<a href='mailto:"+req.getParameter("email") + "'>" +
            req.getParameter("user") + "</a>");
            out.println("<br>喜爱的宠物类型");
            out.println("<ul>");
            //取得复选项请求参数值
            for (String type : req.getParameterValues("type")) {
                out.println("<li>" + type + "</li>");
            }
            out.println("</ul>");
            out.println("</body>");
            out.println("</html>");
            out.close();
        }
    }

    使用getOutputStream()输出二进制字符

    在大部分情况下,会从HttpServletResponse取得PrintWriter实例,使用println()对浏览器进行字符输出。然而有时候需要直接对浏览器进行字节输出,这时可以使用HttpServletResponse的gteOutputStream()方法取得ServletOutputStream实例。

    package cc.openhoem;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    /**
     * Created by Administrator on 2016/4/20.
     */
    public class DownLoad extends HttpServlet{
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String passwd = req.getParameter("passwd");
            if ("123456".equals(passwd)) {
                //设置内容类型
                resp.setContentType("application/pdf");
                //取得输入串流
                InputStream in = getServletContext().getResourceAsStream("/WEB-INF/jdbc.pdf");
                //取得输出串流
                OutputStream out = resp.getOutputStream();
                //读取PDF并输出至浏览器
                writeBytes(in, out);
            }
        }
    
        private void writeBytes(InputStream in, OutputStream out) throws IOException {
            byte[] buffer = new byte[1024];
            int length = -1;
            while(-1 != (length = in.read(buffer))) {
                out.write(buffer, 0, length);
            }
            in.close();
            out.close();
        }
    }

    使用sendRedirect()、sendError()

    forward()会将请求转发至指定的URL,这个动作是在Web容器中进行,浏览器并不会知道请求被转发,地址栏也不会有变化。

    在转发过程中,都还是在同一个请求周期,这也是为什么RequestDispatcher是由调用HttpServletRequest的getRequestDispatcher()方法取得,所以在HttpServletRequest中使用setAttribute()设置的属性对象,都可以在转发过程中共享。

    可以使用HttpServletResponse的sendRedirect()要求浏览器重新请求另一个URL,又称为重定向(Redirect),使用时可指定绝对URL或相对URL。

    reponse.sendRedirect("http://openhoem.cc");

    这个方法会在响应中设置HTTP状态码301以及Location标头,浏览器接收到这个标头,会重新使用GET方法请求指定URL,因此,地址栏上会发生URL变更。

    如果在处理请求的过程中发现一些错误,而你想要传送服务器默认的状态与错误信息,可以使用sendError()方法。例如,如果根据请求参数必须返回的资源根本不存在,则可以发出错误信息:

    resp.sendError(HttpServletResponse.SC_NOT_FOUND, "笔记文件");
    • 3.4 综合练习

    微博应用程序功能概述

    微博首页、微博会员注册、会员注册失败画面、会员注册成功画面、会员登陆成功画面

    实现会员注册功能

    <form>标签

    <form method="post" action="register.do">

    邮件地址字段

    <input type="text" name="username" size="25" maxlength="100">

    名称字段

    <input type="text" name="username" size="25" maxlength="16">

    密码与确认密码字段

    <input type="password" name="password" size="25" maxlength="16">

    <input type="password" name="confirmedPasswd" size="25" maxlength="16">

    package cc.openhoem.controller;
    
    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.BufferedWriter;
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by Administrator on 2016/4/21.
     */
    @WebServlet("/register.do")
    public class Register extends HttpServlet{
        private final String USERS = "D:/ProgramData";
        private final String SUCCESS_VIEW = "success.view";
        private final String ERROR_VIEW = "error.view";
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //取得请求参数
            String email = req.getParameter("emial");
            String username = req.getParameter("username");
            String password = req.getParameter("password");
            String confirmPasswd = req.getParameter("confirmPasswd");
    
            List<String> errors = new ArrayList<String>();
            if (isInvalidEmail(email)) {
                errors.add("未填写邮件或邮件格式不正确");
            }
            if (isInvalidUsername(username)) {
                errors.add("用户名称为空或已存在");
            }
            if (isInvalidPassword(password, confirmPasswd)) {
                errors.add("请确认莫玛符合格式并再次确认密码");
            }
            String resultPage = ERROR_VIEW;
            //窗体验证出错误,设置收集错误的List为请求属性
            if (!errors.isEmpty()) {
                req.setAttribute("errors", errors);
            } else {
                resultPage = SUCCESS_VIEW;
                //创建用户资料
                createUserData(email, username, password);
            }
    
            req.getRequestDispatcher(resultPage).forward(req, resp);
        }
    
        private boolean isInvalidEmail(String email) {
            return email == null || !email.matches(
                    "^[_a-z0-9-]+([.]" + "[_a-z0-9-]+)*@[a-z0-9-]+([.][a-z0-9-]+)*$");
        }
        //检查用户资料夹是否创建来确认用户是否已注册
        private boolean isInvalidUsername(String username) {
            for (String file : new File(USERS).list()) {
                if (file.equals(username)) {
                    return true;
                }
            }
            return false;
        }
        private boolean isInvalidPassword(String password, String confirmedPasswd) {
            return password == null ||
                    password.length() < 6 ||
                    password.length() > 16 ||
                    !password.equals(confirmedPasswd);
        }
        //创建用户资料,在profile中存储邮件与密码
        private void createUserData(String email, String username, String password) throws IOException {
            File userhome = new File(USERS + "/" +username);
            userhome.mkdir();
            BufferedWriter writer = new BufferedWriter(new FileWriter(userhome + "/profile"));
            writer.write(email + "	" + password);
            writer.close();
        }
    }
    package cc.openhoem.controller;
    
    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;
    import java.io.PrintWriter;
    import java.util.List;
    
    /**
     * Created by Administrator on 2016/4/21.
     */
    @WebServlet("/error.view")
    public class Error extends HttpServlet{
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //设置响应编码
            resp.setContentType("text/html; charset=UTF-8");
            PrintWriter out = resp.getWriter();
            out.println("<!DOCTYPE HTML PUBLIC" + "'-//W3C//DTD HTML 4.01 Transitional//EN>'");
            out.println("<html>");
            out.println("<head>");
            out.println("<meta http-equiv='Content-Type'" + "content='text/html;charset=UTF-8'>");
            out.println("   <title>新增会员失败</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>新增会员失败</h1>");
            out.println("u1 style='color: rgb(255, 0, 0);'>");
            //取得请求属性
            List<String> errors = (List<String>) req.getAttribute("errors");
            //显示错误信息
            for (String error : errors) {
                out.println("   <li>" + error + "</li>");
            }
            out.println("</u1>");
            out.println("<a href='register.html'>返回注册页面</a>");
            out.println("</body>");
            out.println("</html>");
            out.close();
        }
    }

    要注意到,为了显示中文的错误信息,使用HttpServletResponse的setContentType()时顺便指定了charset属性,由于只有在失败时才会转发到这个页面,并在请求中带有error属性,于是使用HttpServletRequest的getAtrribute()取得属性,并逐一显示错误信息。

    package cc.openhoem.controller;
    
    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;
    import java.io.PrintWriter;
    
    /**
     * Created by Administrator on 2016/4/21.
     */
    @WebServlet("/success.view")
    public class Success extends HttpServlet{
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("text/html;charset=UTF-8");
            PrintWriter out = resp.getWriter();
            //....
            out.println("<!DOCTYPE HTML PUBLIC" + "'-//W3C//DTD HTML 4.01 Transitional//EN>'");
            out.println("<html>");
            out.println("<head>");
            out.println("<meta http-equiv='Content-Type'" + "content='text/html;charset=UTF-8'>");
            out.println("   <title>新增会员失败</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>会员 " + req.getParameter("username") + " 注册成功</h1>");
            out.println("<a href='index.html'>回首页登录</a>");
            out.println("</body>");
            out.println("</html>");
            out.close();
        }
    }

    实现会员登录功能

    <form>标签

    <form method="post" action="login.do">

    名称字段

    <input type="text" name="username">

    密码字段

    <input type="password" name="password">

    package cc.openhoem.controller;
    
    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.*;
    
    /**
     * Created by Administrator on 2016/4/21.
     */
    @WebServlet("/login.do")
    public class Login extends HttpServlet{
        private final String USERS = "D:/ProgramData";
        private final String SUCCESS_VIEW = "member.view";
        private final String ERROR_VIEW = "index.html";
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String username = req.getParameter("username");
            String password = req.getParameter("password");
    
        }
        
        private boolean cheakLogin(String username, String password) throws IOException {
            if (username != null && password != null) {
                //读取用户资料夹中的profile
                for (String file : new File(USERS).list()) {
                    if (file.equals(username)) {
                        BufferedReader reader = new BufferedReader(
                                new FileReader(USERS + "/" + file + "/profile"));
                        String passwd = reader.readLine().split("	")[1];
                        if (password.equals(password)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    }

    检查登录基本上就是查看用户名是否有对应的资料夹,并且看看profile文件中存放的密码是否符合,注意先前创建profile时,邮件与密码中间是用“ ”字符分隔。如果名称与密码不符就重新定向回首页,让用户可以重新登录,登陆信息正确就转发会员网页。

    package cc.openhoem.controller;
    
    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;
    import java.io.PrintWriter;
    
    /**
     * Created by Administrator on 2016/4/21.
     */
    @WebServlet("/member.view")
    public class Member extends HttpServlet{
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            PrintWriter out = resp.getWriter();
            out.println("<!DOCTYPE HTML PUBLIC" + "'-//W3C//DTD HTML 4.01 Transitional//EN>'");
            out.println("<html>");
            out.println("<head>");
            out.println("<meta http-equiv='Content-Type'" + "content='text/html;charset=UTF-8'>");
            out.println("   <title>新增会员失败</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>会员 " + req.getParameter("username") + " 您好!</h1>");
            out.println("</body>");
            out.println("</html>");
            out.close();
        }
    }
  • 相关阅读:
    ConditionedActivityGroup
    一个WF系统架构草图
    新添加了一个栏目
    入门篇(3):了解一下Activity的使用
    再谈调用子流程(2)
    自定义WorkflowRole
    ListenActivity
    WhileActivity
    我是一只鸟
    得到工作流结点列表
  • 原文地址:https://www.cnblogs.com/beaconSky/p/5370499.html
Copyright © 2020-2023  润新知