40、Servlet与JSP快速参考指南

Servlet与JSP快速参考指南

1. 常见HTTP请求头

在HTTP请求中,有一些常见的请求头,它们各自有着不同的作用:
- Cookie :服务器之前发送给客户端的cookie,使用 getCookies 方法获取,而非 getHeader
- Host :原始URL中给出的主机名,这是HTTP 1.1中的必选头。
- If-Modified-Since :表示客户端仅在页面在指定日期之后有更改时才需要该页面,建议实现 getLastModified 方法来处理。
- Referer :引用网页的URL。
- User-Agent :标识发出请求的浏览器的字符串。

2. 标准CGI变量的访问

通常应从请求信息、响应信息和服务器信息的角度考虑,而非CGI变量。以下是一些相关的功能和对应的Servlet方法:
| CGI变量 | Servlet等效方法 |
| ---- | ---- |
| AUTH_TYPE | request.getAuthType() |
| CONTENT_LENGTH | request.getContentLength() |
| CONTENT_TYPE | request.getContentType() |
| DOCUMENT_ROOT | getServletContext().getRealPath(“/”) |
| HTTP_XXX_YYY | request.getHeader(“Xxx - Yyy”) |
| PATH_INFO | request.getPathInfo() |
| PATH_TRANSLATED | request.getPathTranslated() |
| QUERY_STRING | request.getQueryString() |
| REMOTE_ADDR | request.getRemoteAddr() |
| REMOTE_HOST | request.getRemoteHost() |
| REMOTE_USER | request.getRemoteUser() |
| REQUEST_METHOD | request.getMethod() |
| SCRIPT_NAME | request.getServletPath() |
| SERVER_NAME | request.getServerName() |
| SERVER_PORT | request.getServerPort() |
| SERVER_PROTOCOL | request.getProtocol() |
| SERVER_SOFTWARE | getServletContext().getServerInfo() |

3. HTTP状态码
3.1 HTTP响应格式

HTTP响应的格式依次为:状态行(HTTP版本、状态码、消息)、响应头、空行、文档。例如:

HTTP/1.1 200 OK
Content-Type: text/plain
Hello World
3.2 设置状态码的方法

HttpServletResponse 中有以下设置状态码的方法,需在向浏览器发送文档内容之前设置状态码:
- public void setStatus(int statusCode) :使用常量表示状态码,而非显式的整数。
- public void sendError(int code, String message) :将消息包装在一个小的HTML文档中。
- public void sendRedirect(String url) :在2.2版本中允许使用相对URL。

3.3 状态码分类
状态码范围 含义
100 - 199 信息性状态码,客户端应采取其他操作
200 - 299 请求成功
300 - 399 文件已移动,通常包含一个Location头指示新地址
400 - 499 客户端错误
500 - 599 服务器错误
3.4 常见HTTP 1.1状态码
  • 200 (OK) :一切正常,文档随后返回,这是Servlet的默认状态码。
  • 204 (No Content) :浏览器应继续显示上一个文档。
  • 301 (Moved Permanently) :请求的文档已永久移动到其他位置(在Location头中指示),浏览器会自动转到新位置。
  • 302 (Found) :请求的文档临时移动到其他位置(在Location头中指示),浏览器会自动转到新位置,Servlet设置此头时应使用 sendRedirect ,而非 setStatus
  • 401 (Unauthorized) :浏览器试图访问受密码保护的页面,但没有提供正确的Authorization头。
  • 404 (Not Found) :没有该页面,Servlet应使用 sendError 设置此头。
4. HTTP响应头
4.1 设置任意响应头

HttpServletResponse 中有以下设置响应头的方法,需在向浏览器发送文档内容之前设置:
- public void setHeader(String headerName, String headerValue) :设置任意头。
- public void setDateHeader(String headerName, long milliseconds) :将自1970年以来的毫秒数转换为GMT格式的日期字符串。
- public void setIntHeader(String headerName, int headerValue) :避免在调用 setHeader 之前将整数转换为字符串。
- addHeader addDateHeader addIntHeader :添加头的新实例,而非替换,仅在2.2版本中可用。

4.2 设置常见响应头
  • setContentType :设置 Content-Type 头,Servlet几乎总是使用此方法。
  • setContentLength :设置 Content-Length 头,用于持久HTTP连接,可使用 ByteArrayOutputStream 缓冲文档以确定大小。
  • addCookie :向 Set-Cookie 头添加值。
  • sendRedirect :设置 Location 头(并更改状态码)。
4.3 常见HTTP 1.1响应头
响应头 含义
Allow 服务器支持的请求方法,当Servlet收到OPTIONS请求时,默认的 service 方法会自动设置。
Cache-Control no-cache 值可防止浏览器缓存结果,同时发送相同值的 Pragma 头,以防浏览器仅支持HTTP 1.0。
Content-Encoding 文档的编码方式,浏览器在处理文档之前会反转此编码,Servlet在使用之前必须确认浏览器支持该编码。
Content-Length 响应中的字节数。
Content-Type 返回文档的MIME类型。
Expires 文档应被视为过期的时间,使用 setDateHeader 设置此头。
Last-Modified 文档最后更改的时间,建议提供 getLastModified 方法,而非显式设置此头。
Location 浏览器应重新连接的URL,建议使用 sendRedirect ,而非直接设置此头。
Pragma no-cache 值指示HTTP 1.0客户端不缓存文档。
Refresh 浏览器应重新加载页面的秒数,也可包含要连接的URL。
Set-Cookie 浏览器应记住的cookie,建议使用 addCookie ,而非直接设置此头。
WWW-Authenticate 客户端应在下一个请求的 Authorization 头中提供的授权类型和领域。
5. 从Servlet生成GIF图像

以下是从Servlet生成GIF图像的步骤:
1. 创建一个图像:使用 Component createImage 方法。
2. 绘制图像:调用图像的 getGraphics 方法,然后进行正常的绘图操作。
3. 设置 Content-Type 响应头:使用 response.setContentType("image/gif")
4. 获取输出流:使用 response.getOutputStream()
5. 以GIF格式将图像发送到输出流:使用Jef Poskanzer的 GifEncoder ,可参考 http://www.acme.com/java/

6. 处理Cookie
6.1 Cookie的典型用途
  • 在电子商务会话中识别用户。
  • 避免输入用户名和密码。
  • 定制网站。
  • 聚焦广告。
6.2 Cookie存在的问题

主要是隐私问题,而非安全问题。例如,服务器可以记住用户在以前会话中的操作,若用户提供个人信息,服务器可以将该信息与用户的先前操作关联起来,服务器还可以通过合作的第三方(如doubleclick.net)共享cookie信息,设计不佳的网站可能会直接在cookie中存储敏感信息(如信用卡号)。

6.3 Cookie的一般用法
  • 发送cookie到浏览器(标准方法)
Cookie c = new Cookie("name", "value");
c.setMaxAge(...);
// 设置其他属性
response.addCookie(c);
  • 发送cookie到浏览器(简化方法) :使用 LongLivedCookie 类。
  • 从浏览器读取cookie(标准方法)
Cookie[] cookies = response.getCookies();
for(int i = 0; i < cookies.length; i++) {
    Cookie c = cookies[i];
    if (c.getName().equals("someName")) {
        doSomethingWith(c);
        break;
    }
}
  • 从浏览器读取cookie(简化方法) :使用 ServletUtilities.getCookie ServletUtilities.getCookieValue 从cookie数组中提取cookie或cookie值。
6.4 Cookie方法
方法 描述
getComment/setComment 获取/设置注释,大多数浏览器支持的版本0的cookie不支持此方法。
getDomain/setDomain 指定cookie适用的域名,当前主机必须是指定域名的一部分。
getMaxAge/setMaxAge 获取/设置cookie的过期时间(以秒为单位),若未设置,cookie仅适用于当前浏览会话。
getName/setName 获取/设置cookie名称,新cookie通过构造函数提供名称,对于传入的cookie数组,使用 getName 查找感兴趣的cookie。
getPath/setPath 获取/设置cookie适用的路径,若未指定,cookie适用于包含当前页面的目录内或以下的URL。
getSecure/setSecure 获取/设置标志,指示cookie是否仅适用于SSL连接。
getValue/setValue 获取/设置与cookie关联的值,新cookie通过构造函数提供值,对于传入的cookie数组,使用 getName 查找感兴趣的cookie,然后调用 getValue
getVersion/setVersion 获取/设置cookie协议版本,默认版本为0,在浏览器开始支持版本1之前,建议使用版本0。
7. 会话跟踪
7.1 查找会话信息
HttpSession session = request.getSession(true);
ShoppingCart cart = (ShoppingCart)session.getValue("shoppingCart");
if (cart == null) { // 会话中没有购物车
    cart = new ShoppingCart();
    session.putValue("shoppingCart", cart);
}
doSomethingWith(cart);
7.2 将会话信息关联起来
HttpSession session = request.getSession(true);
session.putValue("referringPage", request.getHeader("Referer"));
ShoppingCart cart = (ShoppingCart)session.getValue("previousItems");
if (cart == null) { // 会话中没有购物车
    cart = new ShoppingCart();
    session.putValue("previousItems", cart);
}
String itemID = request.getParameter("itemID");
if (itemID != null) {
    cart.addItem(Catalog.getItem(itemID));
}
7.3 HttpSession方法
方法 描述
public Object getValue(String name) [2.1]
public Object getAttribute(String name) [2.2]
从会话对象中提取先前存储的值,若没有与给定名称关联的值,则返回null。
public void putValue(String name, Object value) [2.1]
public void setAttribute(String name, Object value) [2.2]
将值与名称关联起来,若值实现了 HttpSessionBindingListener 接口,会调用其 valueBound 方法,若先前的值实现了该接口,会调用其 valueUnbound 方法。
public void removeValue(String name) [2.1]
public void removeAttribute(String name) [2.2]
移除与指定名称关联的任何值,若要移除的值实现了 HttpSessionBindingListener 接口,会调用其 valueUnbound 方法。
public String[] getValueNames() [2.1]
public Enumeration getAttributeNames() [2.2]
返回会话中所有属性的名称。
public String getId() 返回为每个会话生成的唯一标识符。
public boolean isNew() 若客户端(浏览器)从未见过该会话,则返回true,否则返回false。
public long getCreationTime() 返回会话首次创建的时间(自1970年以来的毫秒数),若要获取可用于打印的值,可将该值传递给 Date 构造函数或 GregorianCalendar setTimeInMillis 方法。
public long getLastAccessedTime() 返回会话最后一次从客户端发送的时间。
public int getMaxInactiveInterval()
public void setMaxInactiveInterval(int seconds)
获取/设置会话在自动失效之前应无访问的时间(以秒为单位),负值表示会话永不过期,与cookie过期日期不同。
public void invalidate() 使会话无效,并解除与它关联的所有对象的绑定。
8. JSP脚本元素
8.1 脚本元素类型
  • 表达式 <%= expression %> ,表达式会被求值并插入到Servlet的输出中,也可使用 <jsp:expression> expression </jsp:expression>
  • 脚本段 <% code %> ,脚本段会插入到Servlet的 _jspService 方法中(由 service 调用),也可使用 <jsp:scriptlet> code </jsp:scriptlet>
  • 声明 <%! code %> ,声明会插入到Servlet类的主体中,在任何现有方法之外,也可使用 <jsp:declaration> code </jsp:declaration>
8.2 模板文本
  • 使用 <\% 在输出中获取 <%
  • <%-- JSP注释 --%> 是JSP注释。
  • <!-- HTML注释 --> 是HTML注释。
  • 所有其他非JSP特定的文本会直接传递到输出页面。
8.3 预定义变量

在表达式和脚本段(不包括声明)中自动可用的隐式对象:
| 变量 | 描述 |
| ---- | ---- |
| request | 与请求关联的 HttpServletRequest 。 |
| response | 与客户端响应关联的 HttpServletResponse 。 |
| out | 用于向客户端发送输出的 JspWriter PrintWriter 的子类)。 |
| session | 与请求关联的 HttpSession 对象。 |
| application | 通过 getServletConfig().getContext() 获得的 ServletContext ,由服务器或Web应用程序中的所有Servlet和JSP页面共享。 |
| config | 此页面的 ServletConfig 对象。 |
| pageContext | 与当前页面关联的 PageContext 对象。 |
| page | 等同于 this (当前Servlet实例),目前不太有用,为未来预留。 |

9. JSP页面指令:构建生成的Servlet
9.1 import属性
  • <%@ page import="package.class" %>
  • <%@ page import="package.class1,...,package.classN" %>
9.2 contentType属性
  • <%@ page contentType="MIME-Type" %>
  • <%@ page contentType="MIME-Type; charset=Character-Set" %>
    该属性不能有条件地调用,若需要有条件地设置,可使用 <% response.setContentType("..."); %>

示例:使用 contentType

<%@ page contentType="application/vnd.ms-excel" %>
<%-- 注意列之间是制表符,而非空格 --%>
1997
1998
1999
2000
2001 (Anticipated)
12.3
13.4
14.5
15.6
16.7
9.3 isThreadSafe属性
  • <%@ page isThreadSafe="true" %> (默认)
  • <%@ page isThreadSafe="false" %>
    值为 true 表示代码是线程安全的,系统可以发送多个并发请求;值为 false 表示JSP文档生成的Servlet将实现 SingleThreadModel

非线程安全代码示例

<%! private int idNum = 0; %>
<% String userID = "userID" + idNum;
out.println("Your ID is " + userID + ".");
idNum = idNum + 1; %>

线程安全代码示例

<%! private int idNum = 0; %>
<% synchronized(this) {
    String userID = "userID" + idNum;
    out.println("Your ID is " + userID + ".");
    idNum = idNum + 1; 
} %>
9.4 其他属性
属性 描述
session <%@ page session="true" %> (默认), <%@ page session="false" %>
buffer <%@ page buffer="sizekb" %> <%@ page buffer="none" %> ,服务器可以使用比指定更大的缓冲区,但不能更小。
autoflush <%@ page autoflush="true" %> (默认), <%@ page autoflush="false" %> ,当 buffer="none" 时, autoflush 不能为 false
extends <%@ page extends="package.class" %>
info <%@ page info="Some Message" %>
errorPage <%@ page errorPage="Relative URL" %> ,抛出的异常会通过 exception 变量自动提供给指定的错误页面。
isErrorPage <%@ page isErrorPage="true" %> <%@ page isErrorPage="false" %> (默认)。
language <%@ page language="cobol" %> ,目前Java是默认且唯一合法的选择,无需关注此属性。
9.5 XML语法
  • 通常语法: <%@ page attribute="value" %> <%@ page import="java.util.*" %>
  • XML等效语法: <jsp:directive.page attribute="value" /> <jsp:directive.page import="java.util.*" />
10. 在JSP文档中包含文件和小程序
10.1 在页面翻译时包含文件
  • <%@ include file="Relative URL" %> ,更改包含的文件不一定会导致JSP文档重新翻译,需要手动更改JSP文档或更新其修改日期,例如:
<%-- Navbar.jsp modified 3/1/00 --%> 
<%@ include file="Navbar.jsp" %> 
10.2 在请求时包含文件
  • <jsp:include page="Relative URL" flush="true" /> ,Servlet可以使用 RequestDispatcher include 方法实现类似结果,使用Java Web Server时,包含的文件必须使用 .html .htm 扩展名。
10.3 Java插件的小程序(简单情况)
  • 常规形式
<APPLET CODE="MyApplet.class" WIDTH=475 HEIGHT=350>
</APPLET>
  • JSP形式(Java插件)
<jsp:plugin type="applet" 
            code="MyApplet.class"
            width="475" height="350">
</jsp:plugin>
10.4 jsp:plugin属性
属性 描述
type 对于小程序,此属性值应为 applet
code APPLET CODE 属性相同。
width APPLET WIDTH 属性相同。
height APPLET HEIGHT 属性相同。
codebase APPLET CODEBASE 属性相同。
align APPLET IMG ALIGN 属性相同。
hspace APPLET HSPACE 属性相同。
vspace APPLET VSPACE 属性相同。
archive APPLET ARCHIVE 属性相同。
name APPLET NAME 属性相同。
title APPLET (以及HTML 4.0中几乎所有其他HTML元素)罕见的 TITLE 属性相同,用于指定可用于工具提示或索引的标题。
jreversion 标识所需的Java运行时环境(JRE)版本,默认值为1.1。
iepluginurl 指定可从其下载Internet Explorer插件的URL。
nspluginurl 指定可从其下载Netscape插件的URL。
10.5 HTML中的参数:jsp:param
  • 常规形式
<APPLET CODE="MyApplet.class" WIDTH=475 HEIGHT=350>
<PARAM NAME="PARAM1" VALUE="VALUE1">
<PARAM NAME="PARAM2" VALUE="VALUE2">
</APPLET>
  • JSP形式(Java插件)
<jsp:plugin type="applet" 
            code="MyApplet.class"
            width="475" height="350">
<jsp:params>
<jsp:param name="PARAM1" value="VALUE1" />
<jsp:param name="PARAM2" value="VALUE2" />
</jsp:params>
</jsp:plugin>
10.6 替代文本
  • 常规形式
<APPLET CODE="MyApplet.class" WIDTH=475 HEIGHT=350>
<B>Error: this example requires Java.</B>
</APPLET>
  • JSP形式(Java插件)
<jsp:plugin type="applet" 
            code="MyApplet.class"
            width="475" height="350">
    <!-- 替代文本 -->
</jsp:plugin>

Servlet与JSP快速参考指南

11. URL编码

为了确保在Servlet使用URL重写实现会话跟踪时,系统有机会对URL进行编码,需要对不同类型的URL进行处理:
- 常规URL

String originalURL = someRelativeOrAbsoluteURL;
String encodedURL = response.encodeURL(originalURL);
out.println("<A HREF=\"" + encodedURL + "\">...</A>");
  • 重定向URL
String originalURL = someURL; // 2.2版本中相对URL也可行
String encodedURL = response.encodeRedirectURL(originalURL);
response.sendRedirect(encodedURL);
12. 总结与注意事项

在使用Servlet和JSP进行开发时,有许多关键的知识点和操作需要注意,以下通过表格形式进行总结:
| 类别 | 关键知识点 | 注意事项 |
| ---- | ---- | ---- |
| HTTP请求头 | Cookie、Host、If - Modified - Since、Referer、User - Agent等 | 使用正确的方法获取请求头信息,如使用 getCookies 获取Cookie |
| CGI变量访问 | 了解各CGI变量对应的Servlet等效方法 | 从请求、响应和服务器信息角度考虑,而非单纯依赖CGI变量 |
| HTTP状态码 | 不同状态码范围的含义及常见状态码 | 设置状态码要在发送文档内容之前,使用常量表示状态码 |
| HTTP响应头 | 设置任意和常见响应头的方法 | 同样要在发送文档内容之前设置响应头 |
| 生成GIF图像 | 创建、绘制图像,设置响应头,发送图像到输出流 | 使用指定的 GifEncoder 库 |
| 处理Cookie | 典型用途、存在问题及一般用法 | 注意隐私问题,使用合适的方法操作Cookie |
| 会话跟踪 | 查找和关联会话信息的方法 | 了解 HttpSession 的各种方法及使用场景 |
| JSP脚本元素 | 表达式、脚本段、声明的使用及预定义变量 | 注意不同脚本元素的插入位置和预定义变量的作用范围 |
| JSP页面指令 | 多个属性的使用及XML语法 | 注意各属性的功能和使用条件,如 contentType 不能有条件调用 |
| 包含文件和小程序 | 不同时机包含文件及小程序的使用 | 注意文件扩展名和 jsp:plugin 属性的设置 |
| URL编码 | 常规URL和重定向URL的编码 | 确保系统有机会对URL进行编码 |

13. 实际应用流程示例

以下是一个简单的Servlet和JSP开发的实际应用流程示例,使用mermaid流程图展示:

graph LR
    A[接收HTTP请求] --> B[处理请求头信息]
    B --> C{判断请求类型}
    C -->|静态资源请求| D[返回静态资源]
    C -->|动态资源请求| E[调用Servlet或JSP]
    E --> F[处理业务逻辑]
    F --> G[设置响应头和状态码]
    G --> H[生成响应内容]
    H --> I[发送响应到客户端]
14. 代码示例整合

为了更好地理解上述知识点的综合应用,以下是一个简单的JSP示例,展示了如何使用会话跟踪、设置响应头和包含文件:

<%@ page import="java.util.*" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page session="true" %>
<%@ include file="header.jsp" %>

<%
    HttpSession session = request.getSession(true);
    String username = (String) session.getAttribute("username");
    if (username == null) {
        username = "Guest";
        session.setAttribute("username", username);
    }
%>

<!DOCTYPE html>
<html>
<head>
    <title>Welcome Page</title>
</head>
<body>
    <h1>Welcome, <%= username %>!</h1>
    <p>You are now in our system.</p>
    <jsp:include page="footer.jsp" flush="true" />
</body>
</html>
15. 常见错误及解决方法

在Servlet和JSP开发过程中,可能会遇到一些常见的错误,以下通过列表形式给出常见错误及解决方法:
- 状态码设置错误
- 错误表现 :页面显示异常,状态码不符合预期。
- 解决方法 :检查设置状态码的代码,确保在发送文档内容之前设置,使用常量表示状态码。
- Cookie操作问题
- 错误表现 :无法正确获取或设置Cookie。
- 解决方法 :检查Cookie的名称、值和属性设置,使用正确的方法操作Cookie,如 addCookie getCookies 等。
- JSP页面指令使用错误
- 错误表现 :页面出现编译错误或显示异常。
- 解决方法 :检查各属性的使用条件,如 contentType 不能有条件调用, autoflush buffer="none" 时不能为 false
- URL编码问题
- 错误表现 :会话跟踪失效,页面跳转异常。
- 解决方法 :确保对常规URL和重定向URL进行正确的编码,使用 response.encodeURL response.encodeRedirectURL

16. 性能优化建议

为了提高Servlet和JSP应用的性能,可以考虑以下优化建议:
- 合理使用缓存 :通过设置 Cache - Control Expires 响应头,合理控制页面和资源的缓存,减少服务器的负载。
- 优化会话管理 :设置合理的会话超时时间,避免长时间占用服务器资源,及时清理不再使用的会话信息。
- 减少文件包含 :尽量减少不必要的文件包含,避免在页面翻译时包含过多的文件,提高页面加载速度。
- 使用线程安全代码 :在多线程环境下,确保代码是线程安全的,避免出现数据不一致的问题。

17. 未来趋势与展望

随着Web技术的不断发展,Servlet和JSP仍然在企业级Web开发中占据重要地位。未来可能会朝着以下方向发展:
- 与新兴框架的融合 :与Spring、Spring Boot等框架进一步融合,提供更强大的功能和更便捷的开发体验。
- 性能和安全性的提升 :不断优化性能,加强安全性,以应对日益增长的Web应用需求。
- 支持更多的Web标准 :更好地支持HTML5、CSS3等Web标准,提供更好的用户体验。

通过对上述知识点的深入理解和实践,开发者可以更好地掌握Servlet和JSP技术,开发出高效、稳定的Web应用程序。希望本文能为大家在Servlet和JSP开发过程中提供有价值的参考。

随着信息技术在管理上越来越深入而广泛的应用,作为学校以及一些培训机构,都在用信息化战术来部署线上学习以及线上考试,可以线下的考试有机的结合在一起,实现基于SSM的小码创客教育教学资源库的设计实现在技术上已成熟。本文介绍了基于SSM的小码创客教育教学资源库的设计实现的开发全过程。通过分析企业对于基于SSM的小码创客教育教学资源库的设计实现的需求,创建了一个计算机管理基于SSM的小码创客教育教学资源库的设计实现的方案。文章介绍了基于SSM的小码创客教育教学资源库的设计实现的系统分析部分,包括可行性分析等,系统设计部分主要介绍了系统功能设计和数据库设计。 本基于SSM的小码创客教育教学资源库的设计实现有管理员,校长,教师,学员四个角色。管理员可以管理校长,教师,学员等基本信息,校长角色除了校长管理之外,其他管理员可以操作的校长角色都可以操作。教师可以发布论坛,课件,视频,作业,学员可以查看和下载所有发布的信息,还可以上传作业。因而具有一定的实用性。 本站是一个B/S模式系统,采用Java的SSM框架作为开发技术,MYSQL数据库设计开发,充分保证系统的稳定性。系统具有界面清晰、操作简单,功能齐全的特点,使得基于SSM的小码创客教育教学资源库的设计实现管理工作系统化、规范化。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值