├── LICENSE ├── README.md ├── _config.yml ├── demo ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── sapi │ │ │ └── demo │ │ │ ├── DemoApplication.java │ │ │ └── controller │ │ │ ├── CatController.java │ │ │ ├── DogController.java │ │ │ ├── UserController.java │ │ │ └── UserInfoController.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── sapi │ └── demo │ └── DemoApplicationTests.java └── spring.boot.sapi.starter ├── .idea ├── compiler.xml ├── encodings.xml ├── libraries │ ├── Maven__ch_qos_logback_logback_classic_1_2_3.xml │ ├── Maven__ch_qos_logback_logback_core_1_2_3.xml │ ├── Maven__com_fasterxml_classmate_1_3_4.xml │ ├── Maven__com_fasterxml_jackson_core_jackson_annotations_2_9_0.xml │ ├── Maven__com_fasterxml_jackson_core_jackson_core_2_9_6.xml │ ├── Maven__com_fasterxml_jackson_core_jackson_databind_2_9_6.xml │ ├── Maven__com_fasterxml_jackson_datatype_jackson_datatype_jdk8_2_9_6.xml │ ├── Maven__com_fasterxml_jackson_datatype_jackson_datatype_jsr310_2_9_6.xml │ ├── Maven__com_fasterxml_jackson_module_jackson_module_parameter_names_2_9_6.xml │ ├── Maven__javax_annotation_javax_annotation_api_1_3_2.xml │ ├── Maven__javax_validation_validation_api_2_0_1_Final.xml │ ├── Maven__log4j_log4j_1_2_17.xml │ ├── Maven__org_apache_logging_log4j_log4j_api_2_5.xml │ ├── Maven__org_apache_logging_log4j_log4j_core_2_5.xml │ ├── Maven__org_apache_logging_log4j_log4j_to_slf4j_2_10_0.xml │ ├── Maven__org_jboss_logging_jboss_logging_3_3_2_Final.xml │ ├── Maven__org_slf4j_jul_to_slf4j_1_7_25.xml │ ├── Maven__org_slf4j_slf4j_api_1_7_25.xml │ └── Maven__org_yaml_snakeyaml_1_19.xml ├── misc.xml ├── modules.xml └── workspace.xml ├── pom.xml └── src └── main ├── java └── com │ └── github │ └── xiaour │ └── sapi │ ├── annotation │ ├── Sapi.java │ └── SapiGroup.java │ ├── config │ ├── ApiServerAutoConfigure.java │ ├── SapiFactoryAutoConfigure.java │ ├── SapiGroupManager.java │ └── SapiProperties.java │ ├── dto │ ├── ApiField.java │ └── ApiInfo.java │ ├── exception │ └── SimpleApiException.java │ ├── logging │ ├── JakartaCommonsLoggingImpl.java │ ├── Jdk14LoggingImpl.java │ ├── Log.java │ ├── Log4j2Impl.java │ ├── Log4jImpl.java │ ├── LogFactory.java │ ├── NoLoggingImpl.java │ ├── Resources.java │ └── SLF4JImpl.java │ ├── servlet │ ├── ApiJsonServlet.java │ ├── ApiViewServlet.java │ ├── FaqServlet.java │ └── SapiStaticServlet.java │ └── util │ ├── ClassScaner.java │ ├── SapiJsonUtil.java │ └── Utils.java └── resources ├── META-INF ├── additional-spring-configuration-metadata.json └── spring.factories └── support └── http ├── css └── sapi.css ├── faq.html ├── image └── favicon.ico ├── index.html ├── js ├── DataFormater.js ├── bootstrap.min.js ├── jquery.min.js ├── vue-resource.min.js └── vue.min.js └── list.html /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 2 | [![Maven Central](https://img.shields.io/maven-central/v/com.github.xiaour/xiaour.springboot.sapi.starter.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.xiaour%22%20AND%20a:%22xiaour.springboot.sapi.starter%22) 3 | 4 | # spring.boot.sapi.starter 5 | 基于Springboot2.x 的接口管理工具,一个简单的API输出工具。 6 | 7 | 只需简单配置,即可将接口中的所有API接口及参数全部以结构化的方式展示,并提供了接口参数测试页面。 8 | 9 | 10 | ## 快速开始 Quick start 11 | 12 | ### 1.远程仓库配置 13 | 1.1 Maven 14 | ```xml 15 | 16 | com.github.xiaour 17 | xiaour.springboot.sapi.starter 18 | 1.4 19 | 20 | ``` 21 | 1.2 Gradle 22 | 23 | ```gradle 24 | compile 'com.github.xiaour:xiaour.springboot.sapi.starter:1.4' 25 | ``` 26 | 1.3 [更多仓库配置类型请参考 >>>](https://search.maven.org/artifact/com.github.xiaour/xiaour.springboot.sapi.starter/1.4/jar) 27 | 28 | 29 | ### 2.启动类加入注解@Sapi. 30 | controllers属性可以声明多个,如controllers = {"com.example.demo.ctrl","com.example.demo2.ctrl"},controllers的路径对应项目中controller所在的路径。enable是SAPI的开关,如果需要上生产环境只需要enable=false就可以了,这样避免在生产暴露接口。 31 | 32 | ```java 33 | @Sapi(controllers = {"com.example.demo.ctrl"}) 34 | @SpringBootApplication 35 | public class DemoApplication { 36 | 37 | public static void main(String[] args) { 38 | SpringApplication.run(DemoApplication.class, args); 39 | } 40 | } 41 | ``` 42 | ### 3.Controller类加入注解@SapiGroup(title=""). 43 | SapiGroup是用于分组输出的注解,可以在上面加入中文接口注释,该注解可有可无,如不注解,默认输出的是当前类名。 44 | ```java 45 | @SapiGroup(title = "接口分组名称") 46 | @RestController 47 | @RequestMapping("/cat") 48 | public class CatController { 49 | 50 | } 51 | ``` 52 | 53 | ### 4.启动应用控制台将输出SAPI的访问链接,点击链接即可立即开始调试接口。 54 | ```xml 55 | 2018-12-08 21:54:49.328 INFO 1112 --- [ restartedMain] c.g.x.a.config.ApiServerAutoConfigure : SAPI page url:http://127.0.0.1:8080/demo/sapi 56 | ``` 57 | 58 | ![avatar](https://oscimg.oschina.net/oscnet/1a3545fa7abcfff02354740d61ce81daf56.jpg) 59 | 60 | 61 | ## 版本功能 62 | ```text 63 | 当前为1.4版本,实现了输出接口URL和参数,分组输出接口; 64 | 2.0版本将实现添加接口说明、历史版本接口管理 65 | ``` 66 | ------------------------------------------------------------- 67 | ## 更新日志 68 | 69 | 20180621.页面上提供了POST和GET测试。 70 | 71 | 20180628.新增支持文件类型的数据。 72 | 73 | 20180702.新增支持自定义请求Header。 74 | 75 | 20180710.新增了接口分页。 76 | 77 | 20180828.优化了返回结果格式化的问题。 78 | 79 | 20180907.改版了界面列表展示部分,修复了Class扫描不到的情况。 80 | 81 | 20180912.增加了对RequestBody的支持,支持MultipartFile上传文件。 82 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /demo/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaour/spring.boot.sapi.starter/5e9aae7cdc2def1a4ab54462b7ddac12a0f52832/demo/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /demo/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip 2 | -------------------------------------------------------------------------------- /demo/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /demo/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.sapi 7 | demo 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | demo 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.4.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | test 37 | 38 | 39 | 40 | com.github.xiaour 41 | xiaour.springboot.sapi.starter 42 | 1.4.1 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-maven-plugin 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /demo/src/main/java/com/sapi/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.sapi.demo; 2 | 3 | import com.github.xiaour.sapi.annotation.Sapi; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @Sapi(controllers = {"com.sapi.demo.controller"}) 8 | @SpringBootApplication 9 | public class DemoApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(DemoApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /demo/src/main/java/com/sapi/demo/controller/CatController.java: -------------------------------------------------------------------------------- 1 | package com.sapi.demo.controller; 2 | 3 | import com.github.xiaour.sapi.annotation.SapiGroup; 4 | import org.springframework.web.bind.annotation.*; 5 | 6 | /** 7 | * @Author: Xiaour 8 | * @Description: 9 | * @Date: 2018/8/30 16:53 10 | */ 11 | @SapiGroup(title = "小猫管理") 12 | @RestController 13 | @RequestMapping("/cat") 14 | public class CatController { 15 | 16 | @GetMapping("/get/{mouse}") 17 | public String get(@PathVariable String mouse,String my,int age){ 18 | System.out.println(mouse); 19 | return "{\"hellokitty\":\"smile\"}"; 20 | } 21 | 22 | @PostMapping("/hellokitty/") 23 | public String hellokitty(String user){ 24 | return "{\"hellokitty\":\"smile\"}"; 25 | } 26 | 27 | @DeleteMapping("/delete") 28 | public String delete(){ 29 | return "{\"hellokitty\":\"smile\"}"; 30 | } 31 | 32 | @PutMapping("/put") 33 | public String put(){ 34 | return "{\"hellokitty\":\"smile\"}"; 35 | } 36 | 37 | @PatchMapping("/PATCH") 38 | public String patch(String name){ 39 | return "{\"hellokitty\":\""+name+"\"}"; 40 | } 41 | 42 | @RequestMapping("/request") 43 | public String request(){ 44 | return "{\"hellokitty\":\"smile\"}"; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /demo/src/main/java/com/sapi/demo/controller/DogController.java: -------------------------------------------------------------------------------- 1 | package com.sapi.demo.controller; 2 | 3 | import com.github.xiaour.sapi.annotation.SapiGroup; 4 | import org.springframework.web.bind.annotation.*; 5 | 6 | /** 7 | * @Author: Xiaour 8 | * @Description: 9 | * @Date: 2018/8/30 16:53 10 | */ 11 | @RestController 12 | @RequestMapping("/dog") 13 | public class DogController { 14 | 15 | @GetMapping("/get/{mouse}") 16 | public String get(@PathVariable String mouse,String my,int age){ 17 | System.out.println(mouse); 18 | return "{\"hellokitty\":\"smile\"}"; 19 | } 20 | 21 | @PostMapping("/hellokitty/") 22 | public String hellokitty(String user){ 23 | return "{\"hellokitty\":\"smile\"}"; 24 | } 25 | 26 | @DeleteMapping("/delete") 27 | public String delete(){ 28 | return "{\"hellokitty\":\"smile\"}"; 29 | } 30 | 31 | @PutMapping("/put") 32 | public String put(){ 33 | return "{\"hellokitty\":\"smile\"}"; 34 | } 35 | 36 | @PatchMapping("/PATCH") 37 | public String patch(String name){ 38 | return "{\"hellokitty\":\""+name+"\"}"; 39 | } 40 | 41 | @RequestMapping("/request") 42 | public String request(){ 43 | return "{\"hellokitty\":\"smile\"}"; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /demo/src/main/java/com/sapi/demo/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.sapi.demo.controller; 2 | 3 | import com.github.xiaour.sapi.annotation.SapiGroup; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | import org.springframework.web.bind.annotation.RestController; 8 | import org.springframework.web.multipart.MultipartFile; 9 | 10 | /** 11 | * @Author: Xiaour 12 | * @Description: 13 | * @Date: 2018/8/27 17:05 14 | */ 15 | @SapiGroup(title = "用户管理") 16 | @RestController 17 | @RequestMapping(value="/user") 18 | public class UserController { 19 | 20 | /** 21 | * 获取用户信息 22 | * @param userName 23 | * @param id 24 | * @return 25 | */ 26 | @RequestMapping(value = "/getUserInfo",method = RequestMethod.GET) 27 | public String getUserInfo(String userName,Integer id){ 28 | 29 | return "{\n" + "\"employees\": [\n" + "{ \"firstName\":\"Bill\" , \"lastName\":\"Gates\" },\n" + "{ \"firstName\":\"George\" , \"lastName\":\"Bush\" },\n" + "{ \"firstName\":\"Thomas\" , \"lastName\":\"Carter\" }\n" + "]\n" + "}"; 30 | } 31 | 32 | @PostMapping(value = "/addUserInfo") 33 | public String addUserInfo(String userName, Integer age, String address,String title, MultipartFile avatar){ 34 | 35 | return "{\n" + "\"employees\": [\n" + "{ \"firstName\":\"Bill\" , \"lastName\":\"Gates\" },\n" + "{ \"firstName\":\"George\" , \"lastName\":\"Bush\" },\n" + "{ \"firstName\":\"Thomas\" , \"lastName\":\"Carter\" }\n" + "]\n" + "}"; 36 | } 37 | 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /demo/src/main/java/com/sapi/demo/controller/UserInfoController.java: -------------------------------------------------------------------------------- 1 | package com.sapi.demo.controller; 2 | 3 | import com.github.xiaour.sapi.annotation.SapiGroup; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | import org.springframework.web.bind.annotation.RestController; 8 | import org.springframework.web.multipart.MultipartFile; 9 | 10 | /** 11 | * @Author: Xiaour 12 | * @Description: 13 | * @Date: 2018/8/27 17:05 14 | */ 15 | @SapiGroup(title = "Vip用户信息") 16 | @RestController 17 | @RequestMapping(value="/userInfo") 18 | public class UserInfoController { 19 | 20 | /** 21 | * 获取用户信息 22 | * @param userName 23 | * @param id 24 | * @return 25 | */ 26 | @RequestMapping(value = "/getUserInfo",method = RequestMethod.GET) 27 | public String getUserInfo(String userName,Integer id){ 28 | 29 | return "{\n" + "\"employees\": [\n" + "{ \"firstName\":\"Bill\" , \"lastName\":\"Gates\" },\n" + "{ \"firstName\":\"George\" , \"lastName\":\"Bush\" },\n" + "{ \"firstName\":\"Thomas\" , \"lastName\":\"Carter\" }\n" + "]\n" + "}"; 30 | } 31 | 32 | @PostMapping(value = "/addUserInfo") 33 | public String addUserInfo(String userName, Integer age, String address,String title, MultipartFile avatar){ 34 | 35 | return "{\n" + "\"employees\": [\n" + "{ \"firstName\":\"Bill\" , \"lastName\":\"Gates\" },\n" + "{ \"firstName\":\"George\" , \"lastName\":\"Bush\" },\n" + "{ \"firstName\":\"Thomas\" , \"lastName\":\"Carter\" }\n" + "]\n" + "}"; 36 | } 37 | 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.sapi.enable=true 2 | server.servlet.context-path=/demo -------------------------------------------------------------------------------- /demo/src/test/java/com/sapi/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sapi.demo; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class DemoApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_2_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__ch_qos_logback_logback_core_1_2_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__com_fasterxml_classmate_1_3_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_9_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_9_6.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_9_6.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jdk8_2_9_6.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jsr310_2_9_6.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_parameter_names_2_9_6.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__javax_annotation_javax_annotation_api_1_3_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__javax_validation_validation_api_2_0_1_Final.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__log4j_log4j_1_2_17.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__org_apache_logging_log4j_log4j_api_2_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__org_apache_logging_log4j_log4j_core_2_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__org_apache_logging_log4j_log4j_to_slf4j_2_10_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__org_jboss_logging_jboss_logging_3_3_2_Final.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__org_slf4j_jul_to_slf4j_1_7_25.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_25.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/libraries/Maven__org_yaml_snakeyaml_1_19.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.github.xiaour 6 | xiaour.springboot.sapi.starter 7 | 1.4.1 8 | 9 | 10 | 11 | org.apache.maven.plugins 12 | maven-compiler-plugin 13 | 14 | 1.8 15 | 1.8 16 | 17 | 18 | 19 | 20 | com.github.xiaour.sapi.starter 21 | jar 22 | https://github.com/xiaour/spring.boot.sapi.starter 23 | SAPI:spring.boot.sapi.starter 24 | 25 | 26 | org.sonatype.oss 27 | oss-parent 28 | 7 29 | 30 | 31 | 32 | 33 | The Apache Software License, Version 2.0 34 | http://www.apache.org/licenses/LICENSE-2.0.txt 35 | repo 36 | 37 | 38 | 39 | 40 | master 41 | git@github.com:xiaour/spring.boot.sapi.starter.git 42 | scm:git:git@github.com:xiaour/spring.boot.sapi.starter.git 43 | scm:git:git@github.com:xiaour/spring.boot.sapi.starter.git 44 | 45 | 46 | 47 | 48 | xiaour 49 | cityuu@163.com 50 | https://xiaour.github.io 51 | 52 | 53 | 54 | 55 | UTF-8 56 | UTF-8 57 | 1.8 58 | 59 | 60 | 61 | 62 | sonatype-oss-release 63 | 64 | 65 | release 66 | Nexus Release Repository 67 | https://oss.sonatype.org/service/local/staging/deploy/maven2 68 | 69 | 70 | snapshots 71 | Nexus Snapshot Repository 72 | https://oss.sonatype.org/content/repositories/snapshots 73 | 74 | 75 | 76 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | org.springframework.boot 175 | spring-boot-dependencies 176 | 2.0.4.RELEASE 177 | pom 178 | import 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | org.springframework.boot 187 | spring-boot-starter-web 188 | 189 | 190 | 191 | log4j 192 | log4j 193 | 1.2.17 194 | provided 195 | 196 | 197 | 198 | org.apache.logging.log4j 199 | log4j-api 200 | 2.16.0 201 | provided 202 | 203 | 204 | org.apache.logging.log4j 205 | log4j-core 206 | 2.17.1 207 | provided 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/annotation/Sapi.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.annotation; 2 | 3 | import org.springframework.boot.web.servlet.ServletComponentScan; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * @Author: zhangtao@suiyueyule.com 9 | * @Date: 2019-01-31 17:27 10 | * @version: v1.0 11 | * @Description: 12 | */ 13 | @Documented 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ElementType.TYPE}) 16 | @ServletComponentScan(basePackages = {"com.github.xiaour.sapi.servlet"}) 17 | public @interface Sapi { 18 | 19 | String [] controllers() default {""}; 20 | 21 | boolean enable() default true; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/annotation/SapiGroup.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Documented 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.TYPE}) 8 | public @interface SapiGroup { 9 | String title(); 10 | } 11 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/config/ApiServerAutoConfigure.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.config; 2 | 3 | import com.github.xiaour.sapi.logging.Log; 4 | import com.github.xiaour.sapi.logging.LogFactory; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.boot.web.context.WebServerInitializedEvent; 9 | import org.springframework.context.ApplicationListener; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | import java.util.HashSet; 14 | import java.util.Set; 15 | 16 | @Configuration 17 | @ConditionalOnClass(SapiFactoryAutoConfigure.class) 18 | @ConfigurationProperties("spring.sapi.server") 19 | public class ApiServerAutoConfigure implements ApplicationListener { 20 | 21 | private final static Log LOG = LogFactory.getLog(ApiServerAutoConfigure.class); 22 | 23 | 24 | public static Set apiRouter=new HashSet(); 25 | 26 | private static String contextPath; 27 | private static int serverPort; 28 | 29 | @Override 30 | public void onApplicationEvent(WebServerInitializedEvent webServerInitializedEvent) { 31 | 32 | this.serverPort=webServerInitializedEvent.getWebServer().getPort(); 33 | 34 | this.contextPath=webServerInitializedEvent.getApplicationContext().getApplicationName(); 35 | 36 | apiRouter.add(contextPath+"/sapidata/apiList"); 37 | 38 | apiRouter.add(contextPath+"/sapidata/group"); 39 | 40 | LOG.info("Sapi v1.4.1:http://127.0.0.1:"+getPort()+getContextPath()+"/sapi"); 41 | } 42 | 43 | public static int getPort() { 44 | return serverPort; 45 | } 46 | 47 | public static String getContextPath() { 48 | return contextPath; 49 | } 50 | 51 | @Bean 52 | @ConditionalOnMissingBean 53 | public String initBean() { 54 | 55 | return ""; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/config/SapiFactoryAutoConfigure.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.config; 2 | 3 | import com.github.xiaour.sapi.annotation.Sapi; 4 | import com.github.xiaour.sapi.annotation.SapiGroup; 5 | import com.github.xiaour.sapi.dto.ApiField; 6 | import com.github.xiaour.sapi.dto.ApiInfo; 7 | import com.github.xiaour.sapi.logging.Log; 8 | import com.github.xiaour.sapi.logging.LogFactory; 9 | import com.github.xiaour.sapi.util.ClassScaner; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 12 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 13 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 14 | import org.springframework.context.annotation.Configuration; 15 | import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; 16 | import org.springframework.core.LocalVariableTableParameterNameDiscoverer; 17 | import org.springframework.core.ParameterNameDiscoverer; 18 | import org.springframework.core.annotation.AnnotationAttributes; 19 | import org.springframework.core.type.AnnotationMetadata; 20 | import org.springframework.util.StringUtils; 21 | import org.springframework.web.bind.annotation.*; 22 | 23 | import java.lang.annotation.Annotation; 24 | import java.lang.reflect.Field; 25 | import java.lang.reflect.Method; 26 | import java.lang.reflect.Modifier; 27 | import java.util.ArrayList; 28 | import java.util.HashSet; 29 | import java.util.List; 30 | import java.util.Set; 31 | 32 | import static org.springframework.core.annotation.AnnotationAttributes.fromMap; 33 | 34 | @Configuration 35 | @AutoConfigureBefore(ApiServerAutoConfigure.class) 36 | @EnableConfigurationProperties({ApiServerAutoConfigure.class,SapiProperties.class}) 37 | public class SapiFactoryAutoConfigure implements ImportBeanDefinitionRegistrar{ 38 | private final static Log LOG = LogFactory.getLog(SapiFactoryAutoConfigure.class); 39 | 40 | private SapiProperties sapiProperties; 41 | 42 | @Autowired 43 | public SapiFactoryAutoConfigure(SapiProperties sapiProperties) { 44 | this.sapiProperties = sapiProperties; 45 | } 46 | 47 | public SapiFactoryAutoConfigure() { 48 | super(); 49 | } 50 | 51 | 52 | public static List simpleApiList; 53 | 54 | private static final String allRequestType="POST,GET,PUT,DELETE,PATCH"; 55 | 56 | static class OtherMapping{ 57 | private String [] mappingName; 58 | 59 | private String requestType; 60 | 61 | public String[] getMappingName() { 62 | return mappingName; 63 | } 64 | 65 | public void setMappingName(String[] mappingName) { 66 | this.mappingName = mappingName; 67 | } 68 | 69 | public String getRequestType() { 70 | return requestType; 71 | } 72 | 73 | public void setRequestType(String requestType) { 74 | this.requestType = requestType; 75 | } 76 | } 77 | 78 | @Override 79 | public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { 80 | 81 | AnnotationAttributes attributes = fromMap(annotationMetadata.getAnnotationAttributes(Sapi.class.getCanonicalName())); 82 | 83 | String[] values; 84 | 85 | boolean enabled; 86 | 87 | //优先从配置文件取值,其次从注解取值 88 | if(sapiProperties!=null){ 89 | 90 | values = sapiProperties.getControllers().split(","); 91 | 92 | enabled = StringUtils.hasText(sapiProperties.getEnable())?Boolean.getBoolean(sapiProperties.getEnable()):true; 93 | 94 | }else { 95 | 96 | values = attributes.getStringArray("controllers"); 97 | 98 | enabled = attributes.getBoolean("enable"); 99 | } 100 | 101 | //如果开启,则扫描接口 102 | if(enabled){ 103 | 104 | if(values != null) { 105 | 106 | init(values); 107 | 108 | }else { 109 | 110 | LOG.error("Sapi annotations not config,Please Configured the Application class @Sapi(controllers={\"your.controller.path1\",\"...\"})"); 111 | } 112 | } 113 | } 114 | 115 | 116 | public void init(String[] controllers){ 117 | 118 | LOG.debug("Springboot SAPI init."); 119 | 120 | Set classes= new HashSet(); 121 | try { 122 | 123 | for(String packageName:controllers){ 124 | classes.addAll(ClassScaner.scan(packageName)); 125 | } 126 | 127 | List list=new ArrayList(); 128 | 129 | String groupTitle;//分组名称 130 | 131 | for(Class c:classes){ 132 | RequestMapping requestMapping= (RequestMapping) c.getAnnotation(RequestMapping.class); 133 | 134 | SapiGroup sapiGroup= (SapiGroup)c.getAnnotation(SapiGroup.class); 135 | 136 | if(sapiGroup==null){ 137 | groupTitle=c.getSimpleName(); 138 | }else{ 139 | groupTitle=sapiGroup.title(); 140 | } 141 | 142 | SapiGroupManager.setSapiGroup(groupTitle); 143 | 144 | if(requestMapping==null){ 145 | list.addAll(getReflectAllMethod(c,new String[]{""},groupTitle)); 146 | }else { 147 | list.addAll(getReflectAllMethod(c, requestMapping.value(),groupTitle)); 148 | } 149 | } 150 | 151 | simpleApiList= list; 152 | 153 | } catch (Exception e) { 154 | 155 | LOG.error("SAPI init exception:",e); 156 | } 157 | } 158 | 159 | 160 | /** 161 | * 获取所有的自定义方法 162 | * @param mLocalClass 163 | * @param routes 164 | * @param groupTitle 165 | * @return 166 | */ 167 | private List getReflectAllMethod( Class mLocalClass,String [] routes,String groupTitle){ 168 | 169 | ParameterNameDiscoverer pnd = new LocalVariableTableParameterNameDiscoverer(); 170 | 171 | List list = new ArrayList(); 172 | 173 | try { 174 | do{ 175 | Method methods[] = mLocalClass.getDeclaredMethods(); // 取得全部的方法 176 | 177 | for (Method method:methods) { 178 | 179 | Class paramsTypes[] = method.getParameterTypes(); 180 | 181 | Annotation[][] paramAnnotations = method.getParameterAnnotations(); 182 | 183 | int length = paramsTypes.length; 184 | 185 | for(String route:routes){ 186 | 187 | String mod = Modifier.toString(method.getModifiers()); 188 | 189 | String metName = method.getName(); 190 | 191 | RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); 192 | 193 | if (mod.equals("public") && !metName.equals("toString") && !metName.equals("equals")) { 194 | 195 | if(requestMapping!=null){ 196 | 197 | for(String mappingName:requestMapping.value()) { 198 | 199 | String requestInfo =""; 200 | 201 | RequestMethod[] me = method.getAnnotation(RequestMapping.class).method(); 202 | 203 | for (RequestMethod rm : me) { 204 | 205 | requestInfo = rm.name(); 206 | } 207 | 208 | if(requestInfo == ""){ 209 | 210 | requestInfo=allRequestType; 211 | 212 | } 213 | ApiInfo apiInfo = getApiInfo(method,route,mappingName,pnd,length,paramsTypes,requestInfo,paramAnnotations); 214 | 215 | apiInfo.setGroupTitle(groupTitle); 216 | 217 | list.add(apiInfo); 218 | } 219 | }else{ 220 | 221 | OtherMapping otherMapping=getMappingType(method); 222 | 223 | if(otherMapping!=null){ 224 | 225 | for(String mappingName:otherMapping.getMappingName()) { 226 | 227 | ApiInfo apiInfo = getApiInfo(method,route,mappingName,pnd,length,paramsTypes,otherMapping.getRequestType(),paramAnnotations); 228 | 229 | apiInfo.setGroupTitle(groupTitle); 230 | 231 | list.add(apiInfo); 232 | } 233 | } 234 | } 235 | 236 | } 237 | } 238 | } 239 | 240 | mLocalClass=mLocalClass.getSuperclass(); 241 | 242 | }while(mLocalClass!=null); 243 | 244 | } catch (Exception e) { 245 | 246 | LOG.error("Sapi init exception:",e); 247 | } 248 | return list; 249 | } 250 | 251 | private static ApiInfo getApiInfo(Method method, 252 | String route, 253 | String mappingName, 254 | ParameterNameDiscoverer pnd, 255 | int length, 256 | Class paramsTypes[], 257 | String requestType, 258 | Annotation[][] paramAnnotations){ 259 | ApiInfo apiInfo = new ApiInfo(); 260 | 261 | if(requestType!=null) { 262 | 263 | apiInfo.setRequestType(requestType); 264 | } 265 | 266 | apiInfo.setUrl(route + "/" + mappingName); 267 | 268 | apiInfo.setUrl(apiInfo.getUrl().replaceAll("//","/")); 269 | 270 | String[] paramNames = pnd.getParameterNames(method); 271 | 272 | List apiFields = getDefaultType(length, paramsTypes, paramNames,paramAnnotations); 273 | 274 | apiInfo.setFieldList(apiFields); 275 | 276 | apiInfo.setId(); 277 | 278 | return apiInfo; 279 | } 280 | 281 | /** 282 | * 获取请求方式 283 | * @return 284 | */ 285 | private static OtherMapping getMappingType(Method method){ 286 | OtherMapping otherMapping=new OtherMapping(); 287 | //因为方法上可能有多个注解,所以只能通过需要的注解进行判断 288 | 289 | PostMapping postMapping = method.getAnnotation(PostMapping.class); 290 | 291 | GetMapping getMapping = method.getAnnotation(GetMapping.class); 292 | 293 | DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class); 294 | 295 | PatchMapping patchMapping = method.getAnnotation(PatchMapping.class); 296 | 297 | PutMapping putMapping = method.getAnnotation(PutMapping.class); 298 | 299 | if(postMapping!=null){ 300 | otherMapping.setMappingName(postMapping.value()); 301 | otherMapping.setRequestType(RequestMethod.POST.name()); 302 | return otherMapping; 303 | } 304 | if(getMapping!=null){ 305 | otherMapping.setMappingName(getMapping.value()); 306 | otherMapping.setRequestType(RequestMethod.GET.name()); 307 | return otherMapping; 308 | } 309 | if(deleteMapping!=null){ 310 | otherMapping.setMappingName(deleteMapping.value()); 311 | otherMapping.setRequestType(RequestMethod.DELETE.name()); 312 | return otherMapping; 313 | } 314 | if(patchMapping!=null){ 315 | otherMapping.setMappingName(patchMapping.value()); 316 | otherMapping.setRequestType(RequestMethod.PATCH.name()); 317 | return otherMapping; 318 | } 319 | if(putMapping!=null){ 320 | otherMapping.setMappingName(putMapping.value()); 321 | otherMapping.setRequestType(RequestMethod.PUT.name()); 322 | return otherMapping; 323 | } 324 | return null; 325 | } 326 | 327 | /** 328 | * 是否是原生的JAVA类 329 | * @param clz 330 | * @return 331 | */ 332 | private static boolean isJavaClass(Class clz) { 333 | 334 | if(clz.getName().contains("multipart.MultipartFile")){ 335 | return true; 336 | } 337 | return clz != null && clz.getClassLoader() == null; 338 | } 339 | 340 | /** 341 | * 获取默认基本类型字段 342 | * @param length 343 | * @param paramsTypes 344 | * @param paramNames 345 | * @return 346 | */ 347 | private static List getDefaultType(int length,Class paramsTypes[],String[] paramNames,Annotation[][] paramAnnotations){ 348 | 349 | List apiFields=new ArrayList(length); 350 | 351 | for(int i=0;i apiFields){ 392 | try { 393 | Field[] fields = clz.getDeclaredFields(); 394 | for(Field f : fields) { 395 | ApiField apiField = new ApiField(); 396 | apiField.setName(f.getName()); 397 | apiField.setType(getTypeName(f.getType().getName())); 398 | 399 | apiFields.add(apiField); 400 | } 401 | } catch(Exception e) { 402 | LOG.error("Sapi getCustomType exception:",e); 403 | } 404 | } 405 | 406 | /** 407 | * 截取类型 408 | * @param typeName 409 | * @return 410 | */ 411 | private static String getTypeName(String typeName){ 412 | 413 | return typeName.toString().substring(typeName.lastIndexOf(".") + 1,typeName.length()); 414 | } 415 | 416 | 417 | } -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/config/SapiGroupManager.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.config; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | 7 | public class SapiGroupManager { 8 | 9 | private static Set sapiGroup =new HashSet(); 10 | 11 | 12 | public static Set getSapiGroup() { 13 | return sapiGroup; 14 | } 15 | 16 | public static void setSapiGroup(String groupTitle){ 17 | sapiGroup.add(groupTitle); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/config/SapiProperties.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * @Author: zhangtao@suiyueyule.com 7 | * @Date: 2019-07-16 10:35 8 | * @version: v1.0 9 | * @Description: 10 | */ 11 | @ConfigurationProperties(prefix = "spring.sapi") 12 | public class SapiProperties { 13 | 14 | private String enable;//enable SAPI 15 | 16 | private String controllers; 17 | 18 | public String getEnable() { 19 | return enable; 20 | } 21 | 22 | public void setEnable(String enable) { 23 | this.enable = enable; 24 | } 25 | 26 | public String getControllers() { 27 | return controllers; 28 | } 29 | 30 | public void setControllers(String controllers) { 31 | this.controllers = controllers; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/dto/ApiField.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.dto; 2 | 3 | public class ApiField { 4 | 5 | private String type;//字段类型 6 | 7 | private String name;//字段名 8 | 9 | private String notNull;//是否为空 10 | 11 | private String remark;//备注 12 | 13 | private boolean isPath=false;//是否路径参数 14 | 15 | private boolean isRequestBody=false;//是否Body参数 16 | 17 | public String getType() { 18 | return type; 19 | } 20 | 21 | public void setType(String type) { 22 | this.type = type; 23 | } 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | public void setName(String name) { 30 | this.name = name; 31 | } 32 | 33 | public String getNotNull() { 34 | return notNull; 35 | } 36 | 37 | public void setNotNull(String notNull) { 38 | this.notNull = notNull; 39 | } 40 | 41 | public String getRemark() { 42 | return remark; 43 | } 44 | 45 | public void setRemark(String remark) { 46 | this.remark = remark; 47 | } 48 | 49 | public boolean getIsPath() { 50 | return isPath; 51 | } 52 | 53 | public boolean getIsRequestBody() { 54 | return isRequestBody; 55 | } 56 | 57 | public void setRequestBody(boolean requestBody) { 58 | isRequestBody = requestBody; 59 | } 60 | 61 | public void setIsPath(boolean isPath) { 62 | this.isPath = isPath; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/dto/ApiInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.dto; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 接口信息 7 | */ 8 | public class ApiInfo { 9 | 10 | 11 | private Integer id; 12 | 13 | private String url;//URL 14 | 15 | private String requestType="";//请求类型 16 | 17 | private List fieldList;//字段 18 | 19 | private String success;//成功返回值 20 | 21 | private String error;//错误返回值 22 | 23 | private String other;//其他返回值 24 | 25 | private String name;//名称 26 | 27 | private String groupTitle;//分组名称 28 | 29 | public String getGroupTitle() { 30 | return groupTitle; 31 | } 32 | 33 | public void setGroupTitle(String groupTitle) { 34 | this.groupTitle = groupTitle; 35 | } 36 | 37 | public String getName() { 38 | return name; 39 | } 40 | 41 | public void setName(String name) { 42 | this.name = name; 43 | } 44 | 45 | public String getUrl() { 46 | return url; 47 | } 48 | 49 | public void setUrl(String url) { 50 | this.url = url; 51 | } 52 | 53 | public String getRequestType() { 54 | return requestType; 55 | } 56 | 57 | public void setRequestType(String requestType) { 58 | this.requestType = requestType; 59 | } 60 | 61 | public List getFieldList() { 62 | return fieldList; 63 | } 64 | 65 | public void setFieldList(List fieldList) { 66 | this.fieldList = fieldList; 67 | } 68 | 69 | public String getSuccess() { 70 | return success; 71 | } 72 | 73 | public void setSuccess(String success) { 74 | this.success = success; 75 | } 76 | 77 | public String getError() { 78 | return error; 79 | } 80 | 81 | public void setError(String error) { 82 | this.error = error; 83 | } 84 | 85 | public String getOther() { 86 | return other; 87 | } 88 | 89 | public void setOther(String other) { 90 | this.other = other; 91 | } 92 | 93 | public void setId(){ 94 | this.id=Math.abs(this.getUrl().hashCode()); 95 | } 96 | 97 | public Integer getId(){ 98 | return id; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/exception/SimpleApiException.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.exception; 2 | 3 | public class SimpleApiException extends Exception{ 4 | 5 | public SimpleApiException(String errMsg) { 6 | super(errMsg); 7 | } 8 | 9 | public SimpleApiException() { 10 | super(); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/logging/JakartaCommonsLoggingImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1999-2018 Alibaba Group Holding Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.xiaour.sapi.logging; 17 | 18 | import org.apache.commons.logging.Log; 19 | import org.apache.commons.logging.LogFactory; 20 | 21 | public class JakartaCommonsLoggingImpl implements com.github.xiaour.sapi.logging.Log { 22 | 23 | private Log log; 24 | 25 | private int errorCount; 26 | private int warnCount; 27 | private int infoCount; 28 | private int debugCount; 29 | 30 | 31 | public JakartaCommonsLoggingImpl(Log log){ 32 | this.log = log; 33 | } 34 | 35 | public JakartaCommonsLoggingImpl(String loggerName){ 36 | log = LogFactory.getLog(loggerName); 37 | } 38 | 39 | public boolean isDebugEnabled() { 40 | return log.isDebugEnabled(); 41 | } 42 | 43 | public void error(String s, Throwable e) { 44 | log.error(s, e); 45 | errorCount++; 46 | } 47 | 48 | public void error(String s) { 49 | log.error(s); 50 | errorCount++; 51 | } 52 | 53 | public void debug(String s) { 54 | debugCount++; 55 | log.debug(s); 56 | } 57 | 58 | public void debug(String s, Throwable e) { 59 | debugCount++; 60 | log.debug(s, e); 61 | } 62 | 63 | public void warn(String s) { 64 | log.warn(s); 65 | warnCount++; 66 | } 67 | 68 | @Override 69 | public void warn(String s, Throwable e) { 70 | log.warn(s, e); 71 | warnCount++; 72 | } 73 | 74 | @Override 75 | public int getWarnCount() { 76 | return warnCount; 77 | } 78 | 79 | public int getErrorCount() { 80 | return errorCount; 81 | } 82 | 83 | @Override 84 | public void resetStat() { 85 | errorCount = 0; 86 | warnCount = 0; 87 | infoCount = 0; 88 | debugCount++; 89 | } 90 | 91 | @Override 92 | public boolean isInfoEnabled() { 93 | return log.isInfoEnabled(); 94 | } 95 | 96 | @Override 97 | public void info(String msg) { 98 | log.info(msg); 99 | infoCount++; 100 | } 101 | 102 | @Override 103 | public int getInfoCount() { 104 | return infoCount; 105 | } 106 | 107 | @Override 108 | public boolean isWarnEnabled() { 109 | return log.isWarnEnabled(); 110 | } 111 | 112 | public int getDebugCount() { 113 | return debugCount; 114 | } 115 | 116 | @Override 117 | public boolean isErrorEnabled() { 118 | return log.isErrorEnabled(); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/logging/Jdk14LoggingImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1999-2018 Alibaba Group Holding Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.xiaour.sapi.logging; 17 | 18 | import java.util.logging.Level; 19 | import java.util.logging.Logger; 20 | 21 | public class Jdk14LoggingImpl implements Log { 22 | 23 | private Logger log; 24 | 25 | private int errorCount; 26 | private int warnCount; 27 | private int infoCount; 28 | private int debugCount; 29 | 30 | private String loggerName; 31 | 32 | public Jdk14LoggingImpl(String loggerName){ 33 | this.loggerName = loggerName; 34 | log = Logger.getLogger(loggerName); 35 | } 36 | 37 | public boolean isDebugEnabled() { 38 | return log.isLoggable(Level.FINE); 39 | } 40 | 41 | public void error(String s, Throwable e) { 42 | log.logp(Level.SEVERE, loggerName, Thread.currentThread().getStackTrace()[1].getMethodName(), s, e); 43 | errorCount++; 44 | } 45 | 46 | public void error(String s) { 47 | log.logp(Level.SEVERE, loggerName, Thread.currentThread().getStackTrace()[1].getMethodName(), s); 48 | errorCount++; 49 | } 50 | 51 | public void debug(String s) { 52 | debugCount++; 53 | log.logp(Level.FINE, loggerName, Thread.currentThread().getStackTrace()[1].getMethodName(), s); 54 | } 55 | 56 | public void debug(String s, Throwable e) { 57 | debugCount++; 58 | log.logp(Level.FINE, loggerName, Thread.currentThread().getStackTrace()[1].getMethodName(), s, e); 59 | } 60 | 61 | public void warn(String s) { 62 | log.logp(Level.WARNING, loggerName, Thread.currentThread().getStackTrace()[1].getMethodName(), s); 63 | warnCount++; 64 | } 65 | 66 | @Override 67 | public void warn(String s, Throwable e) { 68 | log.logp(Level.WARNING, loggerName, Thread.currentThread().getStackTrace()[1].getMethodName(), s, e); 69 | warnCount++; 70 | } 71 | 72 | @Override 73 | public int getWarnCount() { 74 | return warnCount; 75 | } 76 | 77 | public int getErrorCount() { 78 | return errorCount; 79 | } 80 | 81 | @Override 82 | public void resetStat() { 83 | errorCount = 0; 84 | warnCount = 0; 85 | infoCount = 0; 86 | debugCount = 0; 87 | } 88 | 89 | @Override 90 | public boolean isInfoEnabled() { 91 | return log.isLoggable(Level.INFO); 92 | } 93 | 94 | @Override 95 | public void info(String msg) { 96 | log.logp(Level.INFO, loggerName, Thread.currentThread().getStackTrace()[1].getMethodName(), msg); 97 | infoCount++; 98 | } 99 | 100 | @Override 101 | public int getInfoCount() { 102 | return infoCount; 103 | } 104 | 105 | @Override 106 | public boolean isWarnEnabled() { 107 | return log.isLoggable(Level.WARNING); 108 | } 109 | 110 | public int getDebugCount() { 111 | return debugCount; 112 | } 113 | 114 | @Override 115 | public boolean isErrorEnabled() { 116 | return log.isLoggable(Level.SEVERE); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/logging/Log.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1999-2018 Alibaba Group Holding Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.xiaour.sapi.logging; 17 | 18 | public interface Log { 19 | 20 | boolean isDebugEnabled(); 21 | 22 | void error(String msg, Throwable e); 23 | 24 | void error(String msg); 25 | 26 | boolean isInfoEnabled(); 27 | 28 | void info(String msg); 29 | 30 | void debug(String msg); 31 | 32 | void debug(String msg, Throwable e); 33 | 34 | boolean isWarnEnabled(); 35 | 36 | void warn(String msg); 37 | 38 | void warn(String msg, Throwable e); 39 | 40 | boolean isErrorEnabled(); 41 | 42 | int getErrorCount(); 43 | 44 | int getWarnCount(); 45 | 46 | int getInfoCount(); 47 | 48 | int getDebugCount(); 49 | 50 | void resetStat(); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/logging/Log4j2Impl.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.logging; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | /* 8 | * Copyright 1999-2018 Alibaba Group Holding Ltd. 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | */ 22 | public class Log4j2Impl implements Log { 23 | 24 | private Logger log; 25 | 26 | private int errorCount; 27 | private int warnCount; 28 | private int infoCount; 29 | private int debugCount; 30 | 31 | public Log4j2Impl(Logger log){ 32 | this.log = log; 33 | } 34 | 35 | public Log4j2Impl(String loggerName){ 36 | log = LogManager.getLogger(loggerName); 37 | } 38 | 39 | public Logger getLog() { 40 | return log; 41 | } 42 | 43 | public boolean isDebugEnabled() { 44 | return log.isDebugEnabled(); 45 | } 46 | 47 | public void error(String s, Throwable e) { 48 | errorCount++; 49 | log.error(s, e); 50 | } 51 | 52 | public void error(String s) { 53 | errorCount++; 54 | log.error(s); 55 | } 56 | 57 | public void debug(String s) { 58 | debugCount++; 59 | log.debug(s); 60 | } 61 | 62 | public void debug(String s, Throwable e) { 63 | debugCount++; 64 | log.debug(s, e); 65 | } 66 | 67 | public void warn(String s) { 68 | log.warn(s); 69 | warnCount++; 70 | } 71 | 72 | public void warn(String s, Throwable e) { 73 | log.warn(s, e); 74 | warnCount++; 75 | } 76 | 77 | public int getWarnCount() { 78 | return warnCount; 79 | } 80 | 81 | public int getErrorCount() { 82 | return errorCount; 83 | } 84 | 85 | public void resetStat() { 86 | errorCount = 0; 87 | warnCount = 0; 88 | infoCount = 0; 89 | debugCount = 0; 90 | } 91 | 92 | public int getDebugCount() { 93 | return debugCount; 94 | } 95 | 96 | public boolean isInfoEnabled() { 97 | return log.isInfoEnabled(); 98 | } 99 | 100 | public void info(String msg) { 101 | infoCount++; 102 | log.info(msg); 103 | } 104 | 105 | public boolean isWarnEnabled() { 106 | return log.isEnabled(Level.WARN); 107 | } 108 | 109 | public boolean isErrorEnabled() { 110 | return log.isErrorEnabled(); 111 | } 112 | 113 | public int getInfoCount() { 114 | return infoCount; 115 | } 116 | 117 | public String toString() { 118 | return log.toString(); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/logging/Log4jImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1999-2018 Alibaba Group Holding Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.xiaour.sapi.logging; 17 | 18 | import org.apache.log4j.Level; 19 | import org.apache.log4j.Logger; 20 | 21 | public class Log4jImpl implements Log { 22 | 23 | private static final String callerFQCN = Log4jImpl.class.getName(); 24 | 25 | private Logger log; 26 | 27 | private int errorCount; 28 | private int warnCount; 29 | private int infoCount; 30 | private int debugCount; 31 | 32 | public Log4jImpl(Logger log){ 33 | this.log = log; 34 | } 35 | 36 | public Log4jImpl(String loggerName){ 37 | log = Logger.getLogger(loggerName); 38 | } 39 | 40 | public Logger getLog() { 41 | return log; 42 | } 43 | 44 | public boolean isDebugEnabled() { 45 | return log.isDebugEnabled(); 46 | } 47 | 48 | public void error(String s, Throwable e) { 49 | errorCount++; 50 | log.log(callerFQCN, Level.ERROR, s, e); 51 | } 52 | 53 | public void error(String s) { 54 | errorCount++; 55 | log.log(callerFQCN, Level.ERROR, s, null); 56 | } 57 | 58 | public void debug(String s) { 59 | debugCount++; 60 | log.log(callerFQCN, Level.DEBUG, s, null); 61 | } 62 | 63 | public void debug(String s, Throwable e) { 64 | debugCount++; 65 | log.log(callerFQCN, Level.DEBUG, s, e); 66 | } 67 | 68 | public void warn(String s) { 69 | log.log(callerFQCN, Level.WARN, s, null); 70 | warnCount++; 71 | } 72 | 73 | public void warn(String s, Throwable e) { 74 | log.log(callerFQCN, Level.WARN, s, e); 75 | warnCount++; 76 | } 77 | 78 | public int getWarnCount() { 79 | return warnCount; 80 | } 81 | 82 | public int getErrorCount() { 83 | return errorCount; 84 | } 85 | 86 | public void resetStat() { 87 | errorCount = 0; 88 | warnCount = 0; 89 | infoCount = 0; 90 | debugCount = 0; 91 | } 92 | 93 | public int getDebugCount() { 94 | return debugCount; 95 | } 96 | 97 | public boolean isInfoEnabled() { 98 | return log.isInfoEnabled(); 99 | } 100 | 101 | public void info(String msg) { 102 | infoCount++; 103 | log.log(callerFQCN, Level.INFO, msg, null); 104 | } 105 | 106 | public boolean isWarnEnabled() { 107 | return log.isEnabledFor(Level.WARN); 108 | } 109 | 110 | public boolean isErrorEnabled() { 111 | return log.isEnabledFor(Level.ERROR); 112 | } 113 | 114 | public int getInfoCount() { 115 | return infoCount; 116 | } 117 | 118 | public String toString() { 119 | return log.toString(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/logging/LogFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1999-2018 Alibaba Group Holding Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.xiaour.sapi.logging; 17 | 18 | import java.lang.reflect.Constructor; 19 | 20 | @SuppressWarnings("rawtypes") 21 | public class LogFactory { 22 | 23 | private static Constructor logConstructor; 24 | 25 | static { 26 | String logType= System.getProperty("druid.logType"); 27 | if(logType != null){ 28 | if(logType.equalsIgnoreCase("slf4j")){ 29 | tryImplementation("org.slf4j.Logger", "SLF4JImpl"); 30 | }else if(logType.equalsIgnoreCase("log4j")){ 31 | tryImplementation("org.apache.log4j.Logger", "Log4jImpl"); 32 | }else if(logType.equalsIgnoreCase("log4j2")){ 33 | tryImplementation("org.apache.logging.log4j.Logger", "Log4j2Impl"); 34 | }else if(logType.equalsIgnoreCase("commonsLog")){ 35 | tryImplementation("org.apache.commons.logging.LogFactory", 36 | "JakartaCommonsLoggingImpl"); 37 | }else if(logType.equalsIgnoreCase("jdkLog")){ 38 | tryImplementation("java.util.logging.Logger", "Jdk14LoggingImpl"); 39 | } 40 | } 41 | // 优先选择log4j,而非Apache Common Logging. 因为后者无法设置真实Log调用者的信息 42 | tryImplementation("org.slf4j.Logger", "SLF4JImpl"); 43 | tryImplementation("org.apache.log4j.Logger", "Log4jImpl"); 44 | tryImplementation("org.apache.logging.log4j.Logger", "Log4j2Impl"); 45 | tryImplementation("org.apache.commons.logging.LogFactory", 46 | "JakartaCommonsLoggingImpl"); 47 | tryImplementation("java.util.logging.Logger", "Jdk14LoggingImpl"); 48 | 49 | if (logConstructor == null) { 50 | try { 51 | logConstructor = NoLoggingImpl.class.getConstructor(String.class); 52 | } catch (Exception e) { 53 | throw new IllegalStateException(e.getMessage(), e); 54 | } 55 | } 56 | } 57 | 58 | @SuppressWarnings("unchecked") 59 | private static void tryImplementation(String testClassName, String implClassName) { 60 | if (logConstructor != null) { 61 | return; 62 | } 63 | 64 | try { 65 | Resources.classForName(testClassName); 66 | Class implClass = Resources.classForName(implClassName); 67 | logConstructor = implClass.getConstructor(new Class[] { String.class }); 68 | 69 | Class declareClass = logConstructor.getDeclaringClass(); 70 | if (!Log.class.isAssignableFrom(declareClass)) { 71 | logConstructor = null; 72 | } 73 | 74 | try { 75 | if (null != logConstructor) { 76 | logConstructor.newInstance(LogFactory.class.getName()); 77 | } 78 | } catch (Throwable t) { 79 | logConstructor = null; 80 | } 81 | 82 | } catch (Throwable t) { 83 | // skip 84 | } 85 | } 86 | 87 | public static Log getLog(Class clazz) { 88 | return getLog(clazz.getName()); 89 | } 90 | 91 | public static Log getLog(String loggerName) { 92 | try { 93 | return (Log) logConstructor.newInstance(loggerName); 94 | } catch (Throwable t) { 95 | throw new RuntimeException("Error creating logger for logger '" + loggerName + "'. Cause: " + t, t); 96 | } 97 | } 98 | 99 | @SuppressWarnings("unchecked") 100 | public static synchronized void selectLog4JLogging() { 101 | try { 102 | Resources.classForName("org.apache.log4j.Logger"); 103 | Class implClass = Resources.classForName("Log4jImpl"); 104 | logConstructor = implClass.getConstructor(new Class[] { String.class }); 105 | } catch (Throwable t) { 106 | //ignore 107 | } 108 | } 109 | 110 | @SuppressWarnings("unchecked") 111 | public static synchronized void selectJavaLogging() { 112 | try { 113 | Resources.classForName("java.util.logging.Logger"); 114 | Class implClass = Resources.classForName("Jdk14LoggingImpl"); 115 | logConstructor = implClass.getConstructor(new Class[] { String.class }); 116 | } catch (Throwable t) { 117 | //ignore 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/logging/NoLoggingImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1999-2018 Alibaba Group Holding Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.xiaour.sapi.logging; 17 | 18 | public class NoLoggingImpl implements Log { 19 | 20 | private int infoCount; 21 | private int errorCount; 22 | private int warnCount; 23 | private int debugCount; 24 | private String loggerName; 25 | 26 | private boolean debugEnable = false; 27 | private boolean infoEnable = true; 28 | private boolean warnEnable = true; 29 | private boolean errorEnable = true; 30 | 31 | public NoLoggingImpl(String loggerName){ 32 | this.loggerName = loggerName; 33 | } 34 | 35 | public String getLoggerName() { 36 | return this.loggerName; 37 | } 38 | 39 | public boolean isDebugEnabled() { 40 | return debugEnable; 41 | } 42 | 43 | public void error(String s, Throwable e) { 44 | if (!errorEnable) { 45 | return; 46 | } 47 | 48 | error(s); 49 | 50 | if (e != null) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | 55 | public void error(String s) { 56 | errorCount++; 57 | if (s != null) { 58 | } 59 | } 60 | 61 | public void debug(String s) { 62 | debugCount++; 63 | } 64 | 65 | public void debug(String s, Throwable e) { 66 | debugCount++; 67 | } 68 | 69 | public void warn(String s) { 70 | warnCount++; 71 | } 72 | 73 | @Override 74 | public void warn(String s, Throwable e) { 75 | warnCount++; 76 | } 77 | 78 | public int getErrorCount() { 79 | return errorCount; 80 | } 81 | 82 | @Override 83 | public int getWarnCount() { 84 | return warnCount; 85 | } 86 | 87 | @Override 88 | public void resetStat() { 89 | errorCount = 0; 90 | warnCount = 0; 91 | infoCount = 0; 92 | debugCount = 0; 93 | } 94 | 95 | @Override 96 | public boolean isInfoEnabled() { 97 | return infoEnable; 98 | } 99 | 100 | @Override 101 | public void info(String s) { 102 | infoCount++; 103 | } 104 | 105 | @Override 106 | public boolean isWarnEnabled() { 107 | return warnEnable; 108 | } 109 | 110 | public int getInfoCount() { 111 | return infoCount; 112 | } 113 | 114 | public int getDebugCount() { 115 | return debugCount; 116 | } 117 | 118 | public boolean isErrorEnabled() { 119 | return errorEnable; 120 | } 121 | 122 | public void setErrorEnabled(boolean value) { 123 | this.errorEnable = value; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/logging/Resources.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1999-2018 Alibaba Group Holding Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.xiaour.sapi.logging; 17 | 18 | 19 | /** 20 | * A class to simplify access to resources through the classloader. 21 | */ 22 | public final class Resources extends Object { 23 | 24 | private static ClassLoader defaultClassLoader; 25 | 26 | private Resources(){ 27 | } 28 | 29 | /** 30 | * Returns the default classloader (may be null). 31 | * 32 | * @return The default classloader 33 | */ 34 | public static ClassLoader getDefaultClassLoader() { 35 | return defaultClassLoader; 36 | } 37 | 38 | /** 39 | * Sets the default classloader 40 | * 41 | * @param defaultClassLoader - the new default ClassLoader 42 | */ 43 | public static void setDefaultClassLoader(ClassLoader defaultClassLoader) { 44 | Resources.defaultClassLoader = defaultClassLoader; 45 | } 46 | 47 | /** 48 | * Loads a class 49 | * 50 | * @param className - the class to load 51 | * @return The loaded class 52 | * @throws ClassNotFoundException If the class cannot be found (duh!) 53 | */ 54 | public static Class classForName(String className) throws ClassNotFoundException { 55 | Class clazz = null; 56 | try { 57 | clazz = getClassLoader().loadClass(className); 58 | } catch (Exception e) { 59 | // Ignore. Failsafe below. 60 | } 61 | if (clazz == null) { 62 | clazz = Class.forName(className); 63 | } 64 | return clazz; 65 | } 66 | 67 | private static ClassLoader getClassLoader() { 68 | if (defaultClassLoader != null) { 69 | return defaultClassLoader; 70 | } else { 71 | return Thread.currentThread().getContextClassLoader(); 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/logging/SLF4JImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1999-2018 Alibaba Group Holding Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.xiaour.sapi.logging; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import org.slf4j.spi.LocationAwareLogger; 21 | 22 | public class SLF4JImpl implements Log { 23 | 24 | private static final String callerFQCN = SLF4JImpl.class.getName(); 25 | private static final Logger testLogger = LoggerFactory.getLogger(SLF4JImpl.class); 26 | static { 27 | // if the logger is not a LocationAwareLogger instance, it can not get correct stack StackTraceElement 28 | // so ignore this implementation. 29 | if (!(testLogger instanceof LocationAwareLogger)) { 30 | throw new UnsupportedOperationException(testLogger.getClass() + " is not a suitable logger"); 31 | } 32 | } 33 | private int errorCount; 34 | private int warnCount; 35 | private int infoCount; 36 | private int debugCount; 37 | private LocationAwareLogger log; 38 | 39 | public SLF4JImpl(LocationAwareLogger log){ 40 | this.log = log; 41 | } 42 | 43 | public SLF4JImpl(String loggerName){ 44 | this.log = (LocationAwareLogger) LoggerFactory.getLogger(loggerName); 45 | } 46 | 47 | @Override 48 | public boolean isDebugEnabled() { 49 | return log.isDebugEnabled(); 50 | } 51 | 52 | @Override 53 | public void error(String msg, Throwable e) { 54 | log.log(null, callerFQCN, LocationAwareLogger.ERROR_INT, msg, null, e); 55 | errorCount++; 56 | } 57 | 58 | @Override 59 | public void error(String msg) { 60 | log.log(null, callerFQCN, LocationAwareLogger.ERROR_INT, msg, null, null); 61 | errorCount++; 62 | } 63 | 64 | @Override 65 | public boolean isInfoEnabled() { 66 | return log.isInfoEnabled(); 67 | } 68 | 69 | @Override 70 | public void info(String msg) { 71 | infoCount++; 72 | log.log(null, callerFQCN, LocationAwareLogger.INFO_INT, msg, null, null); 73 | } 74 | 75 | @Override 76 | public void debug(String msg) { 77 | debugCount++; 78 | log.log(null, callerFQCN, LocationAwareLogger.DEBUG_INT, msg, null, null); 79 | } 80 | 81 | @Override 82 | public void debug(String msg, Throwable e) { 83 | debugCount++; 84 | log.log(null, callerFQCN, LocationAwareLogger.ERROR_INT, msg, null, e); 85 | } 86 | 87 | @Override 88 | public boolean isWarnEnabled() { 89 | return log.isWarnEnabled(); 90 | } 91 | 92 | @Override 93 | public boolean isErrorEnabled() { 94 | return log.isErrorEnabled(); 95 | } 96 | 97 | @Override 98 | public void warn(String msg) { 99 | log.log(null, callerFQCN, LocationAwareLogger.WARN_INT, msg, null, null); 100 | warnCount++; 101 | } 102 | 103 | @Override 104 | public void warn(String msg, Throwable e) { 105 | log.log(null, callerFQCN, LocationAwareLogger.WARN_INT, msg, null, e); 106 | warnCount++; 107 | } 108 | 109 | @Override 110 | public int getErrorCount() { 111 | return errorCount; 112 | } 113 | 114 | @Override 115 | public int getWarnCount() { 116 | return warnCount; 117 | } 118 | 119 | @Override 120 | public int getInfoCount() { 121 | return infoCount; 122 | } 123 | 124 | public int getDebugCount() { 125 | return debugCount; 126 | } 127 | 128 | @Override 129 | public void resetStat() { 130 | errorCount = 0; 131 | warnCount = 0; 132 | infoCount = 0; 133 | debugCount = 0; 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/servlet/ApiJsonServlet.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.servlet; 2 | 3 | import com.github.xiaour.sapi.config.ApiServerAutoConfigure; 4 | import com.github.xiaour.sapi.config.SapiGroupManager; 5 | import com.github.xiaour.sapi.dto.ApiInfo; 6 | import com.github.xiaour.sapi.util.SapiJsonUtil; 7 | 8 | import javax.el.MethodNotFoundException; 9 | import javax.servlet.annotation.WebServlet; 10 | import javax.servlet.http.HttpServlet; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.io.IOException; 14 | import java.io.PrintWriter; 15 | import java.util.*; 16 | 17 | import com.github.xiaour.sapi.config.SapiFactoryAutoConfigure; 18 | 19 | @WebServlet(name = "ApiJsonServlet", urlPatterns = {"/sapidata/*"}) 20 | public class ApiJsonServlet extends HttpServlet { 21 | 22 | private static Integer pageSize=10; 23 | 24 | @Override 25 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 26 | doPost(request,response); 27 | } 28 | 29 | @Override 30 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { 31 | resp.setContentType("application/json;charset=utf-8");//指定返回的格式为JSON格式 32 | 33 | resp.setCharacterEncoding("UTF-8");//setContentType与setCharacterEncoding的顺序不能调换,否则还是无法解决中文乱码的问题 34 | 35 | String url=req.getRequestURI(); 36 | 37 | if(!ApiServerAutoConfigure.apiRouter.contains(url)){ 38 | throw new MethodNotFoundException(); 39 | } 40 | if(url.equals(ApiServerAutoConfigure.getContextPath()+"/sapidata/apiList")) { 41 | getApiList(req, resp); 42 | } 43 | if(url.equals(ApiServerAutoConfigure.getContextPath()+"/sapidata/group")){ 44 | getApiGroup(req,resp); 45 | } 46 | } 47 | 48 | private void getApiList(HttpServletRequest request, HttpServletResponse response) throws IOException { 49 | Integer pageNum; 50 | 51 | int totalPage = -1; 52 | 53 | Map map = new HashMap(); 54 | 55 | if(request.getParameter("pageNum")!=null){ 56 | 57 | pageNum = Integer.parseInt(request.getParameter("pageNum")); 58 | 59 | totalPage = getPageCount(pageSize, SapiFactoryAutoConfigure.simpleApiList.size()); 60 | 61 | map.put("apiList",getListByPage(pageSize,pageNum,SapiFactoryAutoConfigure.simpleApiList)); 62 | }else{ 63 | 64 | map.put("apiList", SapiFactoryAutoConfigure.simpleApiList); 65 | 66 | } 67 | map.put("totalPage",totalPage); 68 | 69 | PrintWriter out=response.getWriter() ; 70 | 71 | out.write(SapiJsonUtil.mapJsonUtil(map)); 72 | 73 | out.close(); 74 | } 75 | 76 | private void getApiGroup(HttpServletRequest request, HttpServletResponse response) throws IOException { 77 | 78 | Map map = new HashMap(); 79 | 80 | Set group = SapiGroupManager.getSapiGroup(); 81 | 82 | List apiList = SapiFactoryAutoConfigure.simpleApiList; 83 | 84 | Map childMap; 85 | 86 | for(String title:group){ 87 | childMap = new HashMap(); 88 | for(ApiInfo apiInfo:apiList){ 89 | if(apiInfo.getGroupTitle().equals(title)){ 90 | childMap.put(apiInfo.getUrl(),apiInfo); 91 | } 92 | } 93 | map.put(title,childMap); 94 | } 95 | 96 | Map resultMap = new HashMap(); 97 | resultMap.put("total",apiList.size()); 98 | resultMap.put("totalModule",group.size()); 99 | resultMap.put("data",map); 100 | PrintWriter out=response.getWriter() ; 101 | out.write(SapiJsonUtil.mapJsonUtil(resultMap)); 102 | out.close(); 103 | } 104 | 105 | 106 | public static List getListByPage(Integer pageSize, Integer pageNum, List list){ 107 | 108 | int size = list.size(); 109 | 110 | int pageCount=getPageCount(pageSize,size); 111 | 112 | int fromIndex = pageSize * (pageNum - 1); 113 | 114 | int toIndex = fromIndex + pageSize; 115 | 116 | if (toIndex >= size) { 117 | 118 | toIndex = size; 119 | } 120 | if(pageNum>pageCount+1){ 121 | fromIndex=0; 122 | toIndex=0; 123 | } 124 | 125 | if(fromIndex>size){ 126 | getListByPage(pageSize,pageNum-1,list); 127 | } 128 | return list.subList(fromIndex, toIndex); 129 | } 130 | 131 | 132 | public static Integer getPageCount(Integer pageSize,Integer total){ 133 | int pageCount; 134 | 135 | total=total<=0?1:total; 136 | 137 | if(pageSize%total>0){ 138 | 139 | pageCount=total/pageSize+1; 140 | 141 | }else{ 142 | pageCount=total/pageSize; 143 | } 144 | 145 | return pageCount; 146 | } 147 | 148 | 149 | 150 | 151 | } 152 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/servlet/ApiViewServlet.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.servlet; 2 | 3 | import com.github.xiaour.sapi.config.ApiServerAutoConfigure; 4 | import com.github.xiaour.sapi.logging.Log; 5 | import com.github.xiaour.sapi.logging.LogFactory; 6 | import com.github.xiaour.sapi.util.Utils; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 8 | 9 | import javax.servlet.ServletException; 10 | import javax.servlet.annotation.WebServlet; 11 | import javax.servlet.http.HttpServlet; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.io.IOException; 15 | 16 | 17 | @WebServlet(name = "ApiViewServlet", urlPatterns = {"/sapi/*"}) 18 | @ConditionalOnProperty(name = "spring.sapi.enable", havingValue = "true", matchIfMissing = true) 19 | public class ApiViewServlet extends HttpServlet { 20 | 21 | private final static Log LOG = LogFactory.getLog(ApiViewServlet.class); 22 | 23 | protected final String resourcePath; 24 | 25 | public ApiViewServlet(){ 26 | this.resourcePath = "support/http"; 27 | } 28 | 29 | @Override 30 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { 31 | doPost(request,response); 32 | } 33 | 34 | @Override 35 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) 36 | throws ServletException, IOException { 37 | 38 | resp.setContentType("application/json;charset=utf-8");//指定返回的格式为JSON格式 39 | 40 | resp.setCharacterEncoding("UTF-8");//setContentType与setCharacterEncoding的顺序不能调换,否则还是无法解决中文乱码的问题 41 | 42 | String url = req.getRequestURI(); 43 | 44 | if(url.equals(ApiServerAutoConfigure.getContextPath()+"/sapi/list".replaceAll("//","/"))) { 45 | 46 | list(req, resp); 47 | }else { 48 | index(req,resp); 49 | } 50 | } 51 | 52 | private void list(HttpServletRequest request, HttpServletResponse response) throws IOException { 53 | String contextPath = request.getContextPath(); 54 | String servletPath = request.getServletPath(); 55 | 56 | response.setCharacterEncoding("utf-8"); 57 | 58 | if (contextPath == null) { // root context 59 | contextPath = ""; 60 | } 61 | String uri = contextPath + servletPath; 62 | String path = "/list.html"; 63 | 64 | try { 65 | returnResourceFile(path, uri, response); 66 | } catch (ServletException e) { 67 | LOG.error("Sapi init exception:",e); 68 | } 69 | } 70 | 71 | private void index(HttpServletRequest request, HttpServletResponse response) throws IOException { 72 | String contextPath = request.getContextPath(); 73 | String servletPath = request.getServletPath(); 74 | String requestURI = request.getRequestURI(); 75 | 76 | response.setCharacterEncoding("utf-8"); 77 | 78 | if (contextPath == null) { // root context 79 | contextPath = ""; 80 | } 81 | String uri = contextPath + servletPath; 82 | String path = requestURI.substring(contextPath.length() + servletPath.length()); 83 | 84 | if ("/".equals(path)||"".equals(path)) { 85 | path="/index.html"; 86 | } 87 | 88 | try { 89 | returnResourceFile(path, uri, response); 90 | } catch (ServletException e) { 91 | LOG.error("Sapi init exception:",e); 92 | } 93 | } 94 | 95 | protected void returnResourceFile(String fileName, String uri, HttpServletResponse response) 96 | throws ServletException, 97 | IOException { 98 | 99 | String filePath = getFilePath(fileName); 100 | 101 | if (filePath.endsWith(".html")) { 102 | response.setContentType("text/html; charset=utf-8"); 103 | } 104 | if (fileName.endsWith(".jpg")) { 105 | byte[] bytes = Utils.readByteArrayFromResource(filePath); 106 | if (bytes != null) { 107 | response.getOutputStream().write(bytes); 108 | } 109 | 110 | return; 111 | } 112 | 113 | String text = Utils.readFromResource(filePath); 114 | if (text == null) { 115 | response.sendRedirect(uri + "/index.html"); 116 | return; 117 | } 118 | if (fileName.endsWith(".css")) { 119 | response.setContentType("text/css;charset=utf-8"); 120 | 121 | } else if (fileName.endsWith(".js")) { 122 | response.setContentType("text/javascript;charset=utf-8"); 123 | } 124 | response.getWriter().write(text); 125 | } 126 | 127 | protected String getFilePath(String fileName) { 128 | return resourcePath + fileName; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/servlet/FaqServlet.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.servlet; 2 | 3 | import com.github.xiaour.sapi.logging.Log; 4 | import com.github.xiaour.sapi.logging.LogFactory; 5 | import com.github.xiaour.sapi.util.Utils; 6 | 7 | import javax.servlet.ServletException; 8 | import javax.servlet.annotation.WebServlet; 9 | import javax.servlet.http.HttpServlet; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | 14 | @WebServlet(name = "FaqServlet", urlPatterns = {"/faq"}) 15 | public class FaqServlet extends HttpServlet { 16 | 17 | private final static Log LOG = LogFactory.getLog(FaqServlet.class); 18 | 19 | 20 | protected final String resourcePath; 21 | 22 | 23 | public FaqServlet(){ 24 | this.resourcePath = "support/http"; 25 | } 26 | 27 | @Override 28 | protected void doGet(HttpServletRequest request, HttpServletResponse response) 29 | throws IOException { 30 | 31 | 32 | String contextPath = request.getContextPath(); 33 | String servletPath = request.getServletPath(); 34 | String requestURI = request.getRequestURI(); 35 | 36 | response.setCharacterEncoding("utf-8"); 37 | 38 | if (contextPath == null) { // root context 39 | contextPath = ""; 40 | } 41 | String uri = contextPath + servletPath; 42 | String path = requestURI.substring(contextPath.length() + servletPath.length()); 43 | 44 | if ("/".equals(path)||"".equals(path)) { 45 | path="/faq.html"; 46 | } 47 | 48 | try { 49 | returnResourceFile(path, uri, response); 50 | } catch (ServletException e) { 51 | LOG.error("Sapi init exception:",e); 52 | 53 | } 54 | } 55 | 56 | @Override 57 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) 58 | throws ServletException, IOException { 59 | doGet(req,resp); 60 | } 61 | 62 | protected void returnResourceFile(String fileName, String uri, HttpServletResponse response) 63 | throws ServletException, 64 | IOException { 65 | 66 | String filePath = getFilePath(fileName); 67 | 68 | if (filePath.endsWith(".html")) { 69 | response.setContentType("text/html; charset=utf-8"); 70 | } 71 | if (fileName.endsWith(".jpg")) { 72 | byte[] bytes = Utils.readByteArrayFromResource(filePath); 73 | if (bytes != null) { 74 | response.getOutputStream().write(bytes); 75 | } 76 | 77 | return; 78 | } 79 | 80 | String text = Utils.readFromResource(filePath); 81 | if (text == null) { 82 | response.sendRedirect(uri + "/index.html"); 83 | return; 84 | } 85 | if (fileName.endsWith(".css")) { 86 | response.setContentType("text/css;charset=utf-8"); 87 | 88 | } else if (fileName.endsWith(".js")) { 89 | response.setContentType("text/javascript;charset=utf-8"); 90 | } 91 | response.getWriter().write(text); 92 | } 93 | 94 | protected String getFilePath(String fileName) { 95 | return resourcePath + fileName; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/servlet/SapiStaticServlet.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.servlet; 2 | 3 | import com.github.xiaour.sapi.logging.Log; 4 | import com.github.xiaour.sapi.logging.LogFactory; 5 | import com.github.xiaour.sapi.util.Utils; 6 | 7 | import javax.servlet.ServletException; 8 | import javax.servlet.annotation.WebServlet; 9 | import javax.servlet.http.HttpServlet; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | 14 | 15 | @WebServlet(name = "sapistatic", urlPatterns = {"/sapistatic"}) 16 | public class SapiStaticServlet extends HttpServlet { 17 | 18 | private final static Log LOG = LogFactory.getLog(SapiStaticServlet.class); 19 | 20 | 21 | protected final String resourcePath; 22 | 23 | 24 | public SapiStaticServlet(){ 25 | this.resourcePath = "support/http"; 26 | } 27 | 28 | @Override 29 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 30 | 31 | String contextPath = request.getContextPath(); 32 | 33 | String servletPath = request.getServletPath(); 34 | 35 | response.setCharacterEncoding("utf-8"); 36 | 37 | if (contextPath == null) { // root context 38 | contextPath = ""; 39 | } 40 | 41 | String resourceFile=request.getParameter("s"); 42 | 43 | String uri = contextPath + servletPath; 44 | 45 | try { 46 | returnResourceFile(resourceFile, uri, response); 47 | } catch (ServletException e) { 48 | LOG.error("Sapi init exception:",e); 49 | } 50 | } 51 | 52 | @Override 53 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) 54 | throws ServletException, IOException { 55 | doGet(req,resp); 56 | } 57 | 58 | protected void returnResourceFile(String fileName, String uri, HttpServletResponse response) 59 | throws ServletException, IOException { 60 | 61 | String filePath = new ApiViewServlet().getFilePath(fileName); 62 | 63 | if (filePath.endsWith(".html")) { 64 | response.setContentType("text/html; charset=utf-8"); 65 | } 66 | 67 | String text = Utils.readFromResource(filePath); 68 | if (text == null) { 69 | response.sendRedirect(uri + "/index.html"); 70 | return; 71 | } 72 | if (fileName.endsWith(".css")) { 73 | response.setContentType("text/css;charset=utf-8"); 74 | 75 | } else if (fileName.endsWith(".js")) { 76 | response.setContentType("text/javascript;charset=utf-8"); 77 | } 78 | response.getWriter().write(text); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/util/ClassScaner.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.util; 2 | 3 | import org.springframework.beans.factory.BeanDefinitionStoreException; 4 | import org.springframework.context.ResourceLoaderAware; 5 | import org.springframework.core.io.Resource; 6 | import org.springframework.core.io.ResourceLoader; 7 | import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 8 | import org.springframework.core.io.support.ResourcePatternResolver; 9 | import org.springframework.core.io.support.ResourcePatternUtils; 10 | import org.springframework.core.type.classreading.CachingMetadataReaderFactory; 11 | import org.springframework.core.type.classreading.MetadataReader; 12 | import org.springframework.core.type.classreading.MetadataReaderFactory; 13 | import org.springframework.core.type.filter.AnnotationTypeFilter; 14 | import org.springframework.core.type.filter.TypeFilter; 15 | import org.springframework.util.StringUtils; 16 | import org.springframework.util.SystemPropertyUtils; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.lang.annotation.Annotation; 21 | import java.util.HashSet; 22 | import java.util.LinkedList; 23 | import java.util.List; 24 | import java.util.Set; 25 | 26 | public class ClassScaner implements ResourceLoaderAware { 27 | 28 | //保存过滤规则要排除的注解 29 | private final List includeFilters = new LinkedList(); 30 | 31 | private final List excludeFilters = new LinkedList(); 32 | 33 | private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); 34 | 35 | private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver); 36 | 37 | /** 38 | * 扫描JAVA类 39 | * @param basePackages 40 | * @param annotations 41 | * @return 42 | */ 43 | public static Set scan(String[] basePackages, 44 | Class... annotations) { 45 | ClassScaner cs = new ClassScaner(); 46 | 47 | if(annotations!=null) { 48 | for (Class anno : annotations) { 49 | cs.addIncludeFilter(new AnnotationTypeFilter(anno)); 50 | } 51 | } 52 | 53 | Set classes = new HashSet(); 54 | for (String s : basePackages) 55 | classes.addAll(cs.doScan(s)); 56 | return classes; 57 | } 58 | 59 | public static Set scan(String basePackages, Class... annotations) { 60 | return ClassScaner.scan(StringUtils.tokenizeToStringArray(basePackages, ",; \t\n"), annotations); 61 | } 62 | 63 | public final ResourceLoader getResourceLoader() { 64 | return this.resourcePatternResolver; 65 | } 66 | 67 | public void setResourceLoader(ResourceLoader resourceLoader) { 68 | this.resourcePatternResolver = ResourcePatternUtils 69 | .getResourcePatternResolver(resourceLoader); 70 | this.metadataReaderFactory = new CachingMetadataReaderFactory( 71 | resourceLoader); 72 | } 73 | 74 | public void addIncludeFilter(TypeFilter includeFilter) { 75 | this.includeFilters.add(includeFilter); 76 | } 77 | 78 | 79 | public Set doScan(String basePackage) { 80 | Set classes = new HashSet(); 81 | try { 82 | String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX 83 | + org.springframework.util.ClassUtils 84 | .convertClassNameToResourcePath(SystemPropertyUtils 85 | .resolvePlaceholders(basePackage)) 86 | + "/**/*.class"; 87 | Resource[] resources = this.resourcePatternResolver 88 | .getResources(packageSearchPath); 89 | 90 | for (int i = 0; i < resources.length; i++) { 91 | Resource resource = resources[i]; 92 | if (resource.isReadable()) { 93 | MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); 94 | if ((includeFilters.size() == 0 && excludeFilters.size() == 0) 95 | || matches(metadataReader)) { 96 | try { 97 | String simpleName=metadataReader.getClassMetadata().getClassName(); 98 | if(simpleName.indexOf("$")>=0){ 99 | simpleName=simpleName.substring(0,simpleName.indexOf("$")); 100 | } 101 | classes.add(Class.forName(simpleName)); 102 | } catch (ClassNotFoundException e) { 103 | e.printStackTrace(); 104 | } 105 | } 106 | } 107 | } 108 | } catch (IOException ex) { 109 | throw new BeanDefinitionStoreException( 110 | "I/O failure during classpath scanning", ex); 111 | } 112 | return classes; 113 | } 114 | 115 | protected boolean matches(MetadataReader metadataReader) throws IOException { 116 | for (TypeFilter tf : this.excludeFilters) { 117 | if (tf.match(metadataReader, this.metadataReaderFactory)) { 118 | return false; 119 | } 120 | } 121 | for (TypeFilter tf : this.includeFilters) { 122 | if (tf.match(metadataReader, this.metadataReaderFactory)) { 123 | return true; 124 | } 125 | } 126 | return false; 127 | } 128 | 129 | 130 | public static Set getClassName(String filePath) throws ClassNotFoundException { 131 | String flag= File.separator; 132 | Set classes= new HashSet(); 133 | filePath = ClassLoader.getSystemResource("").getPath() + filePath.replace(".", "/"); 134 | File file = new File(filePath); 135 | 136 | File[] childFiles = file.listFiles(); 137 | if(childFiles==null){ 138 | throw new ClassNotFoundException(filePath+" is not found,Please check the aplication's annotation in @Sapi(controllers) "); 139 | } 140 | for (File childFile : childFiles) { 141 | String childFilePath = childFile.getPath(); 142 | childFilePath = childFilePath.substring(childFilePath.indexOf(flag+"classes") + 9,childFilePath.length()); 143 | childFilePath=childFilePath.replaceAll(".class",""); 144 | childFilePath=childFilePath.replaceAll(flag,"."); 145 | classes.add(Class.forName(childFilePath)); 146 | } 147 | 148 | return classes; 149 | } 150 | 151 | } -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/util/SapiJsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.util; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.Method; 5 | import java.lang.reflect.Modifier; 6 | import java.text.SimpleDateFormat; 7 | import java.util.*; 8 | 9 | 10 | public class SapiJsonUtil { 11 | 12 | //Object类型类型起始字符 13 | private static final String OBJ_START = "{"; 14 | 15 | //Object类型类型结束字符 16 | private static final String OBJ_END = "}"; 17 | 18 | //数组类型类型起始字符 19 | private static final String ARRAY_START = "["; 20 | 21 | //数组类型类型结束字符 22 | private static final String ARRAY_END = "]"; 23 | 24 | //key-value/field-fieldValue分割符 25 | private static final String KEY_SPLIT = ":"; 26 | 27 | //元素分割符 28 | private static final String ELEM_SPLIT = ","; 29 | 30 | //日期格式默认yyyy-MM-dd HH:mm:ss 31 | private static String dateFormat = "yyyy-MM-dd HH:mm:ss"; 32 | 33 | //设置日期格式 34 | public static void setDateFormat(String dateFormat) { 35 | SapiJsonUtil.dateFormat = dateFormat; 36 | } 37 | 38 | //日期按格式输出的字符串 39 | public static String dateToString(Date date){ 40 | SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); 41 | return sdf.format(date); 42 | } 43 | 44 | //Object类型(对象\Map)拼接的开始 45 | private static StringBuffer objStart(){ 46 | return new StringBuffer(OBJ_START); 47 | } 48 | 49 | //Object类型(对象\Map)拼接的结束 50 | private static String objEnd(StringBuffer sb){ 51 | if(sb.lastIndexOf(ELEM_SPLIT) != -1){ 52 | sb.deleteCharAt(sb.lastIndexOf(ELEM_SPLIT)); 53 | } 54 | return sb.append(OBJ_END).toString(); 55 | } 56 | 57 | //Array类型(集合\数组)拼接的开始 58 | private static StringBuffer arrayStart(){ 59 | return new StringBuffer(ARRAY_START); 60 | } 61 | 62 | //Array类型(集合\数组)拼接的结束 63 | private static String arrayEnd(StringBuffer sb){ 64 | if(sb.lastIndexOf(ELEM_SPLIT) != -1){ 65 | sb.deleteCharAt(sb.lastIndexOf(ELEM_SPLIT)); 66 | } 67 | return sb.append(ARRAY_END).toString(); 68 | } 69 | 70 | //拼接Map中的key/Object中的属性名---->"key": 71 | private static String appendKey(String key){ 72 | StringBuffer sb = new StringBuffer(); 73 | return sb.append("\"").append(key).append("\"").append(KEY_SPLIT).toString(); 74 | } 75 | 76 | 77 | //拼接Map中的value/Object中的属性值 78 | private static String appendValue(Object obj){ 79 | 80 | if(obj == null){ 81 | return null; 82 | } 83 | 84 | StringBuffer sb = objStart(); 85 | 86 | //获取对象属性 87 | Class oClass = obj.getClass(); 88 | Field[] fields = oClass.getDeclaredFields(); 89 | 90 | //判断对象有没有属性 91 | if(fields.length <= 0){ 92 | sb.append("null"); 93 | }else{ 94 | for (Field field : fields) { 95 | 96 | //获取私有属性 97 | field.setAccessible(true); 98 | String fieldName = field.getName();//属性名 99 | 100 | //判断有没有get方法 101 | //1 字符串首字母大写 102 | char[] ch = fieldName.toCharArray(); 103 | if(ch[0] >= 'a' && ch[0] <= 'z'){ 104 | ch[0] = (char)(ch[0] - 32); 105 | } 106 | String str = "get" + new String(ch); 107 | 108 | //2 判断方法名存在 109 | Method method = null; 110 | try { 111 | //获取无参的公开方法 112 | method = oClass.getMethod(str, null); 113 | } catch (Exception e1) { 114 | //e1.printStackTrace(); 115 | //异常处理!!! 116 | throw new RuntimeException("没有方法:"+str+"() "+e1.getLocalizedMessage()); 117 | } 118 | 119 | //该属性有get方法 120 | if(method != null){ 121 | //3 判断方法修饰符是否为公开方法 122 | if(Modifier.isPublic(method.getModifiers())){ 123 | //获取属性值 124 | Object fieldValue = null; 125 | try { 126 | fieldValue = field.get(obj);//获取属性值 127 | } catch (Exception e) { 128 | e.printStackTrace(); 129 | } 130 | 131 | //append fieldName 132 | sb.append(appendKey(fieldName)); 133 | //append fieldValue 134 | sb.append(objectJsonUtil(fieldValue)); 135 | sb.append(ELEM_SPLIT); 136 | } 137 | } 138 | 139 | } 140 | 141 | } 142 | return objEnd(sb); 143 | } 144 | 145 | public static String collectionJsonUtil(Collection collection){ 146 | if(collection == null){ 147 | return null; 148 | } 149 | StringBuffer sb = arrayStart(); 150 | //遍历集合中元素 151 | for (Object object : collection) { 152 | //append object by type 153 | sb.append(objectJsonUtil(object)); 154 | sb.append(ELEM_SPLIT); 155 | } 156 | return arrayEnd(sb); 157 | } 158 | 159 | public static String collectionJsonUtil(Collection collection, boolean boo){ 160 | String collectionJsonUtil = collectionJsonUtil(collection); 161 | if(boo&&collectionJsonUtil!=null){ 162 | return jsonFormat(collectionJsonUtil); 163 | } 164 | return collectionJsonUtil; 165 | } 166 | 167 | public static String arrayJsonUtil(Object[] oobj){ 168 | //oobj为空直接返回null 169 | if(oobj == null){ 170 | return null; 171 | } 172 | StringBuffer sb = arrayStart(); 173 | for (Object object : oobj) { 174 | sb.append(objectJsonUtil(object)); 175 | sb.append(ELEM_SPLIT); 176 | } 177 | return arrayEnd(sb); 178 | } 179 | 180 | public static String arrayJsonUtil(Object[] oobj, boolean boo){ 181 | String arrayJsonUtil = arrayJsonUtil(oobj); 182 | if(boo&&arrayJsonUtil!=null){ 183 | return jsonFormat(arrayJsonUtil); 184 | } 185 | return arrayJsonUtil; 186 | } 187 | 188 | 189 | public static String objectJsonUtil(Object obj) { 190 | 191 | if(obj == null){ 192 | return null; 193 | } 194 | 195 | //获取obj对象的Class对象 196 | Class oClass = obj.getClass(); 197 | 198 | StringBuffer sb = new StringBuffer(); 199 | 200 | //根据类型做不同处理 201 | if(String.class.isAssignableFrom(oClass)){ 202 | //String 203 | //解决不转义 204 | String string = charSequence(obj); 205 | sb.append("\"").append(string).append("\""); 206 | }else if(Map.class.isAssignableFrom(oClass)){ 207 | //Map 208 | sb.append(mapJsonUtil((Map) obj)); 209 | }else if(Collection.class.isAssignableFrom(oClass)){ 210 | //Collection 211 | sb.append(collectionJsonUtil((Collection) obj)); 212 | }else if(Integer.class.isAssignableFrom(oClass) || 213 | Double.class.isAssignableFrom(oClass) || 214 | Boolean.class.isAssignableFrom(oClass) || 215 | Long.class.isAssignableFrom(oClass) || 216 | Byte.class.isAssignableFrom(oClass) || 217 | Short.class.isAssignableFrom(oClass) || 218 | Float.class.isAssignableFrom(oClass)){ 219 | //7种基本类型对应的包装类型 220 | sb.append(obj); 221 | }else if(Character.class.isAssignableFrom(oClass)){ 222 | //Character类型,输出ascii码? 223 | //int cha = ((Character)obj).charValue(); 224 | //解决不转义 225 | String string = charSequence(obj); 226 | sb.append("\"").append(string).append("\""); 227 | //转为字符串 or 16进制? 228 | }else if(oClass.isArray()){ 229 | //数组类型 230 | sb.append(arrayJsonUtil((Object[])obj)); 231 | }else if(Date.class.isAssignableFrom(oClass)){ 232 | //Date类型 233 | sb.append("\"").append(dateToString((Date)obj)).append("\""); 234 | }else{ 235 | //其他自定义类型 236 | sb.append(appendValue(obj)); 237 | } 238 | 239 | return sb.toString(); 240 | } 241 | 242 | private static String charSequence(Object obj){ 243 | String string = null; 244 | if(obj instanceof Character){ 245 | Character c = (Character) obj; 246 | string = String.valueOf(c); 247 | }else { 248 | string = (String) obj; 249 | } 250 | 251 | StringBuffer sb = new StringBuffer(); 252 | 253 | for(int i = 0; i < string.length(); i++){ 254 | char c = string.charAt(i); 255 | switch (c) { 256 | case '\"': 257 | sb.append("\\\""); 258 | break; 259 | case '\\': 260 | sb.append("\\\\"); 261 | break; 262 | case '\b': 263 | sb.append("\\b"); 264 | break; 265 | case '\f': 266 | sb.append("\\f"); 267 | break; 268 | case '\n': 269 | sb.append("\\n"); 270 | break; 271 | case '\r': 272 | sb.append("\\r"); 273 | break; 274 | case '\t': 275 | sb.append("\\t"); 276 | break; 277 | case '\'': 278 | sb.append("\\'"); 279 | break; 280 | default: 281 | sb.append(c); 282 | break; 283 | } 284 | } 285 | return sb.toString(); 286 | } 287 | 288 | public static String objectJsonUtil(Object obj, boolean boo){ 289 | String objectJsonUtil = objectJsonUtil(obj); 290 | if(boo&&objectJsonUtil!=null){ 291 | return jsonFormat(objectJsonUtil); 292 | } 293 | return objectJsonUtil; 294 | } 295 | 296 | public static String mapJsonUtil(Map map){ 297 | //map为空直接返回null 298 | if(map == null){ 299 | return null; 300 | } 301 | //append start 302 | StringBuffer sb = SapiJsonUtil.objStart(); 303 | //遍历map 304 | Set> entrySet = map.entrySet(); 305 | for (Map.Entry entry : entrySet) { 306 | //append key 307 | sb.append(SapiJsonUtil.appendKey(entry.getKey())); 308 | //获取map中value 309 | //append value 310 | sb.append(SapiJsonUtil.objectJsonUtil((entry.getValue()))); 311 | sb.append(ELEM_SPLIT); 312 | } 313 | return objEnd(sb); 314 | } 315 | 316 | public static String mapJsonUtil(Map map, boolean boo){ 317 | String mapJsonUtil = mapJsonUtil(map); 318 | if(boo&&mapJsonUtil!=null){ 319 | return jsonFormat(mapJsonUtil); 320 | } 321 | return mapJsonUtil; 322 | } 323 | 324 | public static String jsonFormat(String json){ 325 | StringBuffer sb = new StringBuffer(); 326 | int tabNum = 0; 327 | for(int index = 0; index < json.length(); index++){ 328 | char c = json.charAt(index); 329 | if(tabNum > 0 && '\n' == sb.charAt(sb.length()-1)){ 330 | sb.append(getLevelStr(tabNum)); 331 | } 332 | switch (c) { 333 | case '{': 334 | case '[': 335 | sb.append(c + "\n"); 336 | tabNum++; 337 | break; 338 | case ',': 339 | sb.append(c + "\n"); 340 | break; 341 | case '}': 342 | case ']': 343 | sb.append("\n"); 344 | tabNum--; 345 | sb.append(getLevelStr(tabNum)); 346 | sb.append(c); 347 | break; 348 | default: 349 | sb.append(c); 350 | break; 351 | } 352 | } 353 | return sb.toString(); 354 | } 355 | 356 | private static String getLevelStr(int tabNum){ 357 | StringBuffer sb = new StringBuffer(); 358 | for(int index = 0; index < tabNum; index++){ 359 | sb.append("\t"); 360 | } 361 | return sb.toString(); 362 | } 363 | 364 | public static List> jsonToArray(String jsonStr){ 365 | jsonStr=trimFirstAndLastChar(jsonStr,"["); 366 | jsonStr=trimFirstAndLastChar(jsonStr,"]"); 367 | List> dataList= new ArrayList>(); 368 | String [] jsonList=jsonStr.split(",\\{"); 369 | for(String s:jsonList){ 370 | dataList.add(stringToMap(s)); 371 | } 372 | return dataList; 373 | } 374 | 375 | private static Map stringToMap(String jsonObj){ 376 | // jsonStr=jsonStr.replaceAll("\\[","").replaceAll("\\]",""); 377 | Map map= new HashMap(); 378 | if(jsonObj.contains("[{")) { 379 | jsonObj = jsonObj.replaceAll("\\{", "").replaceAll("\\}", ""); 380 | } 381 | String [] keyValueArray=jsonObj.split(",\""); 382 | for(String kv:keyValueArray){ 383 | String [] kvArray=kv.split("\":"); 384 | map.put(kvArray[0].replaceAll("\"",""),kvArray[1].replaceAll("\"","")); 385 | } 386 | return map; 387 | } 388 | 389 | public static String trimFirstAndLastChar(String source,String element){ 390 | boolean beginIndexFlag = true; 391 | boolean endIndexFlag = true; 392 | do{ 393 | int beginIndex = source.indexOf(element) == 0 ? 1 : 0; 394 | int endIndex = source.lastIndexOf(element) + 1 == source.length() ? source.lastIndexOf(element) : source.length(); 395 | source = source.substring(beginIndex, endIndex); 396 | beginIndexFlag = (source.indexOf(element) == 0); 397 | endIndexFlag = (source.lastIndexOf(element) + 1 == source.length()); 398 | } while (beginIndexFlag || endIndexFlag); 399 | return source; 400 | } 401 | 402 | 403 | public List> splitList(List list, int pageSize) 404 | { 405 | 406 | int listSize = list.size(); 407 | int page = (listSize + (pageSize - 1)) / pageSize; 408 | 409 | List> listArray = new ArrayList>(); 410 | for (int i = 0; i < page; i++) 411 | { 412 | List subList = new ArrayList(); 413 | for (int j = 0; j < listSize; j++) 414 | { 415 | int pageIndex = ((j + 1) + (pageSize - 1)) / pageSize; 416 | if (pageIndex == (i + 1)) 417 | { 418 | subList.add(list.get(j)); 419 | } 420 | if ((j + 1) == ((j + 1) * pageSize)) 421 | { 422 | break; 423 | } 424 | } 425 | listArray.add(subList); 426 | } 427 | return listArray; 428 | } 429 | 430 | 431 | } 432 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/java/com/github/xiaour/sapi/util/Utils.java: -------------------------------------------------------------------------------- 1 | package com.github.xiaour.sapi.util; 2 | import java.io.*; 3 | import java.lang.management.ManagementFactory; 4 | import java.security.MessageDigest; 5 | import java.security.NoSuchAlgorithmException; 6 | import java.sql.*; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | import java.util.Properties; 10 | import java.util.Set; 11 | 12 | public class Utils { 13 | public final static int DEFAULT_BUFFER_SIZE = 1024 * 4; 14 | 15 | public static String read(InputStream in) { 16 | InputStreamReader reader; 17 | try { 18 | reader = new InputStreamReader(in, "UTF-8"); 19 | } catch (UnsupportedEncodingException e) { 20 | throw new IllegalStateException(e.getMessage(), e); 21 | } 22 | return read(reader); 23 | } 24 | 25 | public static String readFromResource(String resource) throws IOException { 26 | InputStream in = null; 27 | try { 28 | in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); 29 | if (in == null) { 30 | in = Utils.class.getResourceAsStream(resource); 31 | } 32 | 33 | if (in == null) { 34 | return null; 35 | } 36 | 37 | String text = Utils.read(in); 38 | return text; 39 | } finally { 40 | close(in); 41 | } 42 | } 43 | 44 | public static byte[] readByteArrayFromResource(String resource) throws IOException { 45 | InputStream in = null; 46 | try { 47 | in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); 48 | if (in == null) { 49 | return null; 50 | } 51 | 52 | return readByteArray(in); 53 | } finally { 54 | close(in); 55 | } 56 | } 57 | 58 | public static byte[] readByteArray(InputStream input) throws IOException { 59 | ByteArrayOutputStream output = new ByteArrayOutputStream(); 60 | copy(input, output); 61 | return output.toByteArray(); 62 | } 63 | 64 | public static long copy(InputStream input, OutputStream output) throws IOException { 65 | final int EOF = -1; 66 | 67 | byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; 68 | 69 | long count = 0; 70 | int n = 0; 71 | while (EOF != (n = input.read(buffer))) { 72 | output.write(buffer, 0, n); 73 | count += n; 74 | } 75 | return count; 76 | } 77 | 78 | public static String read(Reader reader) { 79 | try { 80 | 81 | StringWriter writer = new StringWriter(); 82 | 83 | char[] buffer = new char[DEFAULT_BUFFER_SIZE]; 84 | int n = 0; 85 | while (-1 != (n = reader.read(buffer))) { 86 | writer.write(buffer, 0, n); 87 | } 88 | 89 | return writer.toString(); 90 | } catch (IOException ex) { 91 | throw new IllegalStateException("read error", ex); 92 | } 93 | } 94 | 95 | public static String read(Reader reader, int length) { 96 | try { 97 | char[] buffer = new char[length]; 98 | 99 | int offset = 0; 100 | int rest = length; 101 | int len; 102 | while ((len = reader.read(buffer, offset, rest)) != -1) { 103 | rest -= len; 104 | offset += len; 105 | 106 | if (rest == 0) { 107 | break; 108 | } 109 | } 110 | 111 | return new String(buffer, 0, length - rest); 112 | } catch (IOException ex) { 113 | throw new IllegalStateException("read error", ex); 114 | } 115 | } 116 | 117 | public static String toString(java.util.Date date) { 118 | if (date == null) { 119 | return null; 120 | } 121 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 122 | return format.format(date); 123 | } 124 | 125 | public static String getStackTrace(Throwable ex) { 126 | StringWriter buf = new StringWriter(); 127 | ex.printStackTrace(new PrintWriter(buf)); 128 | 129 | return buf.toString(); 130 | } 131 | 132 | public static String toString(StackTraceElement[] stackTrace) { 133 | StringBuilder buf = new StringBuilder(); 134 | for (StackTraceElement item : stackTrace) { 135 | buf.append(item.toString()); 136 | buf.append("\n"); 137 | } 138 | return buf.toString(); 139 | } 140 | 141 | public static Boolean getBoolean(Properties properties, String key) { 142 | String property = properties.getProperty(key); 143 | if ("true".equals(property)) { 144 | return Boolean.TRUE; 145 | } else if ("false".equals(property)) { 146 | return Boolean.FALSE; 147 | } 148 | return Boolean.FALSE; 149 | } 150 | 151 | public static Integer getInteger(Properties properties, String key) { 152 | String property = properties.getProperty(key); 153 | 154 | if (property == null) { 155 | return null; 156 | } 157 | try { 158 | return Integer.parseInt(property); 159 | } catch (NumberFormatException ex) { 160 | // skip 161 | } 162 | return null; 163 | } 164 | 165 | public static Long getLong(Properties properties, String key) { 166 | String property = properties.getProperty(key); 167 | 168 | if (property == null) { 169 | return null; 170 | } 171 | try { 172 | return Long.parseLong(property); 173 | } catch (NumberFormatException ex) { 174 | // skip 175 | } 176 | return null; 177 | } 178 | 179 | public static Class loadClass(String className) { 180 | Class clazz = null; 181 | 182 | if (className == null) { 183 | return null; 184 | } 185 | 186 | try { 187 | return Class.forName(className); 188 | } catch (ClassNotFoundException e) { 189 | // skip 190 | } 191 | 192 | ClassLoader ctxClassLoader = Thread.currentThread().getContextClassLoader(); 193 | if (ctxClassLoader != null) { 194 | try { 195 | clazz = ctxClassLoader.loadClass(className); 196 | } catch (ClassNotFoundException e) { 197 | // skip 198 | } 199 | } 200 | 201 | return clazz; 202 | } 203 | 204 | private static Date startTime; 205 | 206 | public final static Date getStartTime() { 207 | if (startTime == null) { 208 | startTime = new Date(ManagementFactory.getRuntimeMXBean().getStartTime()); 209 | } 210 | return startTime; 211 | } 212 | 213 | public static long murmurhash2_64(String text) { 214 | final byte[] bytes = text.getBytes(); 215 | return murmurhash2_64(bytes, bytes.length, 0xe17a1465); 216 | } 217 | 218 | public static long murmurhash2_64(final byte[] data, int length, int seed) { 219 | final long m = 0xc6a4a7935bd1e995L; 220 | final int r = 47; 221 | 222 | long h = (seed & 0xffffffffl) ^ (length * m); 223 | 224 | int length8 = length / 8; 225 | 226 | for (int i = 0; i < length8; i++) { 227 | final int i8 = i * 8; 228 | long k = ((long) data[i8 + 0] & 0xff) // 229 | + (((long) data[i8 + 1] & 0xff) << 8) // 230 | + (((long) data[i8 + 2] & 0xff) << 16)// 231 | + (((long) data[i8 + 3] & 0xff) << 24) // 232 | + (((long) data[i8 + 4] & 0xff) << 32)// 233 | + (((long) data[i8 + 5] & 0xff) << 40)// 234 | + (((long) data[i8 + 6] & 0xff) << 48) // 235 | + (((long) data[i8 + 7] & 0xff) << 56); 236 | 237 | k *= m; 238 | k ^= k >>> r; 239 | k *= m; 240 | 241 | h ^= k; 242 | h *= m; 243 | } 244 | 245 | switch (length % 8) { 246 | case 7: 247 | h ^= (long) (data[(length & ~7) + 6] & 0xff) << 48; 248 | case 6: 249 | h ^= (long) (data[(length & ~7) + 5] & 0xff) << 40; 250 | case 5: 251 | h ^= (long) (data[(length & ~7) + 4] & 0xff) << 32; 252 | case 4: 253 | h ^= (long) (data[(length & ~7) + 3] & 0xff) << 24; 254 | case 3: 255 | h ^= (long) (data[(length & ~7) + 2] & 0xff) << 16; 256 | case 2: 257 | h ^= (long) (data[(length & ~7) + 1] & 0xff) << 8; 258 | case 1: 259 | h ^= (long) (data[length & ~7] & 0xff); 260 | h *= m; 261 | } 262 | 263 | h ^= h >>> r; 264 | h *= m; 265 | h ^= h >>> r; 266 | 267 | return h; 268 | } 269 | 270 | public static byte[] md5Bytes(String text) { 271 | MessageDigest msgDigest = null; 272 | 273 | try { 274 | msgDigest = MessageDigest.getInstance("MD5"); 275 | } catch (NoSuchAlgorithmException e) { 276 | throw new IllegalStateException("System doesn't support MD5 algorithm."); 277 | } 278 | 279 | msgDigest.update(text.getBytes()); 280 | 281 | byte[] bytes = msgDigest.digest(); 282 | 283 | return bytes; 284 | } 285 | 286 | public static void putLong(byte[] b, int off, long val) { 287 | b[off + 7] = (byte) (val >>> 0); 288 | b[off + 6] = (byte) (val >>> 8); 289 | b[off + 5] = (byte) (val >>> 16); 290 | b[off + 4] = (byte) (val >>> 24); 291 | b[off + 3] = (byte) (val >>> 32); 292 | b[off + 2] = (byte) (val >>> 40); 293 | b[off + 1] = (byte) (val >>> 48); 294 | b[off + 0] = (byte) (val >>> 56); 295 | } 296 | 297 | public static boolean equals(Object a, Object b) { 298 | return (a == b) || (a != null && a.equals(b)); 299 | } 300 | 301 | public static String hex(int hash) { 302 | byte[] bytes = new byte[4]; 303 | 304 | bytes[3] = (byte) (hash ); 305 | bytes[2] = (byte) (hash >>> 8); 306 | bytes[1] = (byte) (hash >>> 16); 307 | bytes[0] = (byte) (hash >>> 24); 308 | 309 | 310 | char[] chars = new char[8]; 311 | for (int i = 0; i < 4; ++i) { 312 | byte b = bytes[i]; 313 | 314 | int a = b & 0xFF; 315 | int b0 = a >> 4; 316 | int b1 = a & 0xf; 317 | 318 | chars[i * 2] = (char) (b0 + (b0 < 10 ? 48 : 55)); 319 | chars[i * 2 + 1] = (char) (b1 + (b1 < 10 ? 48 : 55)); 320 | } 321 | 322 | return new String(chars); 323 | } 324 | 325 | public static String hex(long hash) { 326 | byte[] bytes = new byte[8]; 327 | 328 | bytes[7] = (byte) (hash ); 329 | bytes[6] = (byte) (hash >>> 8); 330 | bytes[5] = (byte) (hash >>> 16); 331 | bytes[4] = (byte) (hash >>> 24); 332 | bytes[3] = (byte) (hash >>> 32); 333 | bytes[2] = (byte) (hash >>> 40); 334 | bytes[1] = (byte) (hash >>> 48); 335 | bytes[0] = (byte) (hash >>> 56); 336 | 337 | char[] chars = new char[16]; 338 | for (int i = 0; i < 8; ++i) { 339 | byte b = bytes[i]; 340 | 341 | int a = b & 0xFF; 342 | int b0 = a >> 4; 343 | int b1 = a & 0xf; 344 | 345 | chars[i * 2] = (char) (b0 + (b0 < 10 ? 48 : 55)); 346 | chars[i * 2 + 1] = (char) (b1 + (b1 < 10 ? 48 : 55)); 347 | } 348 | 349 | return new String(chars); 350 | } 351 | 352 | public static String hex_t(long hash) { 353 | byte[] bytes = new byte[8]; 354 | 355 | bytes[7] = (byte) (hash ); 356 | bytes[6] = (byte) (hash >>> 8); 357 | bytes[5] = (byte) (hash >>> 16); 358 | bytes[4] = (byte) (hash >>> 24); 359 | bytes[3] = (byte) (hash >>> 32); 360 | bytes[2] = (byte) (hash >>> 40); 361 | bytes[1] = (byte) (hash >>> 48); 362 | bytes[0] = (byte) (hash >>> 56); 363 | 364 | char[] chars = new char[18]; 365 | chars[0] = 'T'; 366 | chars[1] = '_'; 367 | for (int i = 0; i < 8; ++i) { 368 | byte b = bytes[i]; 369 | 370 | int a = b & 0xFF; 371 | int b0 = a >> 4; 372 | int b1 = a & 0xf; 373 | 374 | chars[i * 2 + 2] = (char) (b0 + (b0 < 10 ? 48 : 55)); 375 | chars[i * 2 + 3] = (char) (b1 + (b1 < 10 ? 48 : 55)); 376 | } 377 | 378 | return new String(chars); 379 | } 380 | 381 | 382 | public static void loadFromFile(String path, Set set) { 383 | InputStream is = null; 384 | BufferedReader reader = null; 385 | try { 386 | is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path); 387 | reader = new BufferedReader(new InputStreamReader(is)); 388 | for (; ; ) { 389 | String line = reader.readLine(); 390 | if (line == null) { 391 | break; 392 | } 393 | 394 | line = line.trim().toLowerCase(); 395 | 396 | if (line.length() == 0) { 397 | continue; 398 | } 399 | set.add(line); 400 | } 401 | } catch (Exception ex) { 402 | // skip 403 | } finally { 404 | close(is); 405 | close(reader); 406 | } 407 | } 408 | 409 | 410 | 411 | public static void close(Connection x) { 412 | if (x == null) { 413 | return; 414 | } 415 | try { 416 | x.close(); 417 | } catch (Exception e) { 418 | e.printStackTrace(); 419 | } 420 | } 421 | 422 | public static void close(Statement x) { 423 | if (x == null) { 424 | return; 425 | } 426 | try { 427 | x.close(); 428 | } catch (Exception e) { 429 | e.printStackTrace(); 430 | } 431 | } 432 | 433 | public static void close(ResultSet x) { 434 | if (x == null) { 435 | return; 436 | } 437 | try { 438 | x.close(); 439 | } catch (Exception e) { 440 | e.printStackTrace(); 441 | } 442 | } 443 | 444 | public static void close(Closeable x) { 445 | if (x == null) { 446 | return; 447 | } 448 | 449 | try { 450 | x.close(); 451 | } catch (Exception e) { 452 | e.printStackTrace(); 453 | } 454 | } 455 | 456 | public static void close(Blob x) { 457 | if (x == null) { 458 | return; 459 | } 460 | 461 | try { 462 | x.free(); 463 | } catch (Exception e) { 464 | e.printStackTrace(); 465 | } 466 | } 467 | 468 | public static void close(Clob x) { 469 | if (x == null) { 470 | return; 471 | } 472 | 473 | try { 474 | x.free(); 475 | } catch (Exception e) { 476 | e.printStackTrace(); 477 | } 478 | } 479 | 480 | } 481 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": [ 3 | { 4 | "name": "spring.sapi.enable", 5 | "type": "java.lang.String", 6 | "sourceType": "com.github.xiaour.sapi.config.ApiServerAutoConfigure", 7 | "description": "Enable sapi (true/false) .", 8 | "defaultValue": true 9 | }, 10 | { 11 | "name": "spring.sapi.controllers", 12 | "type": "java.lang.String[]", 13 | "sourceType": "com.github.xiaour.sapi.config.ApiServerAutoConfigure", 14 | "description": "your project controllers folder path." 15 | } 16 | ] 17 | 18 | } -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.github.xiaour.sapi.config.SapiGroupManager,\ 3 | com.github.xiaour.sapi.config.SapiFactoryAutoConfigure,\ 4 | com.github.xiaour.sapi.config.ApiServerAutoConfigure 5 | 6 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/resources/support/http/css/sapi.css: -------------------------------------------------------------------------------- 1 | .btn.active,.btn:active:focus,.btn:focus {outline:medium} 2 | textarea,input,button{background:none;outline:medium;border:1px black solid;} 3 | .input_st {border: 0px;border-bottom: 1px solid #CCCCCC;background-color:#F8F8F8;} 4 | .input_title {min-width: 430px;} 5 | .input_st:hover {border: 1px;border-bottom: 1px solid #CCCCCC;background-color:#F8F8F8;} 6 | input::-webkit-input-placeholder{color:#DAA520;font-style: oblique;} 7 | input::-moz-placeholder{ /* Mozilla Firefox 19+ */color:#DAA520;font-style: oblique;} 8 | input:-moz-placeholder{ /* Mozilla Firefox 4 to 18 */color:#DAA520;font-style: oblique;} 9 | input:-ms-input-placeholder{ /* Internet Explorer 10-11 */color:#DAA520;font-style: oblique;} 10 | *{-webkit-border-radius: 0 !important;-moz-border-radius: 0 !important;border-radius: 0 !important;} 11 | 12 | .table>tbody>tr>td{ 13 | padding:3px;vertical-align: middle; 14 | } 15 | 16 | .codeData{min-height: 220px;max-height: 220px;width: 100%;border: 1px solid #CCC;background: #f5f5f5;overflow:auto} 17 | .codeData-mx{min-height:320px;max-height: 360px;width: 100%;border: 1px solid #CCC;background: #f5f5f5;overflow:auto} 18 | .form-control{border: 1px solid inherit;} 19 | .btn{border: 1px solid inherit;} 20 | .input-md {border: 1px solid #F8F8F8;} 21 | .modal{color: #696969} 22 | .jumbotron {padding-top: 15px;padding-bottom: 10px;margin-bottom: 10px;color: inherit;background-color: #F8F8F8} 23 | .badge{-webkit-border-radius: 50px !important;-moz-border-radius: 50px !important;border-radius: 50px !important;color: #FFF !important;} 24 | .clear-padding{padding-left: 0px;} 25 | .code-string{color:#993300;} 26 | .code-number{color:#cc00cc;} 27 | .code-boolean{color:#000033;} 28 | .code-null{color:magenta;} 29 | .code-key{color:#003377;font-weight:bold;} 30 | 31 | 32 | .ObjectBrace{color:#0a0;font-weight:bold}.ArrayBrace{color:#03f;font-weight:bold}.PropertyName{color:#c00;font-weight:bold}.String{color:#077}.Number{color:#a0a}.Boolean{color:#00f}.Function{color:#a63;}.Null{color:#00f}.Comma{color:#000;font-weight:bold}PRE.CodeContainer{margin-top:0;margin-bottom:0} 33 | body{width: 100%} 34 | 35 | /* Toggle Styles */ 36 | .main-nav{ 37 | position: static; 38 | transform: scale3d(1, 1, 1); 39 | top: 0; 40 | bottom:0; 41 | width: 100%; 42 | } 43 | 44 | 45 | .main-nav.nav-tabs.nav-stacked > li > a { 46 | font-size: 12px; 47 | font-weight: 600; 48 | color: #333; 49 | background: #E9E9E9; 50 | background: -moz-linear-gradient(top, #F8F8F8 0%, #E9E9E9 100%); 51 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#F8F8F8), color-stop(100%,#E9E9E9)); 52 | background: -webkit-linear-gradient(top, #F8F8F8 0%,#E9E9E9 100%); 53 | background: -o-linear-gradient(top, #F8F8F8 0%,#E9E9E9 100%); 54 | background: -ms-linear-gradient(top, #F8F8F8 0%,#E9E9E9 100%); 55 | background: linear-gradient(top, #F8F8F8 0%,#E9E9E9 100%); 56 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F8F8F8', endColorstr='#E9E9E9'); 57 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#F8F8F8', endColorstr='#E9E9E9')"; 58 | border: 1px solid #D5D5D5; 59 | } 60 | 61 | .main-nav.nav-tabs.nav-stacked > li > a > span { 62 | color: #333; 63 | } 64 | 65 | 66 | #main-nav.nav-tabs.nav-stacked > li.active > a, #main-nav.nav-tabs.nav-stacked > li > a:hover > span { 67 | color: #FFF; 68 | } 69 | 70 | .nav-tabs > li > a {margin-right: 0px} 71 | 72 | .nav-header.collapsed > span.glyphicon-chevron-toggle:before {content: "\e114";} 73 | 74 | .nav-header > span.glyphicon-chevron-toggle:before {content: "\e113";} 75 | 76 | .main-nav ul{border: 1px solid #CCCCCC;} 77 | 78 | .secondmenu a { 79 | font-size: 12px; 80 | color: #333; 81 | } 82 | 83 | .secondmenu > li > a:hover { 84 | background-color: #6f7782; 85 | border-color: #428bca; 86 | color: #fff; 87 | } 88 | 89 | .secondmenu li.active { 90 | background-color: #6f7782; 91 | border-color: #428bca; 92 | } 93 | 94 | .secondmenu li.active > a { 95 | color: #ffffff; 96 | } 97 | 98 | .navbar-static-top { 99 | background-color: #212121; 100 | margin-bottom: 5px; 101 | } 102 | 103 | .collapsed.glyphicon-chevron-toggle:before { 104 | content: "\e114"; 105 | } 106 | 107 | 108 | .card { 109 | width: 100%; 110 | height: 100%; 111 | margin: 0px auto; 112 | text-align: center; 113 | color: white; 114 | } 115 | 116 | .header { 117 | color: #303030; 118 | font-size: 1em; 119 | height:50%; 120 | font-size: 40px; 121 | } 122 | 123 | .tips { 124 | background-color: #303030; 125 | height:50%; 126 | line-height:30px; 127 | } 128 | 129 | .rowCard{margin-left: 10px} -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/resources/support/http/faq.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SAPI. A simple api scanner 5 | 6 | 7 | 8 | 9 | 10 | 22 | 23 | 24 | ​ 37 | 38 |
39 |
40 |
41 | 42 |
43 |
44 |

SAPI是什么?

45 |
46 |
47 | SAPI是一个基于Spring的帮助开发人员扫描Restful接口的小工具,只需要一行配置,就可以看到相应的接口以及参数。 48 |
49 |
50 | 51 |
52 |
53 |

SAPI如何测试?

54 |
55 |
56 | 打开了http://127.0.0.1:{your.port}/{your.contextPath}/sapi后,在每个接口右侧页面上可以看到一个绿色的三角按钮()。对相关参数进行赋值后点击就可以看到相关的输出结果了。 59 |
60 |
61 | 62 |
63 |
64 |

为何接口扫描出来了,但是测试时提示404?

65 |
66 |
67 | 很可能您使用了@Controller,如果不需要使用Thymeleaf等系列模板,建议使用@RestController进行测试。 68 |
69 |
70 | 71 |
72 |
73 |

如何获取源码?

74 |
75 |
76 | Sapi是一个开源的组件,任何人都可以在Apache License 2.0的规范下修改源码。https://github.com/xiaour/spring.boot.sapi.starter,同时也欢迎各位开发者Star ^_^ 77 |
78 |
79 | 80 |
81 |
82 |

提Bug或issues

83 |
84 |
85 | 请打开链接反馈问题。 86 |
87 |
88 | 89 |
90 |
91 | 92 | 93 |
94 | 95 |
96 |
97 |
98 | SAPI is a simple api starter for spring 99 |
SAPI V1.4 ©️ xiaour
100 |
101 |
102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/resources/support/http/image/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaour/spring.boot.sapi.starter/5e9aae7cdc2def1a4ab54462b7ddac12a0f52832/spring.boot.sapi.starter/src/main/resources/support/http/image/favicon.ico -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/resources/support/http/js/DataFormater.js: -------------------------------------------------------------------------------- 1 | window.TAB = " "; 2 | function IsArray(obj) { 3 | return obj && 4 | typeof obj === 'object' && typeof obj.length === 'number' && !(obj.propertyIsEnumerable('length')); 5 | } 6 | 7 | function formatJson(continner,json) { 8 | document.getElementById(continner).style.display = "block"; 9 | if (json == "") { 10 | json = '""'; 11 | } 12 | var obj = eval("[" + json + "]"); 13 | 14 | return ProcessObject(obj[0], 0, false, false, false); 15 | } 16 | 17 | function ProcessObject(obj, indent, addComma, isArray, isPropertyContent) { 18 | var html = ""; 19 | var comma = (addComma) ? ", ": ""; 20 | var type = typeof obj; 21 | if (IsArray(obj)) { 22 | if (obj.length == 0) { 23 | html += GetRow(indent, "[ ]" + comma, isPropertyContent); 24 | } else { 25 | html += GetRow(indent, "[", isPropertyContent); 26 | for (var i = 0; i < obj.length; i++) { 27 | html += ProcessObject(obj[i], indent + 1, i < (obj.length - 1), true, false); 28 | } 29 | html += GetRow(indent, "]" + comma); 30 | } 31 | } else { 32 | if (type == "object" && obj == null) { 33 | html += FormatLiteral("null", "", comma, indent, isArray, "Null"); 34 | } else { 35 | if (type == "object") { 36 | var numProps = 0; 37 | for (var prop in obj) { 38 | numProps++; 39 | } 40 | if (numProps == 0) { 41 | html += GetRow(indent, "{ }" + comma, isPropertyContent) 42 | } else { 43 | html += GetRow(indent, "{", isPropertyContent); 44 | var j = 0; 45 | for (var prop in obj) { 46 | html += GetRow(indent + 1, '"' + prop + '": ' + ProcessObject(obj[prop], indent + 1, ++j < numProps, false, true)) 47 | } 48 | html += GetRow(indent, "}" + comma); 49 | } 50 | } else { 51 | if (type == "number") { 52 | html += FormatLiteral(obj, "", comma, indent, isArray, "Number"); 53 | } else { 54 | if (type == "boolean") { 55 | html += FormatLiteral(obj, "", comma, indent, isArray, "Boolean"); 56 | } else { 57 | if (type == "function") { 58 | obj = FormatFunction(indent, obj); 59 | html += FormatLiteral(obj, "", comma, indent, isArray, "Function"); 60 | } else { 61 | if (type == "undefined") { 62 | html += FormatLiteral("undefined", "", comma, indent, isArray, "Null"); 63 | } else { 64 | html += FormatLiteral(obj, '"', comma, indent, isArray, "String"); 65 | } 66 | } 67 | } 68 | } 69 | } 70 | } 71 | } 72 | return html; 73 | } 74 | 75 | function FormatLiteral(literal, quote, comma, indent, isArray, style) { 76 | if (typeof literal == "string") { 77 | literal = literal.split("<").join("<").split(">").join(">"); 78 | } 79 | var str = "" + quote + literal + quote + comma + ""; 80 | if (isArray) { 81 | str = GetRow(indent, str); 82 | } 83 | return str; 84 | } 85 | 86 | function FormatFunction(indent, obj) { 87 | var tabs = ""; 88 | for (var i = 0; i < indent; i++) { 89 | tabs += window.TAB; 90 | } 91 | var funcStrArray = obj.toString().split("\n"); 92 | var str = ""; 93 | for (var i = 0; i < funcStrArray.length; i++) { 94 | str += ((i == 0) ? "": tabs) + funcStrArray[i] + "\n"; 95 | } 96 | return str; 97 | } 98 | 99 | function GetRow(indent, data, isPropertyContent) { 100 | var tabs = ""; 101 | for (var i = 0; i < indent && !isPropertyContent; i++) { 102 | tabs += window.TAB; 103 | } 104 | if (data != null && data.length > 0 && data.charAt(data.length - 1) != "\n") { 105 | data = data + "\n"; 106 | } 107 | return tabs + data; 108 | } 109 | 110 | var isXML = function(elem){ 111 | var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; 112 | return documentElement ? documentElement.nodeName !== "HTML" : false; 113 | } 114 | 115 | var isJSON=function(str) { 116 | if (typeof str == 'string') { 117 | try { 118 | JSON.parse(str); 119 | return true; 120 | } catch(e) { 121 | return false; 122 | } 123 | } 124 | } 125 | 126 | 127 | function formatXML(continner,content) { 128 | var xml_doc = (new DOMParser()).parseFromString(content.replace(/[\n\r]/g, ""), 'text/xml'); 129 | 130 | function build_xml(index, list, element) { 131 | var t = []; 132 | for (var i = 0; i < index; i++) { 133 | t.push('    '); 134 | } 135 | t = t.join(""); 136 | list.push(t + '<'+ element.nodeName +'>\n
'); 137 | for (var i = 0; i < element.childNodes.length; i++) { 138 | var nodeName = element.childNodes[i].nodeName; 139 | if (element.childNodes[i].childNodes.length === 1) { 140 | var value = element.childNodes[i].childNodes[0].nodeValue; 141 | var value_color = !isNaN(Number(value)) ? 'code-number' : 'code-string'; 142 | var value_txt = '' + value + ''; 143 | var item = t + '    <' + nodeName + 144 | '>' + value_txt + '</' + nodeName + '>
'; 145 | 146 | list.push(item); 147 | } else { 148 | build_xml(++index, list, element.childNodes[i]); 149 | } 150 | } 151 | list.push(t + '</'+ element.nodeName +'>\n'); 152 | } 153 | 154 | var list = []; 155 | build_xml(0, list, xml_doc.documentElement); 156 | 157 | //document.getElementById(continner).innerHTML = list.join(""); 158 | 159 | return list.join(""); 160 | } 161 | 162 | function deepClone(obj){ 163 | var objClone = Array.isArray(obj)?[]:{}; 164 | if(obj && typeof obj==="object"){ 165 | for(key in obj){ 166 | if(obj.hasOwnProperty(key)){ 167 | //判断ojb子元素是否为对象,如果是,递归复制 168 | if(obj[key]&&typeof obj[key] ==="object"){ 169 | objClone[key] = deepClone(obj[key]); 170 | }else{ 171 | //如果不是,简单复制 172 | objClone[key] = obj[key]; 173 | } 174 | } 175 | } 176 | } 177 | return objClone; 178 | } 179 | -------------------------------------------------------------------------------- /spring.boot.sapi.starter/src/main/resources/support/http/js/vue-resource.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-resource v1.5.1 3 | * https://github.com/pagekit/vue-resource 4 | * Released under the MIT License. 5 | */ 6 | 7 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueResource=e()}(this,function(){"use strict";function u(t){this.state=2,this.value=void 0,this.deferred=[];var e=this;try{t(function(t){e.resolve(t)},function(t){e.reject(t)})}catch(t){e.reject(t)}}u.reject=function(n){return new u(function(t,e){e(n)})},u.resolve=function(n){return new u(function(t,e){t(n)})},u.all=function(s){return new u(function(n,t){var o=0,r=[];function e(e){return function(t){r[e]=t,(o+=1)===s.length&&n(r)}}0===s.length&&n(r);for(var i=0;i 2 | 3 | 4 | SAPI-API列表 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ​ 32 | 33 | 34 |
35 | 36 | 37 |
38 |
39 |
40 |
接口URL: {{data.url}}
41 |
42 | 45 |
46 |
47 | 48 | 49 |
请求类型:{{data.requestType}}
50 |
51 | 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
接口参数:
参数类型
{{field.name}}{{field.type}}
该接口未定义参数
72 |
73 |
74 | 81 | 82 |
83 | 84 | 85 | 86 | 150 | 151 |
152 | 153 |
154 |
155 |
156 | SAPI is a simple api starter for spring 157 |
SAPI V1.2 ©️ xiaour
158 |
159 |
160 | 161 | 417 | 418 | --------------------------------------------------------------------------------