1、精通Swagger
什么是swagger
swagger是一个Api框架,
- Restful api自动生成文档,和代码对应的在线Api文档。
- 可以直接运行测试接口,不用下载postman。
- 支持多种开发语言,java、php等。
现在的很多项目都是前后端分离的敏捷方式开发:
- 前端–>前端的控制层、视图层 (专业的前端团队开发)
- 后端–>后端的控制层、服务层、数据访问层(专业的后端团队开发)
前后端的交互都是通过API来进行的,怎么处理API的约定?
早期:后端编写文档(协同文档),前端根据文档调用接口获取数据然后渲染视图。
但是随着项目的推进,后端每次修改功能,都要同步更新文档,跟前端团队协商,增加了项目的时间成本,最终可能导致项目延时。使用swagger就很好的解决了开发团队的API协同问题,同时解放了后端人员写Api文档的痛苦。
集成swagger
基础集成
1、导入依赖
maven仓库搜swagger2
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!--swagger默认ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
2、编写配置
@Configuration
@EnableSwagger2
public class SwaggerConfig {
// 注册bean Docker,可以注册多个来分组
@Bean
public Docket demoDocket(){
Docket docket = new Docket(DocumentationType.SWAGGER_2).groupName("demo");
return docket;
}
@Bean
public Docket adminDocket(){
Docket docket = new Docket(DocumentationType.SWAGGER_2).groupName("admin");
return docket;
}
}
点进DocumentationType源码,你会发现有3个版本,我们现在使用的是swagger2版本
3、开启注解@EnableSwagger2,启动测试
对,集成就这么简单,启动后访问http://localhost:8080/swagger-ui.html#/
配置Swagger
1、配置Docket文档信息
点进Docket源码,默认的文档信息
public Docket(DocumentationType documentationType) {
this.apiInfo = ApiInfo.DEFAULT;
this.groupName = "default";
this.enabled = true;
this.genericsNamingStrategy = new DefaultGenericTypeNamingStrategy();
this.applyDefaultResponseMessages = true;
this.host = "";
this.pathMapping = Optional.absent();
this.apiSelector = ApiSelector.DEFAULT;
this.enableUrlTemplating = false;
this.vendorExtensions = Lists.newArrayList();
this.documentationType = documentationType;
}
点进ApiInfo源码,默认的api信息
public static final Contact DEFAULT_CONTACT = new Contact("", "", "");
public static final ApiInfo DEFAULT = new ApiInfo("Api Documentation", "Api Documentation", "1.0", "urn:tos",
DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<VendorExtension>());
所以默认的什么都不配,就会返回上面的文档信息
2、配置接口扫描,需要哪些被扫描到文档中
@Bean
public Docket demoDocket(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("demo")
.apiInfo(demoApiinfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.jude.demo"))// 包路径扫描到文档中
.build();
}
private ApiInfo demoApiinfo(){
// 建造者模式
return new ApiInfoBuilder()
.contact(new Contact("icoding","https://www.icodingedu.com/","icoding666@qq.com")) // 作者联系方式
.version("1.0.0") // 文档版本号
.description("测试使用") // 简介
.title("艾编程-测试API文档") // 标题
.termsOfServiceUrl("https://www.icodingedu.com/my/course/52") // 服务链接
.build();
}
RequestHandlerSelectors的其他几个方法说明
any() // 扫描所有接口
none() // 不扫描接口
basePackage // 根据包路径扫描
withMethodAnnotation(final Class<? extends Annotation> annotation) // 通过方法上的注解扫描,如PostMapping.class,GetMapping.class,ApiOpertion.class
withClassAnnotation(final Class<? extends Annotation> annotation) // 通过类上的注解扫描,如Api.class
3、配置哪些接口不被扫描
@Bean
public Docket demoDocket(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("demo")
.apiInfo(demoApiinfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.jude.demo")) // 包路径扫描到文档中
.paths(PathSelectors.ant("/demo/**"))// 只扫描/demo/开头的请求,实现过滤
.build();
}
PathSelectors的其他方法说明
PathSelectors.any() // 任何请求都会扫描
PathSelectors.regex() // 以正则表达式匹配
也可以使用Predicates,常用的有not(),or(),and()
.paths(Predicates.or(PathSelectors.ant("/demo/dept/**"),PathSelectors.ant("/demo/employee/**")))
.paths(Predicates.not(PathSelectors.regex("/error.*")))
配置Swagger的开关
test 、dev环境才显示swgger的Docket,通过org.springframework.core.env.Environment接口判断当前环境的profiles,就知道当前生效的环境了。
swagger的Api注解
实体类上的配置
@ApiModel 用于参数实体类说明
@ApiModelProperty 字段说明
@Data
@ApiModel(value = "数据采集爬取的内容字段提交对象")
public class WebCollectContentDTO {
@ApiModelProperty(value = "采集ID")
private Long collectId;
@ApiModelProperty(value = "内容页url地址")
private String pageUrl;
}
接口上的配置
@Api 用在controller类上,标注具体实现内容
@ApiOperation 用在方法,表示一个http请求的操作
@ApiParam 用在方法,参数说明
@Api(tags = "部门接口")
public class DepartmentController {
@ApiOperation("测试接口")
@PostMapping("/test")
public String hello(@ApiParam(value = "这个名字会返回",required = true) String username){
return username;
}
}
皮肤包
默认使用springfox-swagger-ui,访问localhost:8080/swagger-ui.html
maven仓库上搜swagger-ui,推荐使用swagger-bootstrap-ui 访问localhost:8080/doc.html
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
整合YapiUpload
swagger2可以生成在线API文档,但需要项目启动后才能访问查看,如果需要统一管理项目的API文档,则需要借助Yapi。YApi是高效、易用、功能强大的API管理平台,目前在github是开源的: https://github.com/YMFE/yapi
YApi不仅提供了常用的接口管理功能,还提供了权限管理、Mock数据、Swagger数据导入等功能
官方文档 https://hellosean1025.github.io/yapi
1、需要先安装nodejs和MongoDB
安装nodejs
wget https://nodejs.org/dist/v12.16.1/node-v12.16.1-linux-x64.tar.gz
tar -zxvf node-v12.16.1-linux-x64.tar.gz
# 创建软链接
ln -s ~/node-12.16.1/bin/node /usr/bin/node
ln -s ~/node-12.16.1/bin/npm /usr/bin/npm
node -v
使用docker或者压缩包的方式安装mongodb
mkdir mongodb
tar zxvf mongodb-linux-x86_64-3.0.6.tgz -C mongodb
mv mongodb-linux-x86_64-3.0.6/* /usr/local/mongodb
# 增加mongodb环境变量
vi /etc/profile
# export PATH=$PATH:/usr/local/mongodb/bin
# 生效环境变量
source /etc/profile
#检查mongodb环境变量是否生效
mongo --version
#配置mongodb配置文件信息
cd mongodb
mkdir data
vim mongodb.cnf
#配置信息详情
# 指定数据存储目录 需要提前创建
dbpath=/usr/local/mongodb/data/
# 指定日志文件
logpath=/usr/local/mongodb/data/mongo.log
# 日志追加写
logappend=true
# 创建后台子进程
fork=true
# 指定端口号
port=27017
#配置信息详情
#启动mongodbserver
mongod -f /usr/local/mongodb/mongdb.cnf
#连接本机的mongodb
cd /usr/local/mongodb/bin/
# 进入客户端
mongo
#当前所有数据库
>show dbs
#创建用户名/密码
>db.createUser({user:'root',pwd:'xxx', roles:[{role:'userAdminAnyDatabase', db:'admin'}]})
2、安装Yapi
这里可以选择两种方式,建议nodejs的方式安装,方便
-
tar安装包
# 解压安装包 tar -xvf yapi.tar cp vendors/config_example.json ./config.json # 配置config.json { "port": "3000", "adminAccount": "admin@admin.com", "db": { "servername": "127.0.0.1", "DATABASE": "yapi", "port": 27017, "user": "root", "pass": "xxx", "authSource": "admin" }, "mail": { "enable": true, "host": "smtp.exmail.qq.com", "port": 465, "from": "xxx@xxx.cn", "auth": { "user": "xxx@xxx.cn", "pass": "xxx" } } } #初始化数据库 cd vendors npm run install-server #启动yapi server node server/app.js
-
npm方式
npm install -g yapi-cli --registry https://registry.npm.taobao.org yapi server
执行 yapi server 启动可视化部署程序,输入相应的配置和点击开始部署,就能完成整个网站的部署。部署完成之后,可按照提示信息,执行 node/{网站路径/server/app.js} 启动服务器。在浏览器打开指定url, 点击登录输入您刚才设置的管理员邮箱,默认密码为 ymfe.org 登录系统(默认密码可在个人中心修改)。
利用pm2管理yapi服务
npm install pm2 -g //安装pm2 cd {项目目录} pm2 start "vendors/server/app.js" --name yapi //pm2管理yapi服务 pm2 info yapi //查看服务信息 pm2 stop yapi //停止服务 pm2 restart yapi //重启服务
升级
cd {项目目录}
yapi ls //查看版本号列表
yapi update //更新到最新版本
yapi update -v {Version} //更新到指定版本
3、登录yapi,添加分组与项目
4、idea整合yapi插件
安装完插件后,在项目的.idea目录下misc.xml文件添加配置
<component name="yapi">
<option name="projectToken">08c610184f731d6658f767eb605ffd2eaccf3d6dc1effb875f91beee29a08364</option>
<option name="projectId">11</option>
<option name="yapiUrl">http://10.16.151.170:8081</option>
<option name="projectType">api</option>
</component>
projectToken、projectId需要在自己的yapi平台上配置获取的
可以单个接口上传那就选中方法右键上传即可,批量上传那就选中类名右键上传。
查看API
2、异步任务
@Async注解
@Service
public class AsyncService {
public void hello(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
如果现在调用hello方法,前端需要等待3秒才会返回结果,如果我们使用异步处理,后台开启线程做数据处理,前端就不需要等待了,我们可以在hello方法上加上@Aysnc注解让它成为一个异步方法
@Async // 告诉Spring这是一个异步方法,默认使用线程池,效率很好
public void hello(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("数据处理中");
}
还需要开启注解@EnableAsync
@SpringBootApplication
@EnableAsync
public class SpringBootDataStudyXjwApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDataStudyXjwApplication.class, args);
}
}
业务方法如果需要回调通知前端,可以结合websocket、juc的CompletableFuture做异步回调
异步失效
如果我们在一个类的方法中调用同一个类的有@Async注解的方法,这种情况下异步不会生效,@Transational也是同理事务控制也不会生效,因为注解的本质的通过动态代理生成代理类取执行方法生效的,原因分析如下:
spring 在扫描bean的时候会扫描该类的方法上是否包含 @Async 注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类 proxy),代理类是继承原来那个bean的,并且重写了父类中被 @Async 注解的方法(如果该注解是加在了类上,则会重写该类的所有方法),并利用AOP切面为这些方法加上异步逻辑。
此时,当这个有注解的方法被调用的时候,实际上调用的是代理类中重写过的方法。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么就不会调用该代理类了,而是直接通过当前对象去调,所以也就不生效了,所以我们看到的现象就是该异步方法没有生效。伪代码:
@Service
class A{
@Async
method b(){...}
method a(){ //标记1
b();
}
}
//Spring扫描注解后,会创建另外一个代理类,并对添加注解的方法根据切入点创建代理
//调用代理,执行切入点处理器invoke方法,实现异步执行
class proxy$A{
A objectA = new A();
method b(){ //标记2
//MethodInterceptor.invoke
objectA.b();
}
method a(){ //标记3
objectA.a(); //由于a()没有注解,所以不会创建代理,而是直接调用A的实例的a()方法
}
}
当我们调用A的bean的a()方法的时候,也是被proxy$A拦截,执行proxy$A.a()(标记3),然而,由以上代码可知,这时候它调用的是objectA.a(),也就是由原来的bean来调用a()方法了,所以代码跑到了“标记1”。由此可见,“标记2”并没有被执行到,所以startTransaction()方法也没有运行。
JDK动态代理的核心是代理处理类会拦截所有代理接口的方法,通过处理类中的invoke去调用目标类中的方法,此时就可以在invoke中加入调用目标类方法的前后逻辑,例如记录日志。
参考: https://www.cnblogs.com/sueyyyy/p/14212953.html
使用@Aysnc注解一般都指定自定义的线程池,因为使用spring默认提供的线程池SimpleAsyncTaskExecutor每次都是new创建的,容易出现OOM
参考:https://zhuanlan.zhihu.com/p/432259634
3、定时任务
Timer类
这个是从jdk1.3就开始有的工具类,使用它可以简单的实现定时任务
private static final SimpleDateFormat dateFormat =
new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public static void main(String[] args) throws Exception {
// 创建定时器
Timer timer = new Timer();
// 提交计划任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("定时任务执行了...");
}
}, dateFormat.parse("2020-12-08 20:30:00"));
}
@Scheduled注解
cron表达式
语法:秒 分 小时 日 月 周 年[非必填]
周:1-7,1是星期一,7是星期六
在线生成:http://www.bejson.com/othertools/cron/
* 任意时间
? 日/周冲突匹配
- 区间,如6-7
L 最后
W 工作日
# 星期,如4#2,第二个星期三
常用例子:
0 0/2 * * * ? 表示每2分钟 执行任务
0 15 10 ? * MON-FRI 表示周一到周五每天上午10:15执行作业
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
例子
// 定时任务类
@Service
public class ScheduledService {
// 每分钟执行一次
@Scheduled(cron = "0 * * * * ?")
public void hello(){
System.out.println("Hello ......");
}
}
开启注解
@SpringBootApplication
@EnableAsync // 开启异步注解的支持
@EnableScheduling // 开启定时任务的支持
public class SpringBootDataStudyXjwApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDataStudyXjwApplication.class, args);
}
}
为什么@Scheduled定时任务是单线程的
从源码入手,从@EnableScheduled这个注解入手,找到ScheduledTaskRegistrar类,其中有一段代码如下:
protected void scheduleTasks() {
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
}
如果taskScheduler为null,则创建单线程的线程池:Executors.newSingleThreadScheduledExecutor()。
三种方案配置多线程的定时任务:
-
直接实现SchedulingConfigurer这个接口,设置taskScheduler,代码如下:
@Configuration public class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { //设定一个长度10的定时任务线程池 taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10)); } }
-
通过配置开启
Spring Boot quartz 已经提供了一个配置用来配置线程池的大小,如下
spring.task.scheduling.pool.size=10
只需要在配置文件中添加如上的配置即可生效
-
结合@Async
@Async这个注解都用过,用来开启异步任务的,使用@Async这个注解之前一定是要先配置线程池
的,配置如下:
@Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor(); poolTaskExecutor.setCorePoolSize(4); poolTaskExecutor.setMaxPoolSize(6); // 设置线程活跃时间(秒) poolTaskExecutor.setKeepAliveSeconds(120); // 设置队列容量 poolTaskExecutor.setQueueCapacity(40); poolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 等待所有任务结束后再关闭线程池 poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); return poolTaskExecutor; }
然后在@Scheduled方法上标注@Async这个注解即可实现多线程定时任务,代码如下:
@Async @Scheduled(cron = "0/2 * * * * ? ") public void test2() { System.out.println("..................执行test2................."); }
4、邮件任务
邮件的开发者权限获取
使用qq邮箱需要获取授权码,其他邮箱不用,如163
开启POP3/SMTP服务,得到授权码(相当于qq密码,可以登录你的邮箱),使用其他邮箱也需要开启这个服务获取搜权码
还有pop服务器,smtp服务器的地址,发邮件需要配置
测试
1、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2、配置文件
分析源码 MailSenderAutoConfiguration(邮件发送自动配置类),它绑定了配置文件属性类MailProperties,通过导入MailSenderPropertiesConfiguration和MailSenderJndiConfiguration,注入Bean: JavaMailSenderImpl 发送邮件的实现类
application.yaml 或application.properties配置属性,我这里使用网易邮箱,因为qq的密保问题没开启qq邮箱的smtp服务。
# 发件人信息
spring.mail.username=134xxxxx60@163.com
spring.mail.password=你的授权码
spring.mail.host=smtp.163.com
# 使用qq邮箱需要配置ssl安全连接,其他邮箱不用配置
spring.mail.properties.mail.smtp.ssl.enable= true
3、测试使用
@SpringBootTest
class SpringBootApplicationTests {
@Autowired
JavaMailSenderImpl javaMailSender;
@Test
void contextLoads() throws SQLException {
// 发送简单邮件,万物皆对象,邮件也不例外
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject("通知,飞天班第13节作业"); // 主题
message.setText("给自己的小破站增加邮件发送功能");
message.setTo("13435410760@163.com","747463168@qq.com");
message.setFrom("13435410760@163.com");
javaMailSender.send(message);
}
}
执行发现异常
查询官方给出的是:554 DT:SPM 发送的邮件内容包含了未被许可的信息,或被系统识别为垃圾邮件。请检查是否有用户发送病毒或者垃圾邮件
试着修改内容还是发送不出去,在收件人给自己也发送一份才可以。
发送复杂邮件
// 发送复杂邮件,可以结合富文本编辑器开发前端
@Test
void sendMail() throws MessagingException {
MimeMessage message = javaMailSender.createMimeMessage();
// 复杂邮件,通过辅助类完成
MimeMessageHelper helper = new MimeMessageHelper(message,true);
// 基本信息
helper.setSubject("复杂邮件"); // 主题
helper.setText("<blockquote style=\"text-align: center;margin-bottom: 10px;\n" +
"padding: 15px;\n" +
"line-height: 22px;\n" +
"border-left: 5px solid #009688;\n" +
"border-radius: 0 2px 2px 0;\n" +
"background-color: #f2f2f2;\">\n" +
"layui 兼容人类正在使用的全部浏览器(IE6/7除外),可作为 PC 端后台系统与前台界面的速成开发方案。\n" +
"</blockquote>",true); // html文本
// 发送附件
helper.addAttachment("layui.png",new File("/Users/xjw/Pictures/layui.png"));
// 发送对象
String[] receiver = {"13435410760@163.com","747463168@qq.com"};
helper.setTo(receiver);
helper.setFrom("13435410760@163.com");
javaMailSender.send(message);
}
5、富文本编辑器Editormd
注意bug:maven过滤资源的时候,可能会损坏一些文件,如图标。
<build>
<!-- 注意:mybatis的xml文件需要导出!
问题:java目录导出没有问题,resources目录过滤也没有问题
-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<!-- maven过滤资源的时候,可能会损坏一些文件,如图标! -->
<!--<resource>-->
<!--<directory>src/main/resources</directory>-->
<!--<includes>-->
<!--<include>**/*.xml</include>-->
<!--</includes>-->
<!--<filtering>true</filtering>-->
<!--</resource>-->
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
editormd是一个开源的markdown编辑器,官网 http://editor.md.ipandao.com/
文章表
可扩展字段:标签,时间,浏览量,点赞,评论表
编写一个editor.html页面
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Jude'Blog</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,user-scalable=yes, minimum-scale=0.4, initial-scale=0.8,target-densitydpi=low-dpi" />
<!--editormd-->
<link rel="stylesheet" th:href="@{/editormd/css/editormd.css}">
<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon" />
</head>
<body>
<div class="layui-fluid">
<div class="layui-row layui-col-space15">
<div class="layui-col-md12">
<!--博客表单-->
<form name="mdEditorForm">
<div>
标题: <input type="text" name="title">
</div>
<div>
作者: <input type="text" name="author">
</div>
<!-- 文章的主体内容 textarea -->
<div id="article-content">
<textarea name="content" id="content" style="display:none;"> </textarea>
</div>
</form>
</div>
</div>
</div>
</body>
<!--editormd-->
<script th:src="@{/editormd/jquery.min.js}"></script>
<script th:src="@{/editormd/editormd.js}"></script>
<script>
var testEditor;
$(function () {
testEditor = editormd("article-content",{
width: "95%",
height: 500,
syncScrolling: "single",
path: "/editormd/lib/", // 依赖模块路径
saveHTMLToTextarea: true, // 保存html到textarea
emoji: true, // 开启表情的功能,图片本地配置
// markdown的配置
tex: true, // 开启科学公式tex语言支持
flowChart: true, //开启流程图支持
// 图片上传
imageUpload: true,
imageFormats: ["jpg","jpeg","gif","png"],
imageUploadURL: "/demo/article/file/upload", // 文件上传请求
onload: function () {
console.log('onload',this);
},
/*指定需要显示的功能按钮*/
toolbarIcons : function() {
return ["undo","redo","|",
"bold","del","italic","quote","ucwords","uppercase","lowercase","|",
// "h1","h2","h3","h4","h5","h6","|",
"list-ul","list-ol","hr","|",
"link","reference-link","image","code","preformatted-text",
"code-block","table","datetime","emoji","html-entities","pagebreak","|",
"goto-line","watch","preview","fullscreen","clear","search","|",
//"help","info",
"releaseIcon", "index"]
},
// 自定义功能按钮,一个发布,一个返回首页
toolbarIconTexts:{
releaseIcon: '<span bgcolor="gray">发布</span>',
index: '<span bgcolor="red">返回首页</span>'
},
// 给自定义按钮指定回调函数
/**
* @param {Object} cm CodeMirror对象
* @param {Object} icon 图标按钮jQuery元素对象
* @param {Object} cursor CodeMirror的光标对象,可获取光标所在行和位置
* @param {String} selection 编辑器选中的文本
*/
toolbarHandlers:{
releaseIcon: function (cm,icon,cursor,selection) {
// 表单提交
mdEditorForm.method = "post";
mdEditorForm.action = "/article/addArticle"; // 提交到服务器
mdEditorForm.submit();
},
index: function () {
window.loaction.href = "/";
}
}
})
})
</script>
</html>
editormd的插件:
本地emoji表情
我们把本地表情图片放到emoji-dialog/emoji下。打开editormd.js,里面 editormd.defaults 就是默认的配置参数,创建editormd实例自己可以参考修改。找到editormd.emoji 把path修改为本地表情路径
editormd.emoji = {
path : "/editormd/plugins/emoji-dialog/emoji/",
ext : ".png"
};
文件上传与静态访问
图片本地上传的请求路径是/demo/article/file/upload,后台contorller写上对应的请求方法,json的返回格式:
// 给editormd回调
JSONObject res = new JSONObject();
res.put("url","/upload/"+month+"/"+filename);
res.put("success",1);
res.put("message","upload success");
配置upload路由静态资源访问
@Configuration
public class ResourceConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 文件保存到当前项目的upload目录下
// 访问的时候使用/upload/1.png就OK了
registry.addResourceHandler("/upload/**").addResourceLocations("file:"+System.getProperty("user.dir")+"/upload/");;
}
}
这样本地上传图片就可以了。
6、单体应用知识点总结
作业
给自己的博客网站,增加富文本编辑器的邮件发送功能