• Spring Boot RestController接口如何输出到终端


    今天在公司项目的代码上实践了一下,发现这种方法对业务代码侵入性太大,大量的业务方法需要增加传入参数,使得业务方法和HttpServletResponse类耦合度太高,需要将业务和输出解耦,具体实现请看下一篇文章 使用HttpServletResponse实现curl接口时控制台输出(续)

    背景

    公司项目的批处理微服务,一般是在晚上固定时段通过定时任务执行,但为了预防执行失败,我们定义了对应的应急接口,必要时可以通过运维在终端中进行curl操作。然而,部分任务耗时较长,curl命令执行后长时间没有输出,如果不查看日志,无法知道系统当前的状态,因此有必要研究一下如何在curl命令调用接口时,在终端输出部分信息,已告知运维人员当前命令的执行状态。

    原理

    使用 HttpServletResponse 类,可以自定义输出,也就是模拟网页输出的效果,只不过我们输出的内容是纯文本。

    代码

    1. 新建一个Spring Boot项目,建立一个 TestController,作为我们的应急接口。
    @RestController
    @Slf4j
    public class EmergencyController {
        @Resource
        private TestService testService;
    
        @GetMapping("/test")
        public void test(HttpServletResponse response) throws IOException {
            response.setContentType("text/plain;charset=utf-8");
            try {
                boolean result = testService.emergencyOperation(response);
                response.getWriter().println(CommonResult.success(null, "应急任务处理成功!").toString());
            } catch (IOException e) {
                log.error("应急任务处理失败!", e);
                response.getWriter().println(CommonResult.fail(null, "应急任务处理失败!").toString());
            }
        }
    }
    

    这里的一个坑是:如果使用了这种方法输出,那么接口方法不能再有任何返回值,不然会让Spring Boot以为重复调用了 response.getWriter() 函数,于是报错。需要将原本的输出内容(如通用返回体CommonResult类,或字符串String)也放入 response.getWriter() 进行输出。

    其中 TestService 是我们的批处理业务接口,无论是应急接口还是定时任务,都需要使用该接口进行实际的业务操作。其实现类 TestServiceImpl 代码如下:

    /**
     * 模拟应急操作方法
     */
    @Override
    public boolean emergencyOperation(HttpServletResponse response) throws IOException {
        // 如果是定时任务,则该参数传入null,不在终端输出
        boolean canOutput = response != null;
        PrintWriter writer = createPrintWriter(canOutput, response);
        log.info("开始执行应急操作任务");
        output(canOutput, writer, "开始执行应急操作任务");
        for (int i = 0; i < 20; i++) {
            output(canOutput, writer, "完成第" + (i+1) + "批次");
            log.info("完成第 {} 批次", i+1);
            try {
                Thread.sleep(500L);
            } catch (InterruptedException e) {
                log.warn("应急操作任务失败");
                output(canOutput, writer, "应急操作任务失败");
                return false;
            }
        }
        log.info("完成应急操作任务");
        output(canOutput, writer, "应急操作任务完成");
        return true;
    }
    

    其中 createPrintWriter() 方法设置 HttpServletResponse 对象的 ContentType 属性,我们输出的是纯文本,因此需要设置为 text/plain;charset=utf-8,具体代码如下:

    private PrintWriter createPrintWriter(boolean output, HttpServletResponse response) throws IOException {
        if (output) {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/plain;charset=utf-8");
            return response.getWriter();
        }
        return null;
    }
    

    在需要使用的地方调用 output() 方法,向控制台打印输出内容:

    private void output(boolean output, PrintWriter writer, String message) throws IOException {
        if (!output) {
            return;
        }
        writer.println(message);
        writer.flush();
    }
    

    测试

    使用 Maven 构建项目,在项目生成目录下运行 jar 包启动程序,另外开一个控制台窗口,执行 curl http://localhost:8080/test,可以看到控制台输出如下:

    同时在运行 Spring Boot 应用的窗口,也可以看到日志成功输出:

    定时任务执行情况

    我们定义定时任务 EmergencyTask 如下(不要忘了在应用启动类上增加 @EnableScheduling 注解)

    @Component
    @Slf4j
    public class EmergencyTask {
        @Resource
        private TestService testService;
    
        @Scheduled(cron = "0 */5 22 * * MON-FRI")
        public void emergencyTask() throws IOException {
            testService.emergencyOperation(null);
        }
    }
    

    这里我们设置的是从22点后每隔5分钟执行一次,当然实际项目中需要根据需求来确定定时任务执行时间。
    启动应用,等5分钟,可以看到定时任务成功执行。

    再次运行 curl http://localhost:8080/test,可以看到控制台和日志均正常输出。

    总结

    使用 HttpServletResponse 类,可以在使用 curl 执行 Spring Boot REST接口的同时,在控制台输出一些信息,给运维人员知道当前命令执行的状态。

  • 相关阅读:
    毕业设计-深度学习身份证号码检测识别-python-opencv
    amv是什么文件格式?如何播放amv视频?
    学C/C++想提升功底 可以花点时间看看这篇博客---C语言程序环境和预处理
    Android-Intent与Bundle在传值上的区别
    [软件工具][原创]pcl引用库一键配置到VisualStudio中支持vs2015-v2022使用教程
    乐乐音乐H5网页版-支持krc歌词(动感歌词、翻译和音译歌词)
    数据挖掘(3)特征化
    万邦抖音获取douyin分享口令url API
    【JavaScript】手撕前端面试题:寄生组合式继承 | 发布订阅模式 | 观察者模式
    华为OD机试 - 出错的或电路 - 二进制 - (Java 2023 B卷 100分)
  • 原文地址:https://www.cnblogs.com/ryuasuka/p/17729040.html