這裡分類和彙總了欣宸的全部原創(含配套原始碼):https://github.com/zq2599/blog_demos
名稱 | 連結 | 備註 |
---|---|---|
專案主頁 | https://github.com/zq2599/blog_demos | 該專案在GitHub上的主頁 |
git倉庫地址(https) | https://github.com/zq2599/blog_demos.git | 該專案原始碼的倉庫地址,https協定 |
git倉庫地址(ssh) | [email protected]:zq2599/blog_demos.git | 該專案原始碼的倉庫地址,ssh協定 |
package com.bolingcavalry.handlerdemo;
import nginx.clojure.NginxClojureRT;
import nginx.clojure.java.NginxJavaRingHandler;
import java.io.IOException;
import java.util.Map;
public class MyInitHandler implements NginxJavaRingHandler {
@Override
public Object[] invoke(Map<String, Object> map) throws IOException {
// 可以根據實際需求執行初始化操作,這裡作為演示,只列印紀錄檔
NginxClojureRT.log.info("MyInitHandler.invoke executed");
return null;
}
}
jvm_handler_type 'java';
jvm_init_handler_name 'com.bolingcavalry.handlerdemo.MyInitHandler';
2022-02-05 23:02:37[info][73954][main]MyInitHandler.invoke executed
location /contentdemo {
# 第一個自定義屬性
content_handler_property foo.name 'foo.value';
# 第二個自定義屬性
content_handler_property bar.name 'bar.value';
# 邏輯處理類
content_handler_name 'com.bolingcavalry.handlerdemo.MyContentHandler';
}
package com.bolingcavalry.handlerdemo;
import nginx.clojure.Configurable;
import nginx.clojure.java.ArrayMap;
import nginx.clojure.java.NginxJavaRingHandler;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Map;
import static nginx.clojure.MiniConstants.CONTENT_TYPE;
import static nginx.clojure.MiniConstants.NGX_HTTP_OK;
public class MyContentHandler implements NginxJavaRingHandler, Configurable {
private Map<String, String> config;
/**
* location中設定的content_handler_property屬性會通過此方法傳給當前類
* @param map
*/
@Override
public void config(Map<String, String> map) {
this.config = map;
}
@Override
public Object[] invoke(Map<String, Object> map) throws IOException {
String body = "From MyContentHandler, "
+ LocalDateTime.now()
+ ", foo : "
+ config.get("foo.name")
+ ", bar : "
+ config.get("bar.name");
return new Object[] {
NGX_HTTP_OK, //http status 200
ArrayMap.create(CONTENT_TYPE, "text/plain"), //headers map
body
};
}
}
# 1. 定義變數,用於儲存路徑
set $myhost "";
location /myproxy {
rewrite_handler_type 'java';
# 2. java程式碼中為變數賦值
rewrite_handler_name 'com.bolingcavalry.handlerdemo.MyRewriteProxyPassHandler';
# 3. 用變數的值作為地址進行跳轉
proxy_pass $myhost;
}
package com.bolingcavalry.handlerdemo;
import nginx.clojure.NginxClojureRT;
import nginx.clojure.java.NginxJavaRequest;
import nginx.clojure.java.NginxJavaRingHandler;
import java.util.Map;
import static nginx.clojure.java.Constants.PHASE_DONE;
public class MyRewriteProxyPassHandler implements NginxJavaRingHandler {
@Override
public Object[] invoke(Map<String, Object> req) {
// 根據業務情況客製化計算出的path
String myhost = computeMyHost(req);
// 用setVariable方法設定myhost變數的值,這個myhost在這個location中被定義,跳轉的時候就用這個值作為path
((NginxJavaRequest)req).setVariable("myhost", myhost);
// 返回PHASE_DONE之後,nginx-clojure框架就會執行proxy_pass邏輯,
// 如果返回的不是PHONE_DONE,nginx-clojure框架就把這個handler當做content handler處理
return PHASE_DONE;
}
/**
* 這裡寫入業務邏輯,根據實際情況確定返回的path
* @param req
* @return
*/
private String computeMyHost(Map<String, Object> req) {
// 確認是http還是https
String scheme = (String)req.get("scheme");
// 確認埠號
String serverPort = (String)req.get("server-port");
// /contentdemo是nginx.conf中設定的一個location,您可以根據自己的業務情況來決定返回值
String myhost = scheme + "://127.0.0.1:" + serverPort + "/contentdemo";
NginxClojureRT.log.info("pass address [" + myhost + "]");
return myhost;
}
}
# 1. 定義變數,用於儲存路徑
set $myhost "";
location /myproxy {
# 指定access handler的型別是java
access_handler_type 'java';
# 指定access handler的執行類類
access_handler_name 'com.bolingcavalry.handlerdemo.BasicAuthHandler';
rewrite_handler_type 'java';
# 2. java程式碼中為變數賦值
rewrite_handler_name 'com.bolingcavalry.handlerdemo.MyRewriteProxyPassHandler';
# 3. 用變數的值作為地址進行跳轉
proxy_pass $myhost;
}
package com.bolingcavalry.handlerdemo;
import nginx.clojure.java.ArrayMap;
import nginx.clojure.java.NginxJavaRingHandler;
import javax.xml.bind.DatatypeConverter;
import java.util.Map;
import static nginx.clojure.MiniConstants.DEFAULT_ENCODING;
import static nginx.clojure.MiniConstants.HEADERS;
import static nginx.clojure.java.Constants.PHASE_DONE;
public class BasicAuthHandler implements NginxJavaRingHandler {
@Override
public Object[] invoke(Map<String, Object> request) {
// 從header中獲取authorization欄位
String auth = (String) ((Map)request.get(HEADERS)).get("authorization");
// 如果header中沒有authorization,就返回401錯誤,並帶上body
if (auth == null) {
return new Object[] { 401, ArrayMap.create("www-authenticate", "Basic realm=\"Secure Area\""),
"<HTML><BODY><H1>401 Unauthorized.</H1></BODY></HTML>" };
}
// authorization應該是 : Basic xfeep:hello!,所以這裡先將"Basic "去掉,然後再用":"分割
String[] up = auth.substring("Basic ".length()).split(":");
// 只是為了演示,所以賬號和密碼的檢查邏輯在程式碼中是寫死的,
// 如果賬號等於"xfeep",並且密碼等於"hello!",就返回PHASE_DONE,這樣nginx-clojure就會繼續執行後面的content handler
if (up[0].equals("xfeep") && up[1].equals("hello!")) {
return PHASE_DONE;
}
// 如果賬號密碼校驗不過,就返回401,body內容是提示賬號密碼不過
return new Object[] { 401, ArrayMap.create("www-authenticate", "Basic realm=\"Secure Area\""),
"<HTML><BODY><H1>401 Unauthorized BAD USER & PASSWORD.</H1></BODY></HTML>" };
}
}
location /contentdemo {
# 第一個自定義屬性
content_handler_property foo.name 'foo.value';
# 第二個自定義屬性
content_handler_property bar.name 'bar.value';
content_handler_name 'com.bolingcavalry.handlerdemo.MyContentHandler';
# log handler型別是java
log_handler_type java;
# log handler的執行類
log_handler_name 'com.bolingcavalry.handlerdemo.MyLogHandler';
# 自定義屬性,在MyLogHandler中作為是否列印User Agent的開關
log_handler_property log.user.agent on;
# 自定義屬性,在MyLogHandler中作為紀錄檔目錄
log_handler_property log.file.path logs/contentdemo.log;
}
package com.bolingcavalry.handlerdemo;
import nginx.clojure.Configurable;
import nginx.clojure.NginxClojureRT;
import nginx.clojure.java.NginxJavaRequest;
import nginx.clojure.java.NginxJavaRingHandler;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
public class MyLogHandler implements NginxJavaRingHandler, Configurable {
/**
* 是否將User Agent列印在紀錄檔中
*/
private boolean logUserAgent;
/**
* 紀錄檔檔案路徑
*/
private String filePath;
@Override
public Object[] invoke(Map<String, Object> request) throws IOException {
File file = new File(filePath);
NginxJavaRequest r = (NginxJavaRequest) request;
try (FileOutputStream out = new FileOutputStream(file, true)) {
String msg = String.format("%s - %s [%s] \"%s\" %s \"%s\" %s %s\n",
r.getVariable("remote_addr"),
r.getVariable("remote_user", "x"),
r.getVariable("time_local"),
r.getVariable("request"),
r.getVariable("status"),
r.getVariable("body_bytes_sent"),
r.getVariable("http_referer", "x"),
logUserAgent ? r.getVariable("http_user_agent") : "-");
out.write(msg.getBytes("utf8"));
}
return null;
}
@Override
public void config(Map<String, String> properties) {
logUserAgent = "on".equalsIgnoreCase(properties.get("log.user.agent"));
filePath = properties.get("log.file.path");
NginxClojureRT.log.info("MyLogHandler, logUserAgent [" + logUserAgent + "], filePath [" + filePath + "]");
}
// 下面這段程式碼來自官方demo,實測發現這段程式碼在列印紀錄檔的邏輯中並未發揮作用,
// 不論是否刪除,紀錄檔輸出的內容都是相同的
/*
@Override
public String[] variablesNeedPrefetch() {
return new String[] { "remote_addr", "remote_user", "time_local", "request", "status", "body_bytes_sent",
"http_referer", "http_user_agent" };
}
*/
}
2022-02-08 08:59:22[info][69035][main]MyLogHandler, logUserAgent [true], filePath [logs/contentdemo.log]
再用postman請求/contentdemo試試,如下圖,首先確保響應和之前一致,證明log handler不影響主業務:
去logs目錄下檢視,發現新增了contentdemo.log檔案,內容如下,postman自帶的header引數已經被成功獲取並列印在紀錄檔中了:
127.0.0.1 - x [08/Feb/2022:09:45:36 +0800] "GET /contentdemo HTTP/1.1" 200 "80" x PostmanRuntime/7.29.0