Ver Fonte

sagger 知识库api

1811872455@163.com há 3 semanas atrás
pai
commit
8543eac98f

+ 1 - 3
java/storlead-api/src/main/resources/application-dev.yml

@@ -6,9 +6,7 @@ server:
   servlet:
     context-path: /router/rest
   compression:
-    enabled: true
-    min-response-size: 1024
-    mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/*
+    enabled: false
 
 management:
   endpoints:

+ 9 - 1
java/storlead-api/src/main/resources/application.yml

@@ -1,10 +1,18 @@
 spring:
   profiles:
     active: dev
-
+  servlet:
+    multipart:
+      max-file-size: 50MB
+      max-request-size: 200MB
+  tomcat:
+    max-upload-size: 200MB
   mvc:
     pathmatch:
       matching-strategy: ant_path_matcher
+    contentnegotiation:
+      favor-path-extension: false
+      favor-parameter: false
   #  security:
   #    jwt:
   #      # 自定义 secretKey

+ 1 - 1
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/SystemUtils.java

@@ -16,7 +16,7 @@ public class SystemUtils {
      * */
     public static void printJson(HttpServletResponse response, String json){
         try {
-            response.setContentType("application/json");
+            response.setContentType("application/json;charset=UTF-8");
             response.setCharacterEncoding("UTF-8");
             response.getWriter().print(json);
             response.getWriter().flush();

+ 44 - 11
java/storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/process/ApiResultHandler.java

@@ -3,10 +3,13 @@
 //import com.fasterxml.jackson.core.JsonParser;
 //import com.fasterxml.jackson.databind.DeserializationFeature;
 //import com.fasterxml.jackson.databind.ObjectMapper;
-//import com.storlead.framework.web.message.WebResp;
+//import com.alibaba.fastjson.JSON;
+//import com.alibaba.fastjson.JSONObject;
+//import com.storlead.framework.common.result.Result;
 //import org.apache.commons.lang3.StringUtils;
 //import org.springframework.core.MethodParameter;
 //import org.springframework.http.MediaType;
+//import org.springframework.http.ResponseEntity;
 //import org.springframework.http.converter.HttpMessageConverter;
 //import org.springframework.http.server.ServerHttpRequest;
 //import org.springframework.http.server.ServerHttpResponse;
@@ -36,6 +39,10 @@
 //
 //    @Override
 //    public boolean supports(MethodParameter returnType, Class converterType) {
+//        // 不处理 ResponseEntity 返回值,避免图片/文件流等被包装导致 ClassCastException
+//        if (ResponseEntity.class.isAssignableFrom(returnType.getParameterType())) {
+//            return false;
+//        }
 //        AnnotatedElement element = returnType.getAnnotatedElement();
 //        return Arrays.stream(annos).anyMatch(anno -> anno.isAnnotation() && element.isAnnotationPresent(anno));
 //    }
@@ -64,16 +71,15 @@
 //    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
 //                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
 //                                  ServerHttpResponse response) {
+//        Object data = normalizeResult(body);
+//        data = wrapIfNeed(data, returnType);
+//        data = normalizeResult(data);
 //
-//        if (MediaType.TEXT_EVENT_STREAM.includes(selectedContentType)) {
-//            return body;
-//        }
 //
-//        Object data = wrapIfNeed(body, returnType);
 //        String selectedConverterTypeClassName = selectedConverterType.getName();
 //        // selectedConverterType 对象是  StringHttpMessageConverter时, 表示不能直接返回  WebResp, 要不然会报错      org.springframework.http.converter.StringHttpMessageConverter
 //        if (selectedConverterTypeClassName.indexOf("StringHttpMessageConverter") >= 0) {    // 如果是返回  string 类型的, 就返回 string 类型的
-//            if (data instanceof WebResp) {
+//            if (data instanceof Result) {
 //                ObjectMapper mapper = new ObjectMapper();
 //                mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
 //                mapper.enable(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS);
@@ -93,16 +99,43 @@
 //    }
 //
 //    public static Object wrapIfNeed(Object body, MethodParameter returnType) {
-//        if (body instanceof WebResp) {
+//        // 对于已经是统一返回类型的结果或资源类,直接透传,避免二次包装导致类型转换异常
+//        if (body instanceof Result) {
+//            return normalizeResult(body);
+//        } else if (body instanceof org.springframework.core.io.Resource) {
+//            // 文件预览/下载等场景需要直接返回二进制流
 //            return body;
-//        } else {
+//        } else if (body instanceof org.springframework.http.ResponseEntity) {
+//            // ResponseEntity 如 404、图片预览等,直接透传,避免包装导致序列化异常
+//            org.springframework.http.ResponseEntity<?> resp = (org.springframework.http.ResponseEntity<?>) body;
+//            Object respBody = resp.getBody();
+//            if (respBody == null || respBody instanceof org.springframework.core.io.Resource) {
+//                return body;
+//            }
+//        }
+//        {
 //            if (body != null) {
-//                return new WebResp(body);
+//                return Result.result(body);
 //            } else if(body == null) {
-//                return new WebResp("");
+//                return Result.ok();
 //            }
-//            return new WebResp(getDefaultValue(returnType));
+//            return Result.result(getDefaultValue(returnType));
+//        }
+//    }
+//
+//    /**
+//     * fastjson.JSONObject 若被 Jackson 当作 Bean 序列化会产生乱码;统一转为普通 Map。
+//     */
+//    private static Object normalizeResult(Object body) {
+//        if (!(body instanceof Result)) {
+//            return body;
+//        }
+//        Result<?> result = (Result<?>) body;
+//        Object payload = result.getResult();
+//        if (payload instanceof JSONObject) {
+//            result.setResult(JSON.parseObject(JSON.toJSONString(payload)));
 //        }
+//        return result;
 //    }
 //
 //    public static Object getDefaultValue(MethodParameter returnType) {

+ 26 - 0
java/storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/process/StorleadFastJsonHttpMessageConverter.java

@@ -0,0 +1,26 @@
+package com.storlead.framework.web.process;
+
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+
+import java.io.IOException;
+
+/**
+ * 保证 API 始终输出 UTF-8 文本 JSON。
+ */
+public class StorleadFastJsonHttpMessageConverter extends FastJsonHttpMessageConverter {
+
+    @Override
+    protected void writeInternal(Object object, HttpOutputMessage outputMessage)
+            throws IOException, HttpMessageNotWritableException {
+        if (object instanceof byte[]) {
+            byte[] bytes = (byte[]) object;
+            outputMessage.getHeaders().setContentType(MediaType.APPLICATION_JSON);
+            outputMessage.getBody().write(bytes, 0, bytes.length);
+            return;
+        }
+        super.writeInternal(object, outputMessage);
+    }
+}

+ 52 - 72
java/storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/process/WebMvcConfigurer.java

@@ -4,59 +4,48 @@ import com.alibaba.fastjson.serializer.SerializerFeature;
 import com.alibaba.fastjson.support.config.FastJsonConfig;
 import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
 import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.MediaType;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.util.AntPathMatcher;
-import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.servlet.HandlerInterceptor;
-import org.springframework.web.servlet.config.annotation.*;
+import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
 @Configuration
 public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
 
+    private static final MediaType JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8);
+
     @Autowired
     private ObjectMapper objectMapper;
 
     @Bean
-    public WebMvcConfigurer corsConfigurer() {
-        return new WebMvcConfigurer() {
+    public org.springframework.web.servlet.config.annotation.WebMvcConfigurer corsConfigurer() {
+        return new org.springframework.web.servlet.config.annotation.WebMvcConfigurer() {
             @Override
             public void addCorsMappings(CorsRegistry registry) {
-                registry.addMapping("/**"); //对应的接口
+                registry.addMapping("/**");
             }
         };
     }
 
-    public WebMvcConfigurer() {
-
-    }
-
-    /**
-     * 配置路由匹配模式
-     *
-     * **/
     @Override
     public void configurePathMatch(PathMatchConfigurer configurer) {
-        /***
-         *
-         * setUseSuffixPatternMatch : 设置是否是后缀模式匹配,如“/user”是否匹配/user.*,默认真即匹配;
-         * setUseTrailingSlashMatch : 设置是否自动后缀路径模式匹配,如“/user”是否匹配“/user/”,默认真即匹配;
-         configurer.setUseSuffixPatternMatch(false)
-         .setUseTrailingSlashMatch(true);
-         **/
         configurer.setUseSuffixPatternMatch(false);
         configurer.setUseTrailingSlashMatch(true);
         configurer.setUseRegisteredSuffixPatternMatch(true);
@@ -65,75 +54,66 @@ public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
         configurer.setPathMatcher(pathMatcher);
     }
 
-    @Override
-    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
-        super.configureMessageConverters(converters);
-        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
-        FastJsonConfig fastJsonConfig = new FastJsonConfig();
-        fastJsonConfig.setSerializerFeatures(
-                SerializerFeature.PrettyFormat
-        );
-        fastConverter.setFastJsonConfig(fastJsonConfig);
-
-        converters.add(fastConverter);
-    }
-
-
-    /** 自定义一个MappingJackson2HttpMessageConverter放在所有默认Converter的前面,使其拥有最高优先级 */
+    /**
+     * FastJson 必须优先于 Jackson:Controller 大量返回 Result + fastjson.JSONObject,
+     * 若 Jackson 先处理会按 Bean 序列化 JSONObject 内部字段,客户端看到二进制乱码(:success...)。
+     */
     @Override
     public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
-        ArrayList<HttpMessageConverter<?>> copy = new ArrayList<>(converters.size());
+        List<HttpMessageConverter<?>> others = new ArrayList<>();
         Iterator<HttpMessageConverter<?>> iterator = converters.iterator();
         while (iterator.hasNext()) {
             HttpMessageConverter<?> converter = iterator.next();
-            if (converter instanceof MappingJackson2HttpMessageConverter) {
+            if (converter instanceof MappingJackson2HttpMessageConverter
+                    || converter instanceof FastJsonHttpMessageConverter) {
                 iterator.remove();
             } else {
-                copy.add(converter);
+                others.add(converter);
             }
         }
-        //converters.clear();
+        converters.add(0, createFastJsonHttpMessageConverter());
+        converters.add(1, createMappingJackson2HttpMessageConverter());
+        converters.addAll(others);
+    }
 
-        converters.add(createMappingJackson2HttpMessageConverter());
-        converters.addAll(copy);
+    private StorleadFastJsonHttpMessageConverter createFastJsonHttpMessageConverter() {
+        StorleadFastJsonHttpMessageConverter fastConverter = new StorleadFastJsonHttpMessageConverter();
+        FastJsonConfig fastJsonConfig = new FastJsonConfig();
+        fastJsonConfig.setCharset(StandardCharsets.UTF_8);
+        fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
+        fastJsonConfig.setSerializerFeatures(
+                SerializerFeature.DisableCircularReferenceDetect,
+                SerializerFeature.WriteMapNullValue
+        );
+        fastConverter.setFastJsonConfig(fastJsonConfig);
+        fastConverter.setDefaultCharset(StandardCharsets.UTF_8);
+        fastConverter.setSupportedMediaTypes(Collections.singletonList(JSON_UTF8));
+        return fastConverter;
     }
 
     private MappingJackson2HttpMessageConverter createMappingJackson2HttpMessageConverter() {
-        if (objectMapper == null) {
-            final ObjectMapper objectMapper = new ObjectMapper();
-            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
-            objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
-            this.objectMapper = objectMapper;
+        ObjectMapper mapper = this.objectMapper;
+        if (mapper == null || !(mapper.getFactory() instanceof JsonFactory)) {
+            mapper = new ObjectMapper(new JsonFactory());
+            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+            mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
+            this.objectMapper = mapper;
         }
-        return new MappingJackson2HttpMessageConverter(this.objectMapper);
+        MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter(mapper);
+        jacksonConverter.setDefaultCharset(StandardCharsets.UTF_8);
+        jacksonConverter.setSupportedMediaTypes(Collections.singletonList(JSON_UTF8));
+        return jacksonConverter;
     }
 
-    /**
-     * 配置返回的数据内容 , 如 json,html
-     * **/
     @Override
     public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
-        /* 是否通过请求Url的扩展名来决定media type */
-        /**/
-        configurer.favorPathExtension(true)
-                // 不检查Accept请求头,客户端传过来Accept要求返回的请求头,不做理会
-                .ignoreAcceptHeader(true).parameterName("mediaType")
-                // 请求以.html与 .jsp结尾的会被当成MediaType.TEXT_HTML
-                .mediaType("html", MediaType.TEXT_HTML).mediaType("jsp", MediaType.TEXT_HTML)
-                // 请求以.do与.json结尾的会被当成MediaType.APPLICATION_JSON
-                .mediaType("do", MediaType.APPLICATION_JSON_UTF8).mediaType("json", MediaType.APPLICATION_JSON_UTF8);
-
-        /**
-         configurer.mediaTypes(ImmutableMap.of("do", MediaType.APPLICATION_JSON_UTF8,"json",MediaType.APPLICATION_JSON_UTF8 ));
-         **/
-        // configurer.mediaTypes(ImmutableMap.of("do", MediaType.APPLICATION_JSON_UTF8));
+        configurer.favorPathExtension(false)
+                .favorParameter(false)
+                .ignoreAcceptHeader(false)
+                .defaultContentType(JSON_UTF8);
     }
 
-    /**
-     * 添加拦截器
-     * **/
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
-
     }
 }