通用
死记:
网址编解码:
// URI遇空格报错,故可清除左侧空格,将其他空格编码为 %2B 传至后端;若不转为URI则无需替换。
var r = URLEncoder.encode(" https://x/path?qs=a b ".replaceFirst("^\\s+", "").replaceAll("\\s", "+"));
URI.create(URLDecoder.decode(r)); // 后端会自动 decode 至 "https://x/path?qs=a++b+"
注意 - HttpServletRequest.getInputStream()首次读取正常,再次读取会报 IllegalStateException ,可通过中间变量暂存。
解决中文乱码(Java18起默认已是UTF-8/Open Liberty InstantOn需设置) - response.setContentType("text/plain; charset=utf-8");
Servlet:
[可选配置文件]web.xml - web-app_6_1.xsd 版本列表(不存在或写高了均取最高支持版本)
Servlet支持版本 - quarkus-undertow为4.0,Open Liberty和Jetty则为6.0。
OpenLiberty:
OpenLiberty 官方称Ubuntu Beta镜像无计划提高当前的JDK17版本 - 仅用于 org.bytedeco:ffmpeg 安装 libxcb 相关库。
OpenLiberty 基于 CRIU 的快速启动容器镜像 - https://openliberty.io/docs/latest/instanton.html
server.xml 环境变量 OUR_PASSWORD 用法:dataSource 节点 properties password="${env.OUR_PASSWORD}";但 keyStore 节点 password 则要么明文,要么 securityUtility 编下码。
gradle 运行后 build\wlp\bin\securityUtility encode --encoding=aes-256 --key=加解密字符(只用于AES/亲测未成功!) OUR_KEYSTORE_PASSWORD 输出 {aes}AbCdAbcd
解码:<feature>passwordUtilities-1.1</feature> //System.setProperty("wlp.password.encryption.key", "加解密字符");
PasswordUtil.decode("{aes}AbCdAbcd"); // implementation("io.openliberty.features:passwordUtilities-1.1:25.0.0.9"){ exclude(group = "io.openliberty.features") }
Open Liberty - App.class.getResource("/x.wasm"); // wsjar:file:/opt/ol/wlp/usr/servers/defaultServer/apps/x.war!/WEB-INF/classes/x.wasm
path支持file和folder路径 - <library id="someLib"><path name="libs/lib-folder/ 或 lib.jar"/></library>
HTTPS证书:
说明 - 禁用 HTTPS 协议用 httpsPort="-1" 默认端口为 9443。
<feature>transportSecurity-1.0</feature>
<httpEndpoint id="defaultHttpEndpoint" ... httpsPort="${https.port}"><sslOptions sslRef="无此节点则取defaultSSLConfig" /></httpEndpoint>
密码至少6位,location="${server.output.dir}/resources/security/key.p12",即 defaultSSLConfig 默认的 keyStoreRef="defaultKeyStore"。
<!-- location与默认生成证书(key.p12)同名会被其覆盖,先测绝对路径;password 亲测明文或aes均可用。 -->
<keyStore id="defaultKeyStore" location="/opt/ol/wlp/usr/servers/defaultServer/resources/security/keystore.p12" password="明文或{aes}AbCdAbcd" />
非明文则还应提供:<variable name="wlp.password.encryption.key" value="${env.OUR_KEYSTORE_PASSWORD}" />
或独立文件提供:<include location="var.xml" /> 包含 <server><variable ... /></server>
jar包类优先于war同名类:<webApplication location="appThatNeedsFix.war"><classloader overrideLibraryRef="someImplFix"/></webApplication>
常用:
request.getParameter("url") 会自动解码 URLEncoder.encode(request.getRequestURL().toString() + "?" + request.getQueryString(), Charset.defaultCharset());
// 首个空参数 ?& 便于后续只处理&符号。
var p1="k=v"; "https://example.com/path/?&" + p1 + "&" + p2;
JAX-RS中必须调用flush()来刷新缓存区,否则响应HTTP 204: response.getWriter().print(jsScript); response.getWriter().flush();
Cookie - response.addCookie(cv); // 无法被本请求的 request.getCookies() 感知到。
Session - request.getSession(false) 过期设置 web.xml :
<web-app ...>
<session-config><session-timeout>-1</session-timeout><cookie-config><max-age>[默认]浏览器关闭JSESSIONID即失效</max-age></cookie-config></session-config>
</web-app>
或 session.setMaxInactiveInterval(30 * 60); // 仅生效在当前请求上下文,单位秒。
JAX-RS解决JSON Binding (JSON-B)报错 - Could not find MessageBodyWriter for response object of type: pkg.JavaRecord$OrPOJO of media type: application/json
引入库 yasson ;若为 OpenLiberty 则添加 jsonb-3.0
JAX-RS SSE(Server-Sent Events): jakarta.ws.rs.sse.SseEventSink,也能用 Servlet 流式响应 asyncSupported=true 模拟。
1分20秒重连问题解决 - 开长连接、关闭缓存:(尽量不要在VPN网络下测试)location = /apis/add-video-watermark {
proxy_http_version 1.1; # 默认的1.0不支持Keep-Alive,而1.1则默认处于长连接。
proxy_buffering off; # 关闭响应缓存,关闭后会连带proxy_cache也处于off,类似no-cache。
proxy_set_header Connection '';
proxy_read_timeout 500;
proxy_pass http://ip:9080; # SSE不能用在try_files指令
}
必须的响应头:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive