Skip to content

Commit 67c5489

Browse files
committed
feat: enhance error handling and proxy settings management with user-friendly messages
1 parent 00c75de commit 67c5489

17 files changed

Lines changed: 1931 additions & 250 deletions

File tree

easy-postman-app/src/main/java/com/laker/postman/panel/collections/right/request/HttpRequestExecutionHelper.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.laker.postman.model.script.TestResult;
88
import com.laker.postman.panel.collections.right.request.sub.ResponsePanel;
99
import com.laker.postman.panel.sidebar.ConsolePanel;
10+
import com.laker.postman.service.http.NetworkErrorMessageResolver;
1011
import com.laker.postman.service.http.HttpUtil;
1112
import com.laker.postman.service.http.RedirectHandler;
1213
import com.laker.postman.service.http.sse.SseResEventListener;
@@ -147,9 +148,10 @@ public void onFailure(String errorMsg, HttpResponse response) {
147148
log.warn("Request interrupted: {} {} - {}", req.method, req.url, ex.getMessage());
148149
} catch (Exception ex) {
149150
log.error("Error executing HTTP request: {} {} - {}", req.method, req.url, ex.getMessage(), ex);
150-
ConsolePanel.appendLog("[Error] " + ex.getMessage(), ConsolePanel.LogType.ERROR);
151+
String userFriendlyMessage = NetworkErrorMessageResolver.toUserFriendlyMessage(ex);
152+
ConsolePanel.appendLog("[Error] " + userFriendlyMessage, ConsolePanel.LogType.ERROR);
151153
if (!disposedSupplier.getAsBoolean()) {
152-
NotificationUtil.showError(ex.getMessage());
154+
NotificationUtil.showError(userFriendlyMessage);
153155
}
154156
}
155157
return null;

easy-postman-app/src/main/java/com/laker/postman/panel/collections/right/request/SseRequestExecutionHelper.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.laker.postman.model.script.TestResult;
77
import com.laker.postman.panel.collections.right.request.sub.ResponsePanel;
88
import com.laker.postman.service.http.HttpSingleRequestExecutor;
9+
import com.laker.postman.service.http.NetworkErrorMessageResolver;
910
import com.laker.postman.service.http.sse.SseEventListener;
1011
import com.laker.postman.service.http.sse.SseUiCallback;
1112
import com.laker.postman.service.js.ScriptExecutionPipeline;
@@ -146,12 +147,13 @@ public void onFailure(String errorMsg, HttpResponse r) {
146147
responsePanel.setResponseTabButtonsEnable(true);
147148
} catch (Exception ex) {
148149
log.error("Error executing SSE request: {} - {}", req.url, ex.getMessage(), ex);
150+
String userFriendlyMessage = NetworkErrorMessageResolver.toUserFriendlyMessage(ex);
149151
SwingUtilities.invokeLater(() -> {
150152
if (disposedSupplier.getAsBoolean()) {
151153
return;
152154
}
153155
responsePanel.setStatus(0);
154-
NotificationUtil.showError(I18nUtil.getMessage(MessageKeys.SSE_ERROR, ex.getMessage()));
156+
NotificationUtil.showError(I18nUtil.getMessage(MessageKeys.SSE_ERROR, userFriendlyMessage));
155157
});
156158
}
157159
return null;

easy-postman-app/src/main/java/com/laker/postman/panel/history/HistoryPanel.java

Lines changed: 1466 additions & 208 deletions
Large diffs are not rendered by default.

easy-postman-app/src/main/java/com/laker/postman/panel/sidebar/SidebarTabPanel.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,15 @@ public void refreshSidebarConfiguration() {
10271027
}
10281028
}
10291029

1030+
public boolean showTab(SidebarTab sidebarTab) {
1031+
int tabIndex = getTabIndex(sidebarTab);
1032+
if (tabIndex < 0 || tabIndex >= tabbedPane.getTabCount()) {
1033+
return false;
1034+
}
1035+
tabbedPane.setSelectedIndex(tabIndex);
1036+
return true;
1037+
}
1038+
10301039
/**
10311040
* 重新创建标签页以应用新的展开/收起状态
10321041
*/

easy-postman-app/src/main/java/com/laker/postman/panel/topmenu/setting/ProxySettingsPanelModern.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ protected void buildContent(JPanel contentPanel) {
9090
proxySection.add(proxyHostRow);
9191
proxySection.add(createVerticalSpace(FIELD_SPACING));
9292
proxyPortField = new JTextField(10);
93-
proxyPortField.setText(String.valueOf(SettingManager.getProxyPort()));
93+
proxyPortField.setText(SettingManager.getProxyPortText());
9494
JPanel proxyPortRow = createFieldRow(
9595
I18nUtil.getMessage(MessageKeys.SETTINGS_PROXY_PORT),
9696
I18nUtil.getMessage(MessageKeys.SETTINGS_PROXY_PORT_TOOLTIP),

easy-postman-app/src/main/java/com/laker/postman/service/HistoryPersistenceService.java

Lines changed: 205 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
import com.laker.postman.common.constants.ConfigPathConstants;
77
import com.laker.postman.ioc.Component;
88
import com.laker.postman.ioc.PostConstruct;
9+
import com.laker.postman.model.HttpFormData;
10+
import com.laker.postman.model.HttpFormUrlencoded;
11+
import com.laker.postman.model.HttpHeader;
12+
import com.laker.postman.model.HttpParam;
913
import com.laker.postman.model.HttpResponse;
1014
import com.laker.postman.model.PreparedRequest;
1115
import com.laker.postman.model.RequestHistoryItem;
@@ -21,6 +25,7 @@
2125
import java.util.ArrayList;
2226
import java.util.LinkedHashMap;
2327
import java.util.List;
28+
import java.util.Objects;
2429
import java.util.concurrent.CopyOnWriteArrayList;
2530

2631
/**
@@ -60,7 +65,7 @@ private void ensureHistoryDirExists() {
6065
/**
6166
* 添加历史记录
6267
*/
63-
public void addHistory(PreparedRequest request, HttpResponse response, long requestTime) {
68+
public RequestHistoryItem addHistory(PreparedRequest request, HttpResponse response, long requestTime) {
6469
RequestHistoryItem item = new RequestHistoryItem(request, response, requestTime);
6570
historyItems.add(0, item); // 添加到开头
6671

@@ -72,6 +77,7 @@ public void addHistory(PreparedRequest request, HttpResponse response, long requ
7277

7378
// 异步保存
7479
saveHistoryAsync();
80+
return item;
7581
}
7682

7783
/**
@@ -89,6 +95,18 @@ public void clearHistory() {
8995
saveHistoryAsync();
9096
}
9197

98+
/**
99+
* 删除单条历史记录
100+
*/
101+
public void removeHistory(RequestHistoryItem item) {
102+
if (item == null) {
103+
return;
104+
}
105+
if (historyItems.remove(item)) {
106+
saveHistoryAsync();
107+
}
108+
}
109+
92110
/**
93111
* 同步保存历史记录
94112
*/
@@ -106,7 +124,7 @@ public void saveHistory() {
106124
}
107125

108126
// 写入文件
109-
String jsonString = JSONUtil.toJsonPrettyStr(jsonArray);
127+
String jsonString = JSONUtil.toJsonStr(jsonArray);
110128
Files.writeString(Paths.get(HISTORY_FILE), jsonString, StandardCharsets.UTF_8);
111129
} catch (IOException e) {
112130
log.error("Failed to save history: {}", e.getMessage());
@@ -253,19 +271,31 @@ private JSONObject convertToJson(RequestHistoryItem item) {
253271
JSONObject requestJson = new JSONObject();
254272
requestJson.set("method", item.request.method);
255273
requestJson.set("url", item.request.url);
256-
// 请求体 - 优先保存实际发送的okHttpRequestBody,但限制大小
274+
// 请求体 - 保留完整内容,历史记录支持重新打开为可编辑请求时需要精确还原
257275
String requestBody = "";
258276
if (item.request.okHttpRequestBody != null && !item.request.okHttpRequestBody.isEmpty()) {
259277
requestBody = item.request.okHttpRequestBody;
260278
} else if (item.request.body != null) {
261279
requestBody = item.request.body;
262280
}
263-
// 限制请求体大小
264281
requestBody = truncateBody(requestBody, MAX_REQUEST_BODY_SIZE);
265282
requestJson.set("body", requestBody);
283+
if (!Objects.equals(item.request.body, requestBody)) {
284+
requestJson.set("originalBody", truncateBody(item.request.body, MAX_REQUEST_BODY_SIZE));
285+
}
286+
requestJson.set("bodyType", item.request.bodyType);
266287
requestJson.set("id", item.request.id);
267288
requestJson.set("followRedirects", item.request.followRedirects);
268289
requestJson.set("isMultipart", item.request.isMultipart);
290+
requestJson.set("cookieJarEnabled", item.request.cookieJarEnabled);
291+
requestJson.set("sslVerificationEnabled", item.request.sslVerificationEnabled);
292+
requestJson.set("httpVersion", item.request.httpVersion);
293+
requestJson.set("requestTimeoutMs", item.request.requestTimeoutMs);
294+
requestJson.set("collectBasicInfo", item.request.collectBasicInfo);
295+
requestJson.set("collectEventInfo", item.request.collectEventInfo);
296+
requestJson.set("enableNetworkLog", item.request.enableNetworkLog);
297+
requestJson.set("prescript", item.request.prescript);
298+
requestJson.set("postscript", item.request.postscript);
269299

270300
// 请求头 - 优先保存实际发送的okHttpHeaders
271301
JSONObject requestHeaders = new JSONObject();
@@ -278,6 +308,10 @@ private JSONObject convertToJson(RequestHistoryItem item) {
278308
}
279309
}
280310
requestJson.set("headers", requestHeaders);
311+
requestJson.set("headersList", convertHeadersListToJson(item.request.headersList));
312+
requestJson.set("paramsList", convertParamsListToJson(item.request.paramsList));
313+
requestJson.set("formDataList", convertFormDataListToJson(item.request.formDataList));
314+
requestJson.set("urlencodedList", convertUrlencodedListToJson(item.request.urlencodedList));
281315

282316

283317
jsonItem.set("request", requestJson);
@@ -364,12 +398,25 @@ private RequestHistoryItem convertFromJson(JSONObject jsonItem) {
364398
JSONObject requestJson = jsonItem.getJSONObject("request");
365399
request.method = requestJson.getStr("method");
366400
request.url = requestJson.getStr("url");
367-
request.body = requestJson.getStr("body");
368-
// 同时设置 okHttpRequestBody 供 HttpHtmlRenderer 使用
369-
request.okHttpRequestBody = request.body;
401+
request.body = requestJson.getStr("originalBody", requestJson.getStr("body"));
402+
request.bodyType = requestJson.getStr("bodyType");
403+
request.okHttpRequestBody = requestJson.getStr("body");
370404
request.id = requestJson.getStr("id");
371405
request.followRedirects = requestJson.getBool("followRedirects", true);
372406
request.isMultipart = requestJson.getBool("isMultipart", false);
407+
request.cookieJarEnabled = requestJson.getBool("cookieJarEnabled", true);
408+
request.sslVerificationEnabled = requestJson.getBool("sslVerificationEnabled", true);
409+
request.httpVersion = requestJson.getStr("httpVersion");
410+
request.requestTimeoutMs = requestJson.getInt("requestTimeoutMs", 0);
411+
request.collectBasicInfo = requestJson.getBool("collectBasicInfo", true);
412+
request.collectEventInfo = requestJson.getBool("collectEventInfo", true);
413+
request.enableNetworkLog = requestJson.getBool("enableNetworkLog", false);
414+
request.prescript = requestJson.getStr("prescript");
415+
request.postscript = requestJson.getStr("postscript");
416+
request.headersList = convertHeadersListFromJson(requestJson.getJSONArray("headersList"));
417+
request.paramsList = convertParamsListFromJson(requestJson.getJSONArray("paramsList"));
418+
request.formDataList = convertFormDataListFromJson(requestJson.getJSONArray("formDataList"));
419+
request.urlencodedList = convertUrlencodedListFromJson(requestJson.getJSONArray("urlencodedList"));
373420

374421
// 重建请求头 - 构建 okHttpHeaders 供 HttpHtmlRenderer 使用
375422
JSONObject requestHeaders = requestJson.getJSONObject("headers");
@@ -383,6 +430,19 @@ private RequestHistoryItem convertFromJson(JSONObject jsonItem) {
383430
}
384431
}
385432
request.okHttpHeaders = headersBuilder.build();
433+
} else if (request.headersList != null && !request.headersList.isEmpty()) {
434+
okhttp3.Headers.Builder headersBuilder = new okhttp3.Headers.Builder();
435+
for (HttpHeader header : request.headersList) {
436+
if (header == null || !header.isEnabled() || header.getKey() == null || header.getKey().isBlank()) {
437+
continue;
438+
}
439+
try {
440+
headersBuilder.add(header.getKey(), header.getValue());
441+
} catch (Exception e) {
442+
// 忽略无效的头信息
443+
}
444+
}
445+
request.okHttpHeaders = headersBuilder.build();
386446
}
387447

388448
// 重建 HttpResponse
@@ -469,4 +529,142 @@ private RequestHistoryItem convertFromJson(JSONObject jsonItem) {
469529

470530
return new RequestHistoryItem(request, response, requestTime);
471531
}
532+
533+
private JSONArray convertHeadersListToJson(List<HttpHeader> headersList) {
534+
JSONArray jsonArray = new JSONArray();
535+
if (headersList == null) {
536+
return jsonArray;
537+
}
538+
for (HttpHeader header : headersList) {
539+
if (header == null) {
540+
continue;
541+
}
542+
JSONObject jsonObject = new JSONObject();
543+
jsonObject.set("enabled", header.isEnabled());
544+
jsonObject.set("key", header.getKey());
545+
jsonObject.set("value", header.getValue());
546+
jsonArray.add(jsonObject);
547+
}
548+
return jsonArray;
549+
}
550+
551+
private List<HttpHeader> convertHeadersListFromJson(JSONArray jsonArray) {
552+
List<HttpHeader> headersList = new ArrayList<>();
553+
if (jsonArray == null) {
554+
return headersList;
555+
}
556+
for (int i = 0; i < jsonArray.size(); i++) {
557+
JSONObject jsonObject = jsonArray.getJSONObject(i);
558+
headersList.add(new HttpHeader(
559+
jsonObject.getBool("enabled", true),
560+
jsonObject.getStr("key", ""),
561+
jsonObject.getStr("value", "")
562+
));
563+
}
564+
return headersList;
565+
}
566+
567+
private JSONArray convertParamsListToJson(List<HttpParam> paramsList) {
568+
JSONArray jsonArray = new JSONArray();
569+
if (paramsList == null) {
570+
return jsonArray;
571+
}
572+
for (HttpParam param : paramsList) {
573+
if (param == null) {
574+
continue;
575+
}
576+
JSONObject jsonObject = new JSONObject();
577+
jsonObject.set("enabled", param.isEnabled());
578+
jsonObject.set("key", param.getKey());
579+
jsonObject.set("value", param.getValue());
580+
jsonArray.add(jsonObject);
581+
}
582+
return jsonArray;
583+
}
584+
585+
private List<HttpParam> convertParamsListFromJson(JSONArray jsonArray) {
586+
List<HttpParam> paramsList = new ArrayList<>();
587+
if (jsonArray == null) {
588+
return paramsList;
589+
}
590+
for (int i = 0; i < jsonArray.size(); i++) {
591+
JSONObject jsonObject = jsonArray.getJSONObject(i);
592+
paramsList.add(new HttpParam(
593+
jsonObject.getBool("enabled", true),
594+
jsonObject.getStr("key", ""),
595+
jsonObject.getStr("value", "")
596+
));
597+
}
598+
return paramsList;
599+
}
600+
601+
private JSONArray convertFormDataListToJson(List<HttpFormData> formDataList) {
602+
JSONArray jsonArray = new JSONArray();
603+
if (formDataList == null) {
604+
return jsonArray;
605+
}
606+
for (HttpFormData formData : formDataList) {
607+
if (formData == null) {
608+
continue;
609+
}
610+
JSONObject jsonObject = new JSONObject();
611+
jsonObject.set("enabled", formData.isEnabled());
612+
jsonObject.set("key", formData.getKey());
613+
jsonObject.set("type", formData.getType());
614+
jsonObject.set("value", formData.getValue());
615+
jsonArray.add(jsonObject);
616+
}
617+
return jsonArray;
618+
}
619+
620+
private List<HttpFormData> convertFormDataListFromJson(JSONArray jsonArray) {
621+
List<HttpFormData> formDataList = new ArrayList<>();
622+
if (jsonArray == null) {
623+
return formDataList;
624+
}
625+
for (int i = 0; i < jsonArray.size(); i++) {
626+
JSONObject jsonObject = jsonArray.getJSONObject(i);
627+
formDataList.add(new HttpFormData(
628+
jsonObject.getBool("enabled", true),
629+
jsonObject.getStr("key", ""),
630+
jsonObject.getStr("type", HttpFormData.TYPE_TEXT),
631+
jsonObject.getStr("value", "")
632+
));
633+
}
634+
return formDataList;
635+
}
636+
637+
private JSONArray convertUrlencodedListToJson(List<HttpFormUrlencoded> urlencodedList) {
638+
JSONArray jsonArray = new JSONArray();
639+
if (urlencodedList == null) {
640+
return jsonArray;
641+
}
642+
for (HttpFormUrlencoded urlencoded : urlencodedList) {
643+
if (urlencoded == null) {
644+
continue;
645+
}
646+
JSONObject jsonObject = new JSONObject();
647+
jsonObject.set("enabled", urlencoded.isEnabled());
648+
jsonObject.set("key", urlencoded.getKey());
649+
jsonObject.set("value", urlencoded.getValue());
650+
jsonArray.add(jsonObject);
651+
}
652+
return jsonArray;
653+
}
654+
655+
private List<HttpFormUrlencoded> convertUrlencodedListFromJson(JSONArray jsonArray) {
656+
List<HttpFormUrlencoded> urlencodedList = new ArrayList<>();
657+
if (jsonArray == null) {
658+
return urlencodedList;
659+
}
660+
for (int i = 0; i < jsonArray.size(); i++) {
661+
JSONObject jsonObject = jsonArray.getJSONObject(i);
662+
urlencodedList.add(new HttpFormUrlencoded(
663+
jsonObject.getBool("enabled", true),
664+
jsonObject.getStr("key", ""),
665+
jsonObject.getStr("value", "")
666+
));
667+
}
668+
return urlencodedList;
669+
}
472670
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.laker.postman.service.http;
2+
3+
import com.laker.postman.util.I18nUtil;
4+
import com.laker.postman.util.MessageKeys;
5+
6+
public final class NetworkErrorMessageResolver {
7+
private static final String SOCKS_MALFORMED_REPLY = "Malformed reply from SOCKS server";
8+
9+
private NetworkErrorMessageResolver() {
10+
}
11+
12+
public static String toUserFriendlyMessage(Throwable throwable) {
13+
if (throwable == null) {
14+
return "";
15+
}
16+
return toUserFriendlyMessage(throwable.getMessage());
17+
}
18+
19+
public static String toUserFriendlyMessage(String rawMessage) {
20+
String normalized = rawMessage == null ? "" : rawMessage.trim();
21+
if (normalized.contains(SOCKS_MALFORMED_REPLY)) {
22+
return I18nUtil.getMessage(MessageKeys.NETWORK_ERROR_PROXY_SOCKS_MALFORMED, normalized);
23+
}
24+
return normalized;
25+
}
26+
}

0 commit comments

Comments
 (0)