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应用
2)ServletConfig对象中维护了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;否则会接收不到