TodoBackend展示应用以及ActFramework的实现

1. 关于TodoBackend

TodoBackend是一个公认的服务器后端技术展示平台, 现在已有85个不同语言和框架的展示项目(Showcase)加入这个平台上, 供开发人员参考学习.

开发TodoBackend的展示应用需要满足以下需求:

  • 应用必须是RESTful的服务, 所有的服务端点必须通过平台指定的测试
  • 应用必须实现对CORS的支持

更多关于TodoBackend展示应用的信息可以参见贡献指南

2. 框架/技术比较

比较一下常见框架/技术实现TodoBackend展示应用的情况:

Language/Platform

Implementation

Data Persistent

Line of Code

Java/JVM

ActFramework

MongoDB

64

Java/JVM

Spring4 + Boot

Java Set

200

Java/JVM

vertx

MongoDB

241

Java/JVM

Dropwizard

Java Map

115

Java/JVM

Jooby

Java Map

231

Java/JVM

SparkFramework

PostgreSQL

348

Kotlin/JVM

Rapidoid

Java Map

81

Closure/JVM

Closure

PostgreSql

142

Scala/JVM

Scala/Play2.5

PostgreSql

136

Golang

Gin

Map in memory

128

Golang

stdlib

In memory data structure

238

JavaScript/NodeJs

express

PostgreSql

130

JavaScript/NodeJs

Koa

Redis

169

Python

webpy

Array in memory

32

Python

django

sqllite

164

Ruby

rails

PostgreSql

311

PHP

symfony2

sqlite

130 (only count files in src dir)

Haskell

Snap

Sqlite

98

C#/.Net

Asp.Net core

? (Entity Framework)

887

C#/.Net

ASP.NET WebAPI 2

In memory list

215

Swift

Kitura

MongoDB

473

3. ActFramework的实现

第一个ActFramework的实现基于MongoDB. 源代码同时发布在码云github.

2.1 代码分析

1. 域模型

在这个实现中我们使用了MongoDB作为数据存储. Act通过act-morphia插件提供了很好的MongoDB支持. 该插件依赖于官方的Morphia文档对象转换层

Act在Morphia之上提出了一个革新特性: AdaptiveRecord, 这个特性运行后端开发人员在域模型类中只声明参与后端计算逻辑的字段. 而只需呈现在前端不参与后端运算的字段可以不用申明. 下面就是我们基于AdaptiveRecordTodo类:

@Entity(value = "todo", noClassnameStored = true)
public class Todo extends MorphiaAdaptiveRecordWithLongId<Todo> {

    // needs to define this property to make it comply with todobackend spec
    // unless https://github.com/TodoBackend/todo-backend-js-spec/issues/6
    // is accepted
    public boolean completed;

    // url is required as per backend test spec. However it is not required
    // to put into the database. So we mark it as Transient property
    @Transient
    public String url;

    // We will generate the derived property `url` after
    // saving the model and loading the model
    @PostLoad
    @PostPersist
    private void updateUrl() {
        url = Act.app().router().fullUrl(S.concat("/todo/", getIdAsStr()));
    }
}

如上所示, 我们并在Todo类中没有声明前端应用用到的title, order甚至连completed都可以省却. 之所以定义了completed的原因在这个TodoBackend test spec的问题

注意类中声明的url属性并非需要存入数据库的数据, 这是一个派生字段, 由GET TODO Item的URL和当前Todo的id联合产生. 我们使用了Morphia的PostLoadPostPersist生命周期回调方法来填充url的值

2. 服务

在传统的Java Web应用中像这个实现中将服务(也称为控制器)嵌入域模型类的做法非常罕见:

@Entity(value = "todo", noClassnameStored = true)
public class Todo extends MorphiaAdaptiveRecordWithLongId<Todo> {

    // needs to define this property to make it comply with todobackend spec
    // unless https://github.com/TodoBackend/todo-backend-js-spec/issues/6
    // is accepted
    public boolean completed;

    ....

    @Controller("/todo")
    @Produces(H.MediaType.JSON)
    public static class Service extends MorphiaDaoWithLongId<Todo> {

        @PostAction
        public Todo create(Todo todo, Router router) {
            return save(todo);
        }

        @GetAction("{id}")
        public Todo show(long id) {
            return findById(id);
        }
        ...
    }
}

但我们认为在这个TODO应用中这样的安排是可以的, 因为该服务只针对Todo一个域模型. 另一方面我们其实鼓励使用这种方式来组织代码, 原因如下:

  1. 操作(服务)与数据(域模型)封装到一个模块是面向对象提倡的做法, 这样可以让应用的内聚性增强
  2. 同时也提高了代码可读性. 因为不需要在类文件(甚至在不同的包目录)之间来回切换, 就可以在阅读服务控制代码的时候查看被操作的数据细节.

顺便提一下, 代码中的@Produces(H.MediaType.JSON) 其实都可以省去. 前提是TodoBackend接受并修改了这个问题报告

3. CORS

TodoBackend要求展示用例必须支持CORS. 于是我们在其他的实现中就会找到各种各样跟CORS相关的代码, 比如:

来自Java 8 with Spring 4 Boot 实现

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    HttpServletResponse response = (HttpServletResponse) res;
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PATCH");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "x-requested-with, origin, content-type, accept");
    chain.doFilter(req, res);
}

和来自Java with Dropwizard implementation

private void addCorsHeader(Environment environment) {
    FilterRegistration.Dynamic filter = environment.servlets().addFilter("CORS", CrossOriginFilter.class);
    filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
    filter.setInitParameter("allowedOrigins", "*");
    filter.setInitParameter("allowedMethods", "GET,PUT,POST,DELETE,OPTIONS,HEAD,PATCH");
}

在Act中我们不需要类似的代码. 在Act中只需在配置文件中加入一行 cors=true即可. 这是另一个Act很酷的地方, 框架已经集成了很多工具帮助处理和Web应用相关的需求, 比如CORS和CSRF等等

总结

ActFramework提供了一个强大而灵活的机制来帮助开发人员迅速而简洁地开发RESTful的服务应用. 使用ActFramework开发人员只需要专注与业务逻辑而不需要去架设各种通用工具.

参考链接

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏枕边书

在Spring-Boot中实现通用Auth认证的几种方式

最近一直被无尽的业务需求淹没,没时间喘息,终于接到一个能让我突破代码舒适区的活儿,解决它的过程非常曲折,一度让我怀疑人生,不过收获也很大,代码方面不明显,但感觉...

750
来自专栏FreeBuf

开源BUG跟踪平台JIRA目录遍历漏洞分析

作者 Taskiller 最近,一则新发布的公告报告了一个影响Jira 5.0.11和6.0.3版本的目录遍历漏洞,该漏洞在去年7月份被验证,并在接下来的几个月...

3556
来自专栏工科狗和生物喵

FreeRTOS-Qemu 实现三任务同步通信机制以及API信息

---- 1. 本次作业的考察要点: 作业地址:https://github.com/HustWolfzzb/STM32F429DiscoveryFreeRTO...

9556
来自专栏Kubernetes

深度解析Kubernetes Local Persistent Volume(二)

摘要:上一篇博客”深度解析Kubernetes Local Persistent Volume(一)“对local volume的基本原理和注意事项进行了分析,...

8243
来自专栏技术博客

Ioc模式和MEF

  分离关注( Separation of Concerns : SOC)是Ioc模式和AOP产生最原始动力,通过功能分解可得到关注点,这些关注可以是 组件Co...

782
来自专栏coolblog.xyz技术专栏

Spring IOC 容器源码分析系列文章导读

Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本。经过十几年的迭代,现在的 Spring 框架已...

1373
来自专栏Java成神之路

分布式_事务_02_2PC框架raincat源码解析

上一节已经将raincat demo工程运行起来了,这一节来分析下raincat的源码

1731
来自专栏技术小黑屋

Android代码规范利器: Checkstyle

程序代码向来都不仅仅是用来运行的,写的一手好代码,易读,可维护应该是每个程序员所追求的。

1091
来自专栏Java架构师学习

浅析Netty

Netty是JBoss出品的高效的Java NIO开发框架,关于其使用,可参考我的另一篇文章 netty使用初步。本文将主要分析Netty实现方面的东西,由于精...

1281
来自专栏互联网技术栈

Netflix Archaius 分布式配置管理依赖构件

archaius是Netflix公司开源项目之一,基于java的配置管理类库,主要用于多配置存储的动态获取。主要功能是对apache common config...

1292

扫码关注云+社区