Http协议和Servlet
1. http协议
1. 什么是协议:双方再交互通信时候,遵守的一种规范,规则
http协议:针对网络上的客户端和服务器端在执行http请求时遵守的一种规范,其实就是规定了客户端在访问服务器端和服务器返回数据时,需要带上什么东西。
2. http协议版本:
1.0版本:请求数据,服务器返回后,回断开连接
通用1.1版本:请求数据,服务器返回后,连接会继续保持,除非服务器和客户端关闭。有一定的时间限制,如果一直空着连接,会自动关闭。
在地址栏中键入网络地址,或者注册按钮,浏览器会显示一些内容,浏览器和服务器如何通讯呢。
使用浏览器打开http://localhost:8082/examples/servlets/servlet/RequestParamExample
3. http请求数据解析
a. 请求行:
POST:请求的方式
/examples/servlets/servlet/RequestParamExample 请求的URL地址
HTTP/1.1:协议的版本
b. 请求头:
Host: localhost:8082
Connection: keep-alive
Content-Length: 28
Cache-Control: max-age=0
Origin: http://localhost:8082
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://localhost:8082/examples/servlets/servlet/RequestParamExample
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: pgv_pvi=2747698176; Phpstorm-eb09a0a4=f84c3339-7383-49af-a83f-1fababd2ac0d
Accept:客户端告诉服务端能支持什么类型的数据
Referer:真正请求的全地址路径
Accept-language:支持的语言格式
User-Agent:向服务器表名,当前来访的客户端信息
Content-Type:提交的数据类型,经过urlencoding编码的form表单数据
Accept-Encoding:gzip,deflate:压缩算法
Content-Length:数据长度
Connection:Keep-Alilve 保持连接
Cache-Control:对缓冲的操作
c. 请求体:
firstname=wang&lastname=jing
浏览器真正发送给服务器的数据
发送的数据时key=value的格式,如果存在多个数据,那么使用&
http响应数据解析
响应的数据里面包含三部分的内容:响应行,响应头,响应体
HTTP/1.1 200
Content-Type: text/html;charset=UTF-8
Content-Length: 711
Date: Wed, 17 Oct 2018 07:07:10 GMT
响应行:HTTP/1.1 200 协议版本,状态码
200:成功,正常处理,得到数据
403:forbidden:拒绝
404:Not Found
500:服务器异常
响应头
Content-Type:服务器返回给客户端的内容类型
Content-Lenght:返回的数据的长度
Date:响应的时间
4. Post和Get请求的数据的区别
http://localhost:8082/examples/servlets/servlet/RequestParamExample?firstname=wang&lastname=jing可以得到Get请求
Get: GET /examples/servlets/servlet/RequestParamExample?firstname=wang&lastname=jing HTTP/1.1 Host: localhost:8082 Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: pgv_pvi=2747698176; Phpstorm-eb09a0a4=f84c3339-7383-49af-a83f-1fababd2ac0d Post: POST /examples/servlets/servlet/RequestParamExample HTTP/1.1 Host: localhost:8082 Connection: keep-alive Content-Length: 28 Cache-Control: max-age=0 Origin: http://localhost:8082 Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Referer: http://localhost:8082/examples/servlets/servlet/RequestParamExample Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: pgv_pvi=2747698176; Phpstorm-eb09a0a4=f84c3339-7383-49af-a83f-1fababd2ac0d
a. 请求的地址不同
b. 带的数据不同
Post采用流的方式携带数据
5. Web资源
在http协议当中,规定了请求和响应双方,客户端和服务器端,与web相关的资源有两类:
静态资源:html,js,css
动态资源:servlet/jsp
2. Servlet
servlet:一个java程序,运行在web服务器上面,用于接收和响应,客户端的http请求
更多的是配合动态资源来做,当然静态资源也需要使用Servlet,不过是Tomcat里面已经定义好了一个DefaultServlet
1. 定义一个Hello Servlet工程
a. 新建一个工程,配置服务器
b. 测试运行Web工程
新建一个类,实现Servlet接口
配置Servlet:告诉服务器,我们的应用有这些Servlet,配置文件webContent/WEB-INF/web.xml
<!-- 向tomcat报告,这个应用中有此servlet,名称为HelloServlet,全路径 /HelloServlet/src/--> <servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>day_01.HelloServlet</servlet-class> </servlet> <!-- 注册servlet的映射,servletName:找到上面注册的具体的servlet,url-pattern:地址栏中的路径 --> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
使用http://localhost:8082/HelloServlet/hello访问该java类
执行过程:
1. 找到tomcat应用
2. 找到项目
3. 找web.xml,然后在里面找到url-pattern,有没有哪一个pattern的内容是hello
4. 找到servlet-mapping中的哪个servlet-name
5. 找到上面定义的Servlet元素中的servlet-name中的HelloServlet
6. 找到下面定义的Servlet-class然后开始创建该类的实例
7. 继而执行该Servlet的service方法
2. Servlet的通用写法
servlet(接口)------>GenericServlet-------->HttpServlet(用于处理Http的请求)
定义一个类,继承HttpServlet复写doGet和doPost
public class HelloServlet02 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("get..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("post"); } }
3. Servlet的生命周期
生命周期:从创建到销毁的时间
生命周期方法:从创建到销毁,所调用的方法
public class HelloServlet03 implements Servlet { // 创建servlet实例时,调用该方法,初次使用servlet,创建实例,init()方法只能执行一次 @Override public void init(ServletConfig config) throws ServletException { System.out.println("HelloServlet03...init..."); } // 客户端来一个请求,执行一次该方法 @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { System.out.println("HelloServlet03...service..."); } // 该项目从tomcat里面移除,正常关闭服务器,shutdown.bat @Override public void destroy() { System.out.println("HelloServlet03...destroy..."); } @Override public ServletConfig getServletConfig() { // TODO Auto-generated method stub return null; } @Override public String getServletInfo() { // TODO Auto-generated method stub return null; } }
4. 让servlet创建实例的时机提前
a. 默认情况下,只有在初次访问servlet时候,才会执行init方法,有时候,可能需要在这个方法中执行一些初始化工作,甚至时做一些比较耗时的逻辑
b. 这个时候,初次访问,可能会在Init方法中逗留太久的时间,那么有没有办法让时间提前一些呢。
<servlet> <servlet-name>HelloServlet04</servlet-name> <servlet-class>day_01.HelloServlet04</servlet-class> <load-on-startup>2</load-on-startup> //值越小越提前执行 </servlet>
服务器重启或者部署项目时就会执行
c. 配置时候,使用load-on-startup来执行,给的数字越小,启动的时机越早,一般不写负数,从2开始即可
5. servletConfig
servletConfig使用方法
package day_01; import java.io.IOException; import java.util.Enumeration; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HelloServletConfig extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ServletConfig:可以获得servlet在配置时的一些信息 ServletConfig config = getServletConfig(); // 获取配置servlet里面servlet-name的文本内容 String servletName = config.getServletName(); System.out.println("servletname:" + servletName); // 获取配置时具体的参数 String initParameter = config.getInitParameter("address"); System.out.println("initparameter:" + initParameter); System.out.println("-----------"); // 获取所有的名称 Enumeration<String> names = config.getInitParameterNames(); while (names.hasMoreElements()){ String name = (String)names.nextElement(); String initpara = config.getInitParameter(name); System.out.println(name + ":" + initpara); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } } <servlet> <servlet-name>HelloServletConfig</servlet-name> <servlet-class>day_01.HelloServletConfig</servlet-class> <!-- 可以添加初始化参数 --> <init-param> <param-name>name</param-name> <param-value>Feng_Ying</param-value> </init-param> <init-param> <param-name>age</param-name> <param-value>20</param-value> </init-param> <init-param> <param-name>address</param-name> <param-value>Beijing_University</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>HelloServletConfig</servlet-name> <url-pattern>/hello5</url-pattern> </servlet-mapping>
1. 未来我们开发了一些应用,使用到了一些技术,或者一些代码,但是我们不会,但是有人写出来了,它的代码放置在servlet类里面
2. 刚好这个servlet里面有一个数字或者变量,但是不是固定的,所以要求使用到servlet时,注册servlet时候,必须要在web.xml,声明init-params
servlet的配置方式
1. 全路径匹配:/a/b
2. 路径匹配:以/开始,但是以*结束,/a/*,/*是任意匹配,*是一个通配符
3. 以扩展名匹配,没有/:*.扩展名,*的内容可以是任意的
6. ServletContext
Servlet上下文:每个web工程都只有一个ServletContext对象,不管在那个servlet里面,获取到的这个类的对象都是同一个
1. 可以获取全局配置参数
<!-- 可以配置全局的参数 --> <context-param> <param-name>address</param-name> <param-value>深圳东莞</param-value> </context-param> <servlet> <servlet-name>ServletContextDemo</servlet-name> <servlet-class>day_01.ServletContextDemo</servlet-class> </servlet>
所有的servlet对象都可以用全局变量
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取ServletContext对象
ServletContext context = getServletContext();
String address = context.getInitParameter("address");
System.out.println("address:" + address);
}
2. 可以获取web应用中的资源
package day_01; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ServletContextDemo2 extends HttpServlet{ private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Properties prop = new Properties(); // InputStream is = ServletContextDemo2.class.getClassLoader().getResourceAsStream("config.properties"); ServletContext context = getServletContext(); // 获取给定文件在服务器上的绝对路径 String path = context.getRealPath("/CONFIG/config.properties"); System.out.println(path); InputStream is = new FileInputStream(path); prop.load(is); String address = prop.getProperty("address"); System.out.println("address:" + address); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
输出:
D:\Wang_File\myeclipse_workspace\.metadata\.me_tcat85\webapps\HelloServlet\CONFIG\config.properties
address:shengzhengdongguan
另一种更加简洁的方法
private void method_other(){ try { Properties prop = new Properties(); // InputStream is = ServletContextDemo2.class.getClassLoader().getResourceAsStream("config.properties"); ServletContext context = getServletContext(); // 获取给定文件在服务器上的绝对路径,获取web工程下的资源,转换成流对象 InputStream is = context.getResourceAsStream("CONFIG/config.properties"); prop.load(is); String address = prop.getProperty("address"); System.out.println("address:" + address); } catch (Exception e) { e.printStackTrace(); } }
第三种方法:通过classLoader获取资源文件:默认的getClassLoader()路径:D:\Wang_File\myeclipse_workspace\.metadata\.me_tcat85\webapps\HelloServlet\WEB-INF\classes
目标目录:D:\Wang_File\myeclipse_workspace\.metadata\.me_tcat85\webapps\HelloServlet\CONFIG
private void method_another(){ try { Properties prop = new Properties(); InputStream is = this.getClass().getClassLoader().getResourceAsStream("../../CONFIG/config.properties"); // 获取给定文件在服务器上的绝对路径 prop.load(is); String address = prop.getProperty("address"); System.out.println("address:" + address); } catch (Exception e) { e.printStackTrace(); } }
3. 存取数据:servlet间共享数据,域对象
a. 定义一个登陆的html页面,定义一个form表单
b. 定义一个Servlet,名为LoginServlet
c. 针对成功和失败,跳转到不同的页面
package com.wang.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class LoginServlet */ @WebServlet("/LoginServlet") // localhost/HelloServlet/LoginServlet public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置请求体里面的编码,这句话对于post没有作用,应为get数据在url上面 request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); // 本处不需要解码,应该是版本比较高tomcat-8.5 // 常用的解码形式:username = new String(username.getBytes("ISO-8859", "UTF-8")) String password = request.getParameter("password"); System.out.println("username:" + username + ",password:" + password); PrintWriter pw = response.getWriter(); if("风影".equals(username) && "wang".equals(password)){ Object obj = getServletContext().getAttribute("count");; int totalCount = 0; if(obj != null){ totalCount = (int)obj; } getServletContext().setAttribute("count", totalCount + 1); System.out.println("登陆成功的次数:" + (totalCount + 1)); // 用户名密码校验成功登陆 设置状态码,重新定位 response.setStatus(302); // 定位跳转的位置是哪一个页面 response.setHeader("Location", "success.html"); }else{ pw.write("login fail..."); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
package com.wang.servlet; 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; /** * Servlet implementation class CountServlet */ @WebServlet("/CountServlet") public class CountServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int count = (int)getServletContext().getAttribute("count"); response.getWriter().write("当前网站成功登陆:" + count + "次"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
<!DOCTYPE html> <!-- -http://localhost:8082/HelloServlet/login.html --> <html> <head> <meta charset="UTF-8"> <title>Login</title> </head> <body> <h2>请输入以下的内容,完成登陆</h2> <form action = "LoginServlet" method = "post"> 账号:<input type="text" name="username" /><br> 密码:<input type="text" name="password" /><br> <input type="submit" value="登陆" /> </form> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>success</title> </head> <body> Login successful... <br/> <a href = "CountServlet">网站登陆成功的次数</a> </body> </html>
ServletContext何时创建,何时销毁
服务器启动的时候,会为每个web应用程序,创建一个ServletContext,当服务器关闭或者从服务器移除托管,会销毁
ServletContext的作用范围:本项目中都可以使用
7. HttpServletRequest
这个对象封装了客户端提交过来的所有信息
1. 可以获取客户端请求的头信息
Enumeration<String> headerNames = request.getHeaderNames(); while(headerNames.hasMoreElements()){ String name = (String)headerNames.nextElement(); String value = request.getHeader(name); System.out.println(name + ":" + value); } host:localhost:8082 connection:keep-alive upgrade-insecure-requests:1 user-agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 accept-encoding:gzip, deflate, br accept-language:zh-CN,zh;q=0.9 cookie:pgv_pvi=2747698176; Phpstorm-eb09a0a4=f84c3339-7383-49af-a83f-1fababd2ac0d
2. 获取客户端提交过来的数据
// 获取客户端提交的数据 或者Enumeration<String> parameterNames = // request.getParameterNames(); String name = request.getParameter("name"); System.out.println("name:" + name); // 获取所有的数据,一个Key可以对应多个数据 Map<String, String[]> map = request.getParameterMap(); Set<String> keySet = map.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String[] value = map.get(key); System.out.println("key:" + key + ",value:" + Arrays.deepToString(value)); } name:wangjing key:name,value:[wangjing, hage]
3. 读取中文数据
Myeclipse get读取的中文不存在中文乱码问题,应该是使用的tomcat-8.5版本比较高:
如果存在,get方式获取数据存在两种解决方法
1. 代码转码
String username = request.getParameter("username"); // 常用的解码形式:get请求过来的数据,在url地址栏经过编码了,所以取到的数据是乱码,tomacat收到这批数据,getParameter,默认使用IOS-8859-1去解码 username = new String(username.getBytes("ISO-8859", "UTF-8")) String password = request.getParameter("password"); System.out.println("username:" + username + ",password:" + password);
2. 在tomcat里面conf/server.xml加上URIEncoding="UTF-8"
<connector connectionTimeout="2000" prot = "8082" protocol = "HTTP/1.1" redirectPort="8445" URIEncoding="UTF-8" />
但是post请求会出现乱码问题,怎么解决呢
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8");// 设置请求体里面的编码,这句话对于get没有作用,应为get数据在url上面 String username = request.getParameter("username"); // 本处不需要解码,应该是版本比较高tomcat-8.5 // 常用的解码形式:username = new String(username.getBytes("ISO-8859", "UTF-8")) String password = request.getParameter("password"); System.out.println("username:" + username + ",password:" + password); PrintWriter pw = response.getWriter(); if("风影".equals(username) && "wang".equals(password)){ Object obj = getServletContext().getAttribute("count");; int totalCount = 0; if(obj != null){ totalCount = (int)obj; } getServletContext().setAttribute("count", totalCount + 1); System.out.println("登陆成功的次数:" + (totalCount + 1)); // 用户名密码校验成功登陆 设置状态码,重新定位 response.setStatus(302); // 定位跳转的位置是哪一个页面 response.setHeader("Location", "success.html"); }else{ pw.write("login fail..."); } }
8. HttpServletResponse
负责返回数据给客户端,输出数据到页面上
// 以字符串方式写数据,文字信息 // response.getWriter().write("hello response"); // 以字节流的方式写数据,文字信息,文件信息 response.getOutputStream().write("Hello Response".getBytes());
响应的数据中有可能出现乱码package com.wang.servlet;import java.io.IOException;import java.nio.charset.Chars
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class ResponseDemo1 */ @WebServlet("/ResponseDemo1") public class ResponseDemo1 extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 字符流输出 // response.getWriter().write("hello response"); // 2. 字节流输出 // response.getOutputStream().write("Hello Response".getBytes()); // 设置响应头 // response.setHeader(name, value); // 设置状态码 // response.setStatus(""); // // response.setContentType(type); // CharacterOutput(response); ByteOutput(response); } private void CharacterOutput(HttpServletResponse response) throws IOException { /* * 中文乱码问题 */ // 字符流方法 // 1. 设置编码时使用utf-8编码 response.setCharacterEncoding("UTF-8"); // 2. 指定浏览器查看这份数据使用的码表 response.setHeader("Content-Type", "text/html;charset=UTF-8"); response.getWriter().write("我爱java"); } private void ByteOutput(HttpServletResponse response) throws IOException { /* * 中文乱码问题 */ // 字节流方法// 1. 指定浏览器查看这份数据使用的码表,以后不管是字节流还是字符流,写如下一句即可 response.setContentType("text/html;charset=UTF-8");
// response.setCharacterEncoding("UTF-8"); response.setHeader("Content-Type", "text/html;charset=UTF-8"); 同上 // 2. 指定输出的中文使用的码表 response.getOutputStream().write("我爱java".getBytes()); // 默认是utf-8 // String csn = Charset.defaultCharset().name(); UTF-8 } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
9. Tomcat下载资源
1. 直接以超链接形式,不写任何代码,也可以下载文件
<body>
让tomcat的默认servlet提供下载功能<br />
<a href = "download/aa.jpg">aa.jpg</a><br />
<a href = "download/bb.txt">bb.jpg</a><br />
<a href = "download/cc.rar">cc.jpg</a><br />
</body>
原因时tomcat里面有一个默认的servlet,DefaultServlet,这个DefaultServlet专门用于处理放在tomcat服务器上面的静态资源
2. 采用自定义tomcat方式
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>index</title> </head> <body> 让tomcat的默认servlet提供下载功能<br /> <a href = "download/aa.jpg">aa.jpg</a><br /> <a href = "download/bb.txt">bb.txt</a><br /> <a href = "download/cc.rar">cc.rar</a><br /> 手动编码提供下载功能:<br> <a href = "DownloadServlet?filename=aa.jpg">aa.jpg</a><br /> <a href = "DownloadServlet?filename=bb.txt">bb.txt</a><br /> <a href = "DownloadServlet?filename=cc.rar">cc.rar</a><br /> </body> </html>
package com.wang.servlet; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; 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("/DownloadServlet") public class DownloadServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取下载文件的名字 String fileName = request.getParameter("filename"); // 2. 获取这个文件在tomcat里面的绝对路径地址 String path = getServletContext().getRealPath("download/" + fileName); // 3. 设置浏览器收到这种资源,采用下载的方式打开 response.setHeader("Content-Disposition", "attachment; filename ="+fileName); // 4. 转换成输入流 InputStream is = new FileInputStream(path); OutputStream os = response.getOutputStream(); // 5. 流转换 int len = 0; byte[] buffer = new byte[1024]; while((len = is.read(buffer)) != -1){ os.write(buffer, 0, len); } os.close(); is.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
下载过程中出现的中文文件名乱码问题解决方案:
package com.wang.servlet; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; 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("/DownloadServlet") public class DownloadServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取下载文件的名字 String fileName = request.getParameter("filename"); // 2. 获取这个文件在tomcat里面的绝对路径地址 String path = getServletContext().getRealPath("download/" + fileName); /* * filename = new String(fileName.getBytes("ISO-8859-1"),"UTF-8"); */ // 3. 如果文件的名字带有中文,那么需要对这个文件进行编码处理,如果是IE,Chrome使用URLEncoding编码 // 如果是Firefox,使用Base64编码 String clientType = request.getHeader("User-Agent"); if(clientType.contains("Firefox")){ fileName = DownloadUtil.base64EncodeFileName(fileName); }else{ fileName = URLEncoder.encode(fileName, "UTF-8"); } // 4. 设置浏览器收到这种资源,采用下载的方式打开 response.setHeader("Content-Disposition", "attachment; filename ="+fileName); // 5. 转换成输入流 InputStream is = new FileInputStream(path); OutputStream os = response.getOutputStream(); // 6. 流转换 int len = 0; byte[] buffer = new byte[1024]; while((len = is.read(buffer)) != -1){ os.write(buffer, 0, len); } os.close(); is.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } package com.wang.servlet; import java.io.UnsupportedEncodingException; import sun.misc.BASE64Encoder; public class DownloadUtil { public static String base64EncodeFileName(String fileName) { BASE64Encoder base64Encoder = new BASE64Encoder(); try { return "=?UTF-8?B?" + new String(base64Encoder.encode(fileName .getBytes("UTF-8"))) + "?="; } catch (UnsupportedEncodingException e) { e.printStackTrace(); throw new RuntimeException(e); } } }
10. 请求转发和重定向
重定向:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); if("admin".equals(username) && "admin".equals(password)){ /*response.setStatus(302); response.setHeader("Location", "success.html");*/ // 上面时以前的写法,重定向写法: response.sendRedirect("servlet/success.html"); }else{ /*response.setStatus(302); response.setHeader("Location", "fail.html");*/ } }
请求转发
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); if("admin".equals(username) && "admin".equals(password)){ /*response.setStatus(302); response.setHeader("Location", "success.html");*/ // 上面时以前的写法,重定向写法: //response.sendRedirect("servlet/success.html"); // 请求转发的写法 request.getRequestDispatcher("servlet/success.html").forward(request, response); }else{ /*response.setStatus(302); response.setHeader("Location", "fail.html");*/ } }
请求转发和重定向的不同点:
1. 重定向显示的是最后的那个资源的路径地址 http://localhost:8082/HelloServlet/servlet/success.html
请求的次数至少有两次,服务器在第一次请求后,会返回302以及一个地址,浏览器会根据这个地址进行第二次访问
可以跳转到任意的路径,不是自己的工程也可以
效率稍微低一点,需要执行两次请求
后续的请求,没法使用上一次request存储的数据,或者,没有办法使用上一次的request对象,因为这是两次不同的请求
2. 请求转发显示的是请求servlet的地址 http://localhost:8082/HelloServlet/Login?username=admin&password=admin
请求次数只有一次,因为服务器内部帮助客户端执行了后续的工作
只能跳转自己项目的项目路径
效率稍微高一点,执行一次请求
可以使用上一次的request对象
11. Cookie
一份小数据,是服务器给客户端,并且存储在客户端上的小数据
应用场景:自动登陆,浏览记录,购物车
cookie出现的原因:
http的请求是无状态的,客户端与服务器通讯时候是无状态的,客户端在第二次访问服务器时候,是根本不知道这个客户端是以前来访过的。为了更好的收集用户习惯,更好的用户体验和交互。
cookie的简单使用
1. 在响应的时候,添加cookie
2. 客户端接收到信息的界面,响应头多了一个字段set-cookie
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie cookie = new Cookie("wangjing", "2015141452156"); response.setContentType("text/html;charset=UTF-8"); // 给响应添加一个cookie response.addCookie(cookie); response.getWriter().write("请求成功"); }
对于客户端的一个网址,访问一次后cookie便会记录下来
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie cookie = new Cookie("wangjing", "2015141452156"); response.setContentType("text/html;charset=UTF-8"); // 给响应添加一个cookie // response.addCookie(cookie); response.getWriter().write("请求成功"); // 获取客户端返回的cookie Cookie[] cookies = request.getCookies(); if(cookies != null){ for(Cookie c : cookies){ String cookieName = c.getName(); String cookieValue = c.getValue(); System.out.println("cookieName:" + cookieName + ",cookieValue:" + cookieValue); } } }
常用的方法:
案例1:显示最近一次访问的时间
1. 判断账号是否正确,如果正确,则获取cookie,但是得到的cookie是一个数组,要从数组中找到我们想要的对象
2. 如果找到的对象为空,表明第一次登陆,需要添加cookie
3. 如果找到的对象不为空,表明不是第一次登陆,如果更新cookie
package com.wang.cookie; import java.io.IOException; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/LoginServlet_cookie") public class LoginServlet_cookie extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); response.setContentType("text/html;charset=UTF-8"); if("admin".equals(username) && "admin".equals(password)){ Cookie[] cookies = request.getCookies(); Cookie cookie = CookieUtil.findCookie(cookies, "last"); if(cookie == null){ Cookie c = new Cookie("last", System.currentTimeMillis()+""); c.setMaxAge(60*60*3); response.addCookie(c); response.getWriter().write("欢迎您" + username); }else{ long lastVisitTime = Long.parseLong(cookie.getValue()); response.getWriter().write("欢迎您" + username + ",上次来访时间:" + new Date(lastVisitTime)); cookie.setValue(System.currentTimeMillis() + ""); response.addCookie(cookie); } }else{ response.getWriter().write("登陆失败"); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
package com.wang.cookie; import javax.servlet.http.Cookie; public class CookieUtil { public static Cookie findCookie(Cookie[] cookies, String name){ if(cookies != null){ for(Cookie cookie :cookies){ if(name.equals(cookie.getName())){ return cookie; } } } return null; } }
案例2:显示商品的浏览记录
jsp:java server pager 最终会翻译成一个类,就是一个servlet
定义全局变量:<%! int a = 0; %>
定义局部变量:<% int b = 99; %>
在jsp页面上,显示a和b的值
<% =a %>
<% =b %>

<%@ page language = "java" contentType = "text/html;charset=UTF-8" pageEncoding="UTF-8" %> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>会员登录</title> <link rel="stylesheet" href="css/bootstrap.min.css" type="text/css" /> <script src="js/jquery-1.11.3.min.js" type="text/javascript"></script> <script src="js/bootstrap.min.js" type="text/javascript"></script> <!-- 引入自定义css文件 style.css --> <link rel="stylesheet" href="css/style.css" type="text/css" /> <style> body { margin-top: 20px; margin: 0 auto; width: 100%; } .carousel-inner .item img { width: 100%; height: 300px; } </style> </head> <body> <!-- 时间:2015-12-30 描述:菜单栏 --> <div class="container-fluid"> <div class="col-md-4"> <img src="img/logo2.png" /> </div> <div class="col-md-5"> <img src="img/header.png" /> </div> <div class="col-md-3" style="padding-top:20px"> <ol class="list-inline"> <li><a href="login.htm">登录</a></li> <li><a href="register.htm">注册</a></li> <li><a href="cart.htm">购物车</a></li> </ol> </div> </div> <!-- 时间:2015-12-30 描述:导航条 --> <div class="container-fluid"> <nav class="navbar navbar-inverse"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">首页</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">手机数码<span class="sr-only">(current)</span></a></li> <li><a href="#">电脑办公</a></li> <li><a href="#">电脑办公</a></li> <li><a href="#">电脑办公</a></li> </ul> <form class="navbar-form navbar-right" role="search"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search"> </div> <button type="submit" class="btn btn-default">Submit</button> </form> </div> <!-- /.navbar-collapse --> </div> <!-- /.container-fluid --> </nav> </div> <div class="row" style="width:1210px;margin:0 auto;"> <div class="col-md-12"> <ol class="breadcrumb"> <li><a href="#">首页</a></li> </ol> </div> <div class="col-md-2"> <a href="ProductInfoServlet?id=1"> <img src="products/1/cs10001.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>冬瓜</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> <div class="col-md-2"> <a href="ProductInfoServlet?id=2"> <img src="products/1/cs10002.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>圆白菜</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> <div class="col-md-2"> <a href="ProductInfoServlet?id=3"> <img src="products/1/cs10003.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>甜玉米</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> <div class="col-md-2"> <a href="ProductInfoServlet?id=4"> <img src="products/1/cs10004.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>胡萝卜</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> <div class="col-md-2"> <a href="ProductInfoServlet?id=5"> <img src="products/1/cs10005.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>芹菜</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> <div class="col-md-2"> <a href="ProductInfoServlet?id=6"> <img src="products/1/cs10006.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>韭菜</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> <div class="col-md-2"> <a href="product_info.htm"> <img src="products/1/cs10007.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>香菜</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> <div class="col-md-2"> <a href="product_info.htm"> <img src="products/1/cs10008.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>土豆</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> <div class="col-md-2"> <a href="product_info.htm"> <img src="products/1/cs10007.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>香菜</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> <div class="col-md-2"> <a href="product_info.htm"> <img src="products/1/cs10008.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>土豆</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> <div class="col-md-2"> <a href="product_info.htm"> <img src="products/1/cs10007.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>香菜</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> <div class="col-md-2"> <a href="product_info.htm"> <img src="products/1/cs10008.jpg" width="170" height="170" style="display: inline-block;"> </a> <p><a href="product_info.html" style='color:green'>土豆</a></p> <p><font color="#FF0000">商城价:¥299.00</font></p> </div> </div> <!--分页 --> <div style="width:380px;margin:0 auto;margin-top:50px;"> <ul class="pagination" style="text-align:center; margin-top:10px;"> <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">«</span></a></li> <li class="active"><a href="#">1</a></li> <li><a href="#">2</a></li> <li><a href="#">3</a></li> <li><a href="#">4</a></li> <li><a href="#">5</a></li> <li><a href="#">6</a></li> <li><a href="#">7</a></li> <li><a href="#">8</a></li> <li><a href="#">9</a></li> <li> <a href="#" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> </ul> </div> <!-- 分页结束======================= --> <!-- 商品浏览记录: --> <div style="width:1210px;margin:0 auto; padding: 0 9px;border: 1px solid #ddd;border-top: 2px solid #999;height: 246px;"> <h4 style="width: 50%;float: left;font: 14px/30px " 微软雅黑 ";">浏览记录</h4> <div style="width: 50%;float: right;text-align: right;"><a href="">more</a></div> <div style="clear: both;"></div> <div style="overflow: hidden;"> <ul style="list-style: none;"> <% Cookie[] cookies = request.getCookies(); Cookie cookie = com.itcast.servlet.CookieUtil.findCookie(cookies,"history"); if(cookie == null){ %> <h2>您还没有任何的浏览记录</h2> <% }else{ String[] ids = cookie.getValue().split("#"); for(String id : ids){ %> <li style="width: 150px;height: 216;float: left;margin: 0 8px 0 0;padding: 0 18px 15px;text-align: center;"><img src="products/1/cs1000<%=id %>.jpg" width="130px" height="130px" /></li> <% } } %> </ul> </div> <h2><a href = "ClearHistory">清除浏览记录</a></h2> </div> <div style="margin-top:50px;"> <img src="./image/footer.jpg" width="100%" height="78" alt="我们的优势" title="我们的优势" /> </div> <div style="text-align: center;margin-top: 5px;"> <ul class="list-inline"> <li><a>关于我们</a></li> <li><a>联系我们</a></li> <li><a>招贤纳士</a></li> <li><a>法律声明</a></li> <li><a>友情链接</a></li> <li><a target="_blank">支付方式</a></li> <li><a target="_blank">配送方式</a></li> <li><a>服务声明</a></li> <li><a>广告声明</a></li> </ul> </div> <div style="text-align: center;margin-top: 5px;margin-bottom:20px;"> Copyright © 2005-2016 传智商城 版权所有 </div> </body> </html>
@WebServlet("/ProductInfoServlet") public class ProductInfoServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取到当前用户准备浏览的商品ID String id = request.getParameter("id"); Cookie[] cookies = request.getCookies(); Cookie cookie = CookieUtil.findCookie(cookies, "history"); if (cookie == null) { Cookie c = new Cookie("history", id); // 设置有效期 c.setMaxAge(60 * 60 * 24 * 7); // 设置访问这个工程的时候,才带cookie过来 c.setPath("/Product_view"); response.addCookie(c); } else { // 获取以前的cookie String ids = cookie.getValue(); // 让现在浏览的商品和以前浏览的商品形成cookie新的值 cookie.setValue(id + "#" + ids); // 需要重新设置有效期 cookie.setMaxAge(60 * 60 * 24 * 7); // 设置访问这个工程的时候,才带cookie过来 cookie.setPath("/Product_view"); response.addCookie(cookie); } response.sendRedirect("product_info.htm"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
清除浏览记录:其实就是清除cookie,删除cookie没有什么delete方法,只有设置maxage=0
@WebServlet("/ClearHistory") public class ClearHistory extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 清除cookie Cookie cookie = new Cookie("history", ""); cookie.setMaxAge(0); // 设置立即删除 cookie.setPath("/Product_view"); response.addCookie(cookie); response.sendRedirect("product_list.jsp"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
cookie总结:
1. 服务器给客户端发过来的一小份数据,并且存放在客户端上面
2. 获取cookie,添加cookie
request.getCookie();
response.addCookie();
3. Cookie分类
会话cookie:默认情况下,关闭浏览器,cookie就会消失
持久cookie:在一定的时间,都有效,并且保存在客户端上面
cookie.setMasAge(100);
4. cookie的安全问题:
由于cookie保存在客户端上,会有安全隐患
cookie的大小与客户端有限制,一个web工程限制20个,总共限制300个,为了解决这个问题,出现了session
12 Session
session是基于Cookie的一种会话机制,cookie是数据存放在客户端,session是数据存放在服务器端。
session何时创建,何时销毁
创建:如果在servlet里面调用了request.getSession();
销毁:存放在服务器内存中的一份数据,可以持久化,关闭了浏览器,session也不会销毁
session会话时间过期,默认时间为30分钟或者关闭服务器,session销毁
// 得到会话的ID String id = session.getId(); // 存值 session.setAttribute("wang", "2015141452156"); // 取值 session.getAttribute("wang"); // 移除值 session.removeAttribute("wang");
@WebServlet("/LoginSession") public class LoginSession extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); String id = request.getSession().getId(); System.out.println("session id:" + id); response.getWriter().write("收到请求了"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
案例:简单的购物车
package com.itcast.session; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class CarServlet */ @WebServlet("/CarServlet") public class CarServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); // 1. 获取要添加到购物车的商品id System.out.println(request.getParameter("id")); int id = Integer.parseInt(request.getParameter("id")); System.out.println(id); String[] names = {"Iphone", "小米", "华为", "锤子", "洛基亚"}; String name = names[id]; // 2. 获取购物车存放东西的session Map<String, Integer> Map<String, Integer> map = (Map<String, Integer>) request.getSession().getAttribute("cart"); // session里面没有任何东西 if(map == null){ map = new LinkedHashMap<String, Integer>(); request.getSession().setAttribute("cart", map); } // 3. 判断购物车中有没有这个商品 if(map.containsKey(name)){ map.put(name, map.get(name) + 1); }else{ map.put(name, 1); } // 4. 输出界面,跳转 response.getWriter().write("<a href = 'product_list.jsp'><h3>继续购物</h3></a>"); response.getWriter().write("<a href = 'cart.jsp'><h3>去购物车结算</h3></a>"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
package com.itcast.session; 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; import javax.servlet.http.HttpSession; /** * Servlet implementation class ClearCart */ @WebServlet("/ClearCart") public class ClearCart extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); // session.invalidate(); // 使此会话无效,直接使里面存放的所有数据消失 session.removeAttribute("cart"); response.sendRedirect("cart.jsp"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'cart.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <!-- 展示购物车的商品 --> <h2>您的购物车商品如下</h2> <% Map<String, Integer> map = (Map<String, Integer>)session.getAttribute("cart"); if(map != null){ for(String key : map.keySet()){ int value = map.get(key); %> <h3>名称:<%=key %>, 数量:<%=value %></h3> <% } } %> <a href = "ClearCart"><h4>清空购物车</h4></a> </body> </html>

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'product_list.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <a href = "CarServlet?id=0"><h3>Iphone</h3></a> <a href = "CarServlet?id=1"><h3>小米</h3></a> <a href = "CarServlet?id=2"><h3>华为</h3></a> <a href = "CarServlet?id=3"><h3>锤子</h3></a> <a href = "CarServlet?id=4"><h3>洛基亚</h3></a> </body> </html>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人