magic-api
一个基于 Java 的接口快速开发框架,通过 magic-api
提供的 UI 界面完成编写接口,无需定义 Controller
、Service
、Dao
、Mapper
、XML
、VO
等 Java 对象即可完成常见的 HTTP API
接口开发。
官网地址:https://www.ssssssss.org/magic-api
官方在线演示:https://magic-api.ssssssss.org
官方示例项目:https://gitee.com/ssssssss-team/magic-api-example
特性
- • 支持 MySQL、MariaDB、Oracle、DB2、PostgreSQL、SQLServer 等支持jdbc规范的数据库
- • 支持非关系型数据库Redis、Mongodb
- • 支持集群部署、接口自动同步。
- • 支持分页查询以及自定义分页查询
- • 支持多数据源配置,支持在线配置数据源
- • 支持SQL缓存,以及自定义SQL缓存
- • 支持自定义JSON结果、自定义分页结果
- • 支持对接口权限配置、拦截器等功能
- • 支持运行时动态修改数据源
- • 支持Swagger接口文档生成
- • 基于magic-script脚本引擎,动态编译,无需重启,实时发布
- • 支持Linq式查询,关联、转换更简单
- • 支持数据库事务、SQL支持拼接,占位符,判断等语法
- • 支持文件上传、下载、输出图片
- • 支持脚本历史版本对比与恢复
- • 支持脚本代码自动提示、参数提示、悬浮提示、错误提示
- • 支持导入Spring中的Bean、Java中的类
- • 支持在线调试
- • 支持自定义工具类、自定义模块包、自定义类型扩展、自定义方言、自定义列名转换等自定义操作
使用示例
添加依赖
新建 SpringBoot 项目,添加相关依赖:
<properties>
<java.version>1.8</java.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>2.7.15</spring-boot.version>
<magic-api.version>2.1.1</magic-api.version>
<druid-spring-boot.varsion>1.2.6</druid-spring-boot.varsion>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-spring-boot-starter</artifactId>
<version>${magic-api.version}</version>
</dependency>
<dependency>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-plugin-task</artifactId>
<version>${magic-api.version}</version>
</dependency>
<dependency>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-plugin-component</artifactId>
<version>${magic-api.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid-spring-boot.varsion}</version>
</dependency>
</dependencies>
配置文件
server:
port: 8080
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
mvc:
pathmatch:
matching-strategy: ant_path_matcher
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/demo?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&autoReconnect=true
username: root
password: 123456
initialSize: 10
minIdle: 10
maxActive: 100
maxWait: 60000
timeBetweenEvictionRunsMillis: 300000
minEvictableIdleTimeMillis: 3600000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
magic-api:
web: /api/web
show-sql: true #配置打印SQL
sql-column-case: camel
security:
username: admin # 登录用的用户名
password: admin@123 # 登录用的密码
support-cross-domain: true # 跨域支持,默认开启
resource:
type: database # 配置接口存储方式,这里选择存在数据库中
table-name: magic_api_file # 数据库中的表名
prefix: / # 前缀
# location: data/magic-api
page:
page: current
size: size
cache:
enable: true #开启缓存,默认是不开启的
ttl: 3600000 #有效期1小时,默认-1 即永不过期
response-code:
success: 200 #执行成功的code值
invalid: 400 #参数验证未通过的code值
exception: 500 #执行出现异常的code值
详细配置见文档:https://www.ssssssss.org/magic-api/pages/config/spring-boot/#%E5%AE%8C%E6%95%B4%E9%85%8D%E7%BD%AE%E7%A4%BA%E4%BE%8B
统一请求响应配置
package com.demo.config;
import com.demo.base.WrapMapper;
import org.springframework.stereotype.Component;
import org.ssssssss.magicapi.core.context.RequestEntity;
import org.ssssssss.magicapi.core.interceptor.ResultProvider;
import org.ssssssss.magicapi.modules.db.model.Page;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @ClassName: MagicAPIJsonConfig.java
* @Description: 统一请求响应配置
* @Author: tanyp
**/
@Component
public class MagicAPIJsonConfig implements ResultProvider {
/**
* @MonthName: buildResult
* @Description: 定义返回结果,默认返回JsonBean
* @Author: tanyp
* @Param: [requestEntity, i, s, o]
* @return: java.lang.Object
**/
@Override
public Object buildResult(RequestEntity requestEntity, int code, String message, Object data) {
return WrapMapper.wrap(code, message, data);
}
/**
* @MonthName: buildPageResult
* @Description: 定义分页返回结果
* @Author: tanyp
* @Param: [requestEntity, page, total, data]
* @return: java.lang.Object
**/
@Override
public Object buildPageResult(RequestEntity requestEntity, Page page, long total, List<Map<String, Object>> data) {
return new HashMap<String, Object>() {
{
put("total", total);
put("pages", page.getOffset());
put("size", page.getLimit());
put("records", data);
}
};
}
}
定义返回结果实体类
package com.demo.base;
import java.util.Objects;
/**
* @ClassName: WrapMapper.java
* @Description: 返回包装类
* @Author: tanyp
**/
public class WrapMapper {
private WrapMapper() {
}
public static <E> Wrapper<E> wrap(int code, String message, E o) {
return new Wrapper<E>(code, message, o);
}
public static <E> Wrapper<E> wrap(int code, String message) {
return new Wrapper<E>(code, message);
}
public static <E> Wrapper<E> wrap(int code) {
return wrap(code, null);
}
public static <E> Wrapper<E> wrap(Exception e) {
return new Wrapper<E>(Wrapper.ERROR_CODE, e.getMessage());
}
public static <E> E unWrap(Wrapper<E> wrapper) {
return wrapper.getResult();
}
public static <E> Wrapper<E> illegalArgument() {
return wrap(Wrapper.ILLEGAL_ARGUMENT_CODE_, Wrapper.ILLEGAL_ARGUMENT_MESSAGE);
}
public static <E> Wrapper<E> error() {
return wrap(Wrapper.ERROR_CODE, Wrapper.ERROR_MESSAGE);
}
public static <E> Wrapper<E> error(String message) {
return wrap(Wrapper.ERROR_CODE, Objects.isNull(message) ? Wrapper.ERROR_MESSAGE : message);
}
public static <E> Wrapper<E> error(int code, String message) {
return wrap(code, Objects.isNull(message) ? Wrapper.ERROR_MESSAGE : message);
}
public static <E> Wrapper<E> ok() {
return new Wrapper<E>();
}
public static <E> Wrapper<E> wrap(E o) {
return new Wrapper<>(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, o);
}
public static <E> Wrapper<E> ok(E o) {
return new Wrapper<>(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, o);
}
public static <E> Wrapper<E> success() {
return new Wrapper<>(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE);
}
}
package com.demo.base;
import java.io.Serializable;
/**
* @ClassName: Wrapper.java
* @Description: 包装类
* @Author: tanyp
**/
public class Wrapper<T> implements Serializable {
/**
* 成功码.
*/
public static final int SUCCESS_CODE = 200;
/**
* 成功信息.
*/
public static final String SUCCESS_MESSAGE = "操作成功";
/**
* 错误码.
*/
public static final int ERROR_CODE = 500;
/**
* 错误信息.
*/
public static final String ERROR_MESSAGE = "系统异常,请稍后重试!";
/**
* 错误码:参数非法
*/
public static final int ILLEGAL_ARGUMENT_CODE_ = 400;
/**
* 错误信息:参数非法
*/
public static final String ILLEGAL_ARGUMENT_MESSAGE = "请求参数非法,请核查!";
/**
* 错误码:参数非法
*/
public static final int AUTHORIZATION_CODE = 402;
/**
* 错误信息:参数非法
*/
public static final String AUTHORIZATION_MESSAGE = "Token has expired";
/**
* 编号.
*/
private int code;
/**
* 信息.
*/
private String message;
/**
* 结果数据
*/
private T result;
public Wrapper() {
this(SUCCESS_CODE, SUCCESS_MESSAGE);
}
public Wrapper(int code, String message) {
this.code(code).message(message);
}
public Wrapper(int code, String message, T result) {
super();
this.code(code).message(message).result(result);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public Wrapper<T> code(int code) {
this.setCode(code);
return this;
}
public Wrapper<T> message(String message) {
this.setMessage(message);
return this;
}
public Wrapper<T> result(T result) {
this.setResult(result);
return this;
}
@Override
public String toString() {
return "Wrapper{" +
"code=" + code +
", message='" + message + '\'' +
", result=" + result +
'}';
}
public boolean isSuccess() {
return this.getCode() == Wrapper.SUCCESS_CODE;
}
public boolean isFail() {
return !isSuccess();
}
}
全局异常拦截
package com.demo.extension;
import com.demo.base.WrapMapper;
import com.demo.base.Wrapper;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* @ClassName: GlobalException.java
* @Description: 全局异常拦截
* @Author: tanyp
**/
@RestControllerAdvice
public class GlobalException {
@ExceptionHandler(value = Exception.class)
public Wrapper handleException(Exception e) {
if (e instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
// 参数校验异常
return WrapMapper.wrap(Wrapper.ILLEGAL_ARGUMENT_CODE_, "参数有误:" + ex.getBindingResult().getFieldError().getDefaultMessage(), null);
} else {
// 统一系统异常
return WrapMapper.wrap(Wrapper.ERROR_CODE, e.getMessage(), null);
}
}
}
初始化数据库
CREATE TABLE `magic_api_file` (
`file_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`file_content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
PRIMARY KEY (`file_path`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='API 表';
CREATE TABLE `magic_backup_record` (
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原对象ID',
`create_date` bigint NOT NULL COMMENT '备份时间',
`tag` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '标签',
`type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '类型',
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原名称',
`content` blob COMMENT '备份内容',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人',
PRIMARY KEY (`id`,`create_date`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='api 备份';
启动项目
启动成功,日志如下:
2024-01-03 09:44:24.290 INFO 4324 --- [ main] o.s.m.s.b.s.MagicAPIAutoConfiguration : 注册扩展:class org.ssssssss.magicapi.modules.servlet.ResponseModule -> class org.ssssssss.magicapi.servlet.javaee.MagicJavaEEResponseExtension
__ __ _ _ ____ ___
| \/ | __ _ __ _ (_) ___ / \ | _ \|_ _|
| |\/| | / _` | / _` || | / __| / _ \ | |_) || |
| | | || (_| || (_| || || (__ / ___ \ | __/ | |
|_| |_| \__,_| \__, ||_| \___|/_/ \_\|_| |___|
|___/ 2.1.1
集成插件:
- 组件
- 定时任务
2024-01-03 09:44:24.308 ERROR 4324 --- [ main] o.s.m.s.b.s.MagicAPIAutoConfiguration : 当前备份设置未配置,强烈建议配置备份设置,以免代码丢失。
2024-01-03 09:44:24.338 INFO 4324 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '/'
2024-01-03 09:44:24.343 INFO 4324 --- [ main] com.ufan.admin.UfanAdminApplication : Started UfanAdminApplication in 2.158 seconds (JVM running for 2.62)
********************************************当前服务相关地址********************************************
服务启动成功,magic-api已内置启动! Access URLs:
接口本地地址: http://localhost:8080/
接口外部地址: http://172.16.80.253:8080/
接口配置平台: http://172.16.80.253:8080/api/web/index.html
可通过配置关闭输出: magic-api.show-url=false
********************************************当前服务相关地址********************************************
2024-01-03 09:44:26.206 INFO 4324 --- [nio-7001-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-01-03 09:44:26.206 INFO 4324 --- [nio-7001-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2024-01-03 09:44:26.206 INFO 4324 --- [nio-7001-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
访问:http://localhost:8080/api/web/index.html
如图所示:
magic-api 总体来说还是非常方便的,在做一些中小型项目的时候可以轻易的解决问题。
了解更多请看官方文档:https://www.ssssssss.org/magic-api