抛出异常的处理
parent
2709171839
commit
3bea7ab015
|
|
@ -20,12 +20,16 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class OmsClient {
|
public class OmsClient {
|
||||||
|
|
||||||
private static final Set<String> SUCCESS_CODES = Set.of("0", "200", "success", "SUCCESS");
|
private static final Set<String> SUCCESS_CODES = Set.of("0", "200", "success", "SUCCESS");
|
||||||
|
private static final Pattern HTML_TITLE_PATTERN = Pattern.compile("<title[^>]*>(.*?)</title>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
|
||||||
|
private static final Pattern HTML_HEADING_PATTERN = Pattern.compile("<h[1-6][^>]*>(.*?)</h[1-6]>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
|
||||||
private static final List<String> PROJECT_CODE_FIELDS = List.of(
|
private static final List<String> PROJECT_CODE_FIELDS = List.of(
|
||||||
"project_code",
|
"project_code",
|
||||||
"projectCode",
|
"projectCode",
|
||||||
|
|
@ -207,16 +211,22 @@ public class OmsClient {
|
||||||
private JsonNode sendRequest(HttpRequest request) {
|
private JsonNode sendRequest(HttpRequest request) {
|
||||||
try {
|
try {
|
||||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||||
|
String responseBody = response.body();
|
||||||
|
String contentType = response.headers().firstValue("Content-Type").orElse(null);
|
||||||
if (response.statusCode() < 200 || response.statusCode() >= 300) {
|
if (response.statusCode() < 200 || response.statusCode() >= 300) {
|
||||||
throw new BusinessException("OMS接口调用失败,HTTP状态码: " + response.statusCode() + ",响应: " + trimBody(response.body()));
|
throw new BusinessException(resolveOmsResponseMessage(response.statusCode(), responseBody, contentType));
|
||||||
}
|
}
|
||||||
JsonNode root = objectMapper.readTree(response.body());
|
if (!looksLikeJson(contentType, responseBody)) {
|
||||||
|
throw new BusinessException(resolveOmsResponseMessage(response.statusCode(), responseBody, contentType));
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode root = objectMapper.readTree(responseBody);
|
||||||
String code = normalizeText(root.path("code").asText(null));
|
String code = normalizeText(root.path("code").asText(null));
|
||||||
if (!isSuccessCode(code)) {
|
if (!isSuccessCode(code)) {
|
||||||
String message = firstNonBlank(
|
String message = firstNonBlank(
|
||||||
normalizeText(root.path("msg").asText(null)),
|
normalizeText(root.path("msg").asText(null)),
|
||||||
normalizeText(root.path("message").asText(null)),
|
normalizeText(root.path("message").asText(null)),
|
||||||
trimBody(response.body()),
|
trimBody(responseBody),
|
||||||
"OMS接口调用失败");
|
"OMS接口调用失败");
|
||||||
throw new BusinessException(message);
|
throw new BusinessException(message);
|
||||||
}
|
}
|
||||||
|
|
@ -229,6 +239,74 @@ public class OmsClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean looksLikeJson(String contentType, String body) {
|
||||||
|
String normalizedContentType = normalizeText(contentType);
|
||||||
|
if (normalizedContentType != null) {
|
||||||
|
String lowerCaseContentType = normalizedContentType.toLowerCase(Locale.ROOT);
|
||||||
|
if (lowerCaseContentType.contains("application/json") || lowerCaseContentType.contains("+json")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String normalizedBody = normalizeText(body);
|
||||||
|
return normalizedBody != null && (normalizedBody.startsWith("{") || normalizedBody.startsWith("["));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveOmsResponseMessage(int statusCode, String body, String contentType) {
|
||||||
|
String visibleMessage = firstNonBlank(
|
||||||
|
extractHtmlErrorMessage(body),
|
||||||
|
trimBody(body));
|
||||||
|
if (!isBlank(visibleMessage)) {
|
||||||
|
return visibleMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
String normalizedContentType = normalizeText(contentType);
|
||||||
|
if (!isBlank(normalizedContentType)) {
|
||||||
|
return "OMS接口调用失败,HTTP状态码: " + statusCode + ",响应类型: " + normalizedContentType;
|
||||||
|
}
|
||||||
|
return "OMS接口调用失败,HTTP状态码: " + statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractHtmlErrorMessage(String body) {
|
||||||
|
String normalizedBody = normalizeText(body);
|
||||||
|
if (normalizedBody == null || !normalizedBody.startsWith("<")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String heading = extractHtmlFragment(normalizedBody, HTML_HEADING_PATTERN);
|
||||||
|
String title = extractHtmlFragment(normalizedBody, HTML_TITLE_PATTERN);
|
||||||
|
String textOnly = normalizeText(
|
||||||
|
normalizedBody
|
||||||
|
.replaceAll("(?is)<script[^>]*>.*?</script>", " ")
|
||||||
|
.replaceAll("(?is)<style[^>]*>.*?</style>", " ")
|
||||||
|
.replaceAll("(?is)<[^>]+>", " ")
|
||||||
|
.replace(" ", " ")
|
||||||
|
.replaceAll("\\s+", " "));
|
||||||
|
|
||||||
|
if (!isBlank(heading)) {
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
if (!isBlank(textOnly)) {
|
||||||
|
if (!isBlank(title) && !textOnly.startsWith(title)) {
|
||||||
|
return (title + " " + textOnly).trim();
|
||||||
|
}
|
||||||
|
return textOnly;
|
||||||
|
}
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractHtmlFragment(String body, Pattern pattern) {
|
||||||
|
Matcher matcher = pattern.matcher(body);
|
||||||
|
if (!matcher.find()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String content = matcher.group(1);
|
||||||
|
if (content == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return normalizeText(content.replaceAll("(?is)<[^>]+>", " ").replaceAll("\\s+", " "));
|
||||||
|
}
|
||||||
|
|
||||||
private URI buildUri(String path, Map<String, String> queryParams) {
|
private URI buildUri(String path, Map<String, String> queryParams) {
|
||||||
validateConfigured();
|
validateConfigured();
|
||||||
String baseUrl = normalizeBaseUrl(omsProperties.getBaseUrl());
|
String baseUrl = normalizeBaseUrl(omsProperties.getBaseUrl());
|
||||||
|
|
|
||||||
|
|
@ -650,6 +650,64 @@ function handleUnauthorizedResponse() {
|
||||||
window.location.href = `${LOGIN_PATH}?timeout=1`;
|
window.location.href = `${LOGIN_PATH}?timeout=1`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tryParseApiBody<T>(rawText: string, contentType?: string | null): (ApiEnvelope<T> & ApiErrorBody) | null {
|
||||||
|
const normalizedText = rawText.trim();
|
||||||
|
const normalizedContentType = (contentType || "").toLowerCase();
|
||||||
|
const looksLikeJson = normalizedContentType.includes("application/json")
|
||||||
|
|| normalizedContentType.includes("+json")
|
||||||
|
|| normalizedText.startsWith("{")
|
||||||
|
|| normalizedText.startsWith("[");
|
||||||
|
|
||||||
|
if (!normalizedText || !looksLikeJson) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(normalizedText) as ApiEnvelope<T> & ApiErrorBody;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractHtmlErrorMessage(rawText: string) {
|
||||||
|
const titleMatch = rawText.match(/<title[^>]*>([^<]+)<\/title>/i);
|
||||||
|
if (titleMatch?.[1]?.trim()) {
|
||||||
|
return titleMatch[1].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
const headingMatch = rawText.match(/<h[1-6][^>]*>([^<]+)<\/h[1-6]>/i);
|
||||||
|
if (headingMatch?.[1]?.trim()) {
|
||||||
|
return headingMatch[1].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
const textOnly = rawText
|
||||||
|
.replace(/<script[\s\S]*?<\/script>/gi, " ")
|
||||||
|
.replace(/<style[\s\S]*?<\/style>/gi, " ")
|
||||||
|
.replace(/<[^>]+>/g, " ")
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
return textOnly || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildApiErrorMessage(rawText: string, status: number, body?: ApiErrorBody | null) {
|
||||||
|
const directMessage = body?.msg?.trim() || body?.message?.trim();
|
||||||
|
if (directMessage) {
|
||||||
|
return directMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedText = rawText.trim();
|
||||||
|
if (!normalizedText) {
|
||||||
|
return `请求失败(${status})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedText.startsWith("<")) {
|
||||||
|
return extractHtmlErrorMessage(normalizedText) || `请求失败(${status})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedText.length > 300 ? normalizedText.slice(0, 300) : normalizedText;
|
||||||
|
}
|
||||||
|
|
||||||
async function request<T>(input: string, init?: RequestInit, withAuth = false): Promise<T> {
|
async function request<T>(input: string, init?: RequestInit, withAuth = false): Promise<T> {
|
||||||
const headers = new Headers(init?.headers);
|
const headers = new Headers(init?.headers);
|
||||||
if (!headers.has("Content-Type") && init?.body && !(init.body instanceof FormData)) {
|
if (!headers.has("Content-Type") && init?.body && !(init.body instanceof FormData)) {
|
||||||
|
|
@ -670,25 +728,19 @@ async function request<T>(input: string, init?: RequestInit, withAuth = false):
|
||||||
throw new Error("登录已失效,请重新登录");
|
throw new Error("登录已失效,请重新登录");
|
||||||
}
|
}
|
||||||
|
|
||||||
let body: (ApiEnvelope<T> & ApiErrorBody) | null = null;
|
const rawText = await response.text();
|
||||||
try {
|
const body = tryParseApiBody<T>(rawText, response.headers.get("content-type"));
|
||||||
body = (await response.json()) as ApiEnvelope<T> & ApiErrorBody;
|
|
||||||
} catch {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`请求失败(${response.status})`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(body?.msg || body?.message || `请求失败(${response.status})`);
|
throw new Error(buildApiErrorMessage(rawText, response.status, body));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!body) {
|
if (!body) {
|
||||||
throw new Error("接口返回为空");
|
throw new Error(rawText.trim() ? buildApiErrorMessage(rawText, response.status, null) : "接口返回为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body.code !== "0") {
|
if (body.code !== "0") {
|
||||||
throw new Error(body.msg || "请求失败");
|
throw new Error(buildApiErrorMessage(rawText, response.status, body));
|
||||||
}
|
}
|
||||||
|
|
||||||
return body.data;
|
return body.data;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue