TodoBackend展示应用以及ActFramework的实现

1. 关于TodoBackend

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

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

  • 应用必须是RESTful的服务, 所有的服务端点必须通过平台指定的测试

  • 应用必须实现对CORS的支持

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

2. 框架/技术比较

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

Language/PlatformImplementationData PersistentLine of Code
Java/JVMActFrameworkMongoDB64
Java/JVMSpring4 + BootJava Set200
Java/JVMvertxMongoDB241
Java/JVMDropwizardJava Map115
Java/JVMJoobyJava Map231
Java/JVMSparkFrameworkPostgreSQL348
Kotlin/JVMRapidoidJava Map81
Closure/JVMClosurePostgreSql142
Scala/JVMScala/Play2.5PostgreSql136
GolangGinMap in memory128
GolangstdlibIn memory data structure238
JavaScript/NodeJsexpressPostgreSql130
JavaScript/NodeJsKoaRedis169
PythonwebpyArray in memory32
Pythondjangosqllite164
RubyrailsPostgreSql311
PHPsymfony2sqlite130 (only count files in src dir)
HaskellSnapSqlite98
C#/.NetAsp.Net core? (Entity Framework)887
C#/.NetASP.NET WebAPI 2In memory list215
SwiftKituraMongoDB473

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开发人员只需要专注与业务逻辑而不需要去架设各种通用工具.

参考链接

转载于:https://my.oschina.net/greenlaw110/blog/846514

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值