博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第三章 请求与响应
阅读量:6540 次
发布时间:2019-06-24

本文共 32649 字,大约阅读时间需要 108 分钟。

  • 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()。如:

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {    processRequest(req, resp);}@Overrideprotected 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
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)*/@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// TODO Auto-generated method stubPrintWriter out = resp.getWriter();out.println("");out.println("");out.println("HeaderServlet");out.println("");out.println("");//取得应用程序环境路径out.println("

HeaderServlet at " + req.getContextPath() + "

");//取得所有标头名称Enumeration
names = req.getHeaderNames();while (names.hasMoreElements()) {String name = names.nextElement();//取得标头值out.println(name + ":" + req.getHeader(name) + "
");}out.println("");out.println("");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("");        out.println("");        out.println("Servlet BodyView");        out.println("");        out.println("");        out.println(body);        out.println("");        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 + "
"; } return requestBody; }}

对应的窗体

<%@ page contentType="text/html;charset=UTF-8" language="java" %>              Hello    
选择文件:

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

              Hello    
上传照片:
/** * @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设置的路径。

多个文件上传:

文件1:
文件2:
文件3:
/** * @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;    }}
UploadServlet3
cc.openhoem.UploadServlet3
D:/ProgramData

使用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
messages= new HashMap
(); 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 =            ""            +"  "            +"      
%s" +" " +"

%s

" +" " +""; @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("");out.println("");

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()方法取得编码设置。

zh_TW
UTF-8

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等。

pdf
application/pdf

发送中文请求参数值,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("/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("");        out.println("");        out.println("感谢填写");        //取得请求参数值        out.println("联系人:" +        req.getParameter("user") + "");        out.println("
喜爱的宠物类型"); out.println("
    "); //取得复选项请求参数值 for (String type : req.getParameterValues("type")) { out.println("
  • " + type + "
  • "); } out.println("
"); out.println(""); out.println(""); 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
errors = new ArrayList
(); 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 + "\t" + 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("'");        out.println("");        out.println("");        out.println("
"); out.println(" 新增会员失败"); out.println(""); out.println(""); out.println("

新增会员失败

"); out.println("u1 style='color: rgb(255, 0, 0);'>"); //取得请求属性 List
errors = (List
) req.getAttribute("errors"); //显示错误信息 for (String error : errors) { out.println("
  • " + error + "
  • "); } out.println(""); out.println("
    返回注册页面"); out.println(""); out.println(""); 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("'");        out.println("");        out.println("");        out.println("
    "); out.println(" 新增会员失败"); out.println(""); out.println(""); out.println("

    会员 " + req.getParameter("username") + " 注册成功

    "); out.println("回首页登录"); out.println(""); out.println(""); 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("\t")[1];                    if (password.equals(password)) {                        return true;                    }                }            }        }        return false;    }}

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

    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("'");        out.println("");        out.println("");        out.println("
    "); out.println(" 新增会员失败"); out.println(""); out.println(""); out.println("

    会员 " + req.getParameter("username") + " 您好!

    "); out.println(""); out.println(""); out.close(); }}

     

    转载于:https://www.cnblogs.com/beaconSky/p/5370499.html

    你可能感兴趣的文章
    jenkins 实验 (四) jira安装
    查看>>
    2013大学生IT博客大赛拉开帷幕!
    查看>>
    我的友情链接
    查看>>
    分享一个python cookbook的在线教程地址
    查看>>
    【iOS-Cocos2d(2.x) 游戏开发之一】自定义CCSprite需注意&Cocos2d/x/Unity3D引擎
    查看>>
    Linux防火墙iptables中mark模块分析及编写
    查看>>
    我家大门常打开
    查看>>
    [问题]wkhtmltopdf转换网页中文乱码问题解决方案
    查看>>
    关于session.auto_start报警的问题
    查看>>
    Onenote Class Notebook Ctreator,协作教学的利器
    查看>>
    CPU Utilization Plugin For Nagios v2.0
    查看>>
    zabbix从2.2.3升级到最新稳定版3.2.1
    查看>>
    我有一个网站,想提高点权重
    查看>>
    Web前端开发必备:《Jquery实战》第3版 介绍
    查看>>
    2017年前端框架、类库、工具大比拼
    查看>>
    真正聪明的人,为什么从不去社交?
    查看>>
    linux免交互登陆远程主机并执行命令(密钥对和Expect)
    查看>>
    浅谈(SQL Server)数据库中系统表的作用
    查看>>
    微软邮件系统Exchange 2013系列(七)创建发送连接器
    查看>>
    程序员杂记系列
    查看>>