原 荐 ActFramework 发布 1.

ActFramework 1.6.0 是一个很重要的版本,包括 22 项新特性和增强, 以及 7 个 bug 修正. 这里列出一些值得关注的特性和增强. 更多详细的信息, 可以关注 https://github.com/actframework/actframework/milestone/37?closed=1

1. 主要新特性

1.1 JWT 支持

一行配置即可打开 ActFramework 对 JWT 的支持:

jwt=true

当 JWT 支持打开时, ActFramework 将 session/flash 内容编码到 JWT 中,并通过 Authorization 响应头和 Bearer ${JWT} 方式传递给请求方:

Authorization=Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJoZWwiLCJleHAiOjE1MTQ3Mjc3MzF9.y5M6MCZgeyx7ezN9RRFchk0vF22G2rlNFz530n187sw.

当浏览器或者其他请求方也应该使用同样的方式将 JWT 发送给应用.

如果需要直接返回 JWT 可以参照下面代码:

    @GetAction("/jwt")
    public Object getJsonWebToken() {
        return Controller.Util.jwt();
    }

/jwt 发送请求会收到类似下面的响应体:

{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJoZWwiLCJleHAiOjE1MTQ3Mjc4Nzh9.UckgQO9ih1K_O-4V7LU5IIhGdP6Eu8HXX5ePyUSqMX8."
}

1.2 流控

ActFramework 现在支持对特定服务端点进行流控以防止 DoS 攻击

1.2.1 使用 @Throttled 注解在响应方法上设置流控

    @GetAction("gh/435")
    @Throttled(1) // maximum 1 requests per second from the same ip
    public String test() {
        return "GH435 - throttle control";
    }

1.2.2 使用 throttled 注解在路由表中设置流控

GET /gh/435/txt resource[throttled]:asset/gh435.txt

注意 路由表中设置的流控依赖与 req.throttle 配置来设定每秒最大访问数

1.3 Webjars 支持

ActFramework 1.6.0 内置了对 webjars 的支持

  1. 在 pom.xml 文件中添加 webjars 依赖,例如: <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.1.0</version>ziyuan </dependency>
  2. 在 html 页面中指定 webjars 资源 <link type="text/css" rel="stylesheet" href="/webjars/bootstrap/3.1.0/css/bootstrap.css">

2. 配置以及资源文件加载的增强

2.1 将配置注入复杂类型

现在我们可以将配置注入到复杂类型, 包括 Map, List 和 接口实现.

2.1.1 Map 类型注入

对于以下配置:

myconf.foo.bar.one=1
myconf.foo.bar.two=2

注入 Map 类型的 Java 代码:

@Configuration("myconf.foo.bar")
Map<String, Integer> fooBars;

fooBars 中的内容是:

{
    "one": 1,
    "two": 2
}

2.1.2 List/array 注入

对于以下配置:

myconf.list.demo=1,2,3

注入 List/array 类型的 Java 代码:

@Configuration("myconf.list.demo") 
int[] list;

@Configuration("myconf.list.demo") 
List<Integer> list2;

2.1.3 依据配置注入接口实现

假设我们定义了下面的接口以及实现类

public interface GreetingService {
    String greet();

    default String getName() {
        return greet() + " service";
    }
}

public class HelloService implements GreetingService {
    @Override
    public String greet() {
        return "Hello";
    }
}

public class NihaoService implements GreetingService {
    @Override
    public String greet() {
        return "你好";
    }
}

对于以下配置:

greet.default=demo.helloworld.HelloService
greet.nihao=demo.helloworld.NihaoService

下面的代码可以注入配置好的接口实现:

@Configuration("greet.default")
private GreetingService defaultService;

也可以注入接口实现的 Map

@Configuration("greet")
private Map<String, GreetingService> services;

services 中的内容会是:

{
    "default": HelloService instance
    "nihao": NihaoService instance
}

注意 如需更多配置方面的信息, 可以参考文档.

2.2 基于 profile 的资源文件加载

ActFramework 对配置管理有非常友好的支持,包括基于 profile 的配置管理. 不过应用的某些依赖库需要加载自己独立的配置文件, 比如使用了 mybatis 来访问数据库的应用通常需要加载 mybatis-config.xml 文件来构建 SqlSessionFactory 实例. 现在 1.6.0 版中提供了基于 profile 的资源文件加载特性来解决这个问题:

@LoadConfig("mybatis.xml")
private InputStream is;

上面的代码会按照一下顺序来加载 mybatis.xml 文件:

  1. resources/conf/${profile}/mybatis.xml
  2. resources/conf/common/mybatis.xml
  3. resources/conf/mybatis.xml
  4. resources/mybatis.xml

当任意一次加载成功之后, 尝试过程将会终止, 并将加载的内容注入到所需类型中 (参考注入资源文件内容).

2.3 注入资源文件内容

现在开发人员可以使用 @LoadResource 注解将资源文件内容直接注入到需要的字段类型中:

@Singleton
public class Foo {

    @LoadResource("/asset/index.html")
    private ByteBuffer byteBuffer;

    @LoadResource("/asset/index.html")
    private String string;

    @LoadResource("/asset/index.html")
    private List<String> lines;

    @LoadResource("/asset/index.html")
    private InputStream is;

    @LoadResource("/asset/index.html")
    private Reader reader;

    @LoadResource("/asset/index.html")
    private ISObject sobj;

}

上面的代码演示了如何使用 @LoadResource 注解将 /resources/asset/index.html 文件中的内容加载到不同的类型字段中

3. 路由增强

3.1 路由指令注解

现在可以在路由表中使用路由指令注解:

GET /protected_zone resource[authenticated]:asset/pzone/index.html
GET /log/xyz file[authenticated][external][throttled]:/var/log/xyz.log
GET /superway redirect[authenticated]:http://superway.com

上面演示的路由表项中, [authenticated], [external][throttled] 都是路由指令注解, 可以让开发人员更容易的表达复杂的路由需求

3.2 动态 URL 路径变量

Actframework 对 URL 路径变量的支持已经非常好了, 这次我们进一步增强了这方面的特性: 实现了对动态路由变量的支持. 例如下面的请求:

/companyEmployee/id=2;name=Xpto/employeeData/id=1;name=John;contactNumber=2200112334

ActFramework 中可以轻松的声明其中路由变量的映射如下:

@GetRequest("/companyEmployee/{company}/employeeData/{employee}")
public void companyEmployee(
    Map<String, String> company, 
    Map<String, String> employee
) {
    ...
}

作为比较, 在 SpringMVC 中处理相同请求的声明如下:

@RequestMapping(
 value = "/companyEmployee/{company}/employeeData/{employee}",
 method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<Map<String, String>> getEmployeeDataFromCompany(
  @MatrixVariable(pathVar = "company") Map<String, String> company,
  @MatrixVariable(pathVar = "employee") Map<String, String> employee
) {
  ...
}

4. 视图增强

4.2 允许应用程序使用 ViewManager.getTemplate API

示例代码:

@UrlContext("352")
@TemplateContext("352")
public class GH352 extends GithubIssueBase {

    @Before
    public void setupRenderArgs() {
        context.renderArg("who", "Act");
    }

    @GetAction
    public String test(ViewManager viewManager) {
        return viewManager.getTemplate("/gh/352/test.html").render(context);
    }

    @GetAction("relative")
    public String testRelativePath(ViewManager viewManager) {
        return viewManager.getTemplate("test").render(context);
    }

}

4.2 内联模板内容支持

现在我们可以把整个模板的内容传入 render 方法中:

@GetAction("test")
public void hello(@DefaultValue("world") String who) {
    render("@args String who\nHello @who", who);
}

注意 传入的内容必须对应用默认视图引擎有效

5. 其他新特性/增强

5.1 对 FastJSON SerializeFilterSerializerFeature 的支持

现在应用开发可以方便地指定 FastJson SerializeFilterSerializerFeature, 示例如下:

@UrlContext("426")
public class GH426 extends GithubIssueBase {

    public static class Foo {
        public String name;
        public int BarCount;
        public Boolean flag;

        public Foo(String name, int barCount) {
            this.name = name;
            BarCount = barCount;
        }
    }

    @GetAction
    @FastJsonFilter(PascalNameFilter.class)
    @FastJsonFeature({SerializerFeature.WriteNullBooleanAsFalse, SerializerFeature.PrettyFormat})
    public Foo foo(String name, int count) {
        return new Foo(name, count);
    }

}

/gh/426 发送 name=Foocount=3 的请求将返回下面的 JSON 内容:

{
	"BarCount":3,
	"Flag":false,
	"Name":"Tom"
}

5.2 支持更多的重定向语义

1.6.0 版在 Controller.Util 上增加了一些新的重定向方法完整实现了 HTTP 规范指定的几种重定向语义:

Controller.Util.moved(); // 生成 301 Moved Permanantly response
Controller.Util.found(); // 生成 302 Found response
Controller.Util.seeOther(); // 生成 303 See Other response
Controller.Util.temporaryRedirect(); // 生成 307 Temporary Redirect response
Controller.Util.permanentRedirect(); // 生成 308 Permanant Redirect response

关于重定向更多的信息可以参考 https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections

最初的 Controller.Util.redirect() 方法依然保留下来, 该方法在普通 HTTP 请求时生成 302 Found 响应, 而在 AJAX 请求的时候生成 278 响应.

5.3 从表单提交参数中获得 AdaptiveRecord 实例

以前我们可以针对 JSON 格式的请求 body 注入 AdaptiveRecord 类型的参数. 1.6.0 版可以让这个特性针对 multipart/form-dataapplication/x-www-form-urlencoded 请求工作了. 例如下面这个 AdaptiveRecord Model:

@Entity("art")
public class Artwork extends MorphiaAdaptiveRecord<Artwork> {
}

和一下请求响应器

@PostAction
public void submitArtwork(ISObject attachment, Artwork artwork, EventBus eventBus) {
    badRequestIf(null == attachment, "attachment not found, file upload failed?");
    badRequestIf(submittedAlready((String)artwork.getValue("ChildName"), (String)artwork.getValue("ChildFullAddress")), "submitted already");
    save(artwork);
    eventBus.trigger(EVENT_ARTWORK_SUBMITTED, artwork, attachment);
}

我们可以使用下面的 JavaScript 代码发送请求:

// Submit
function submit() {
    var data = new FormData();
    data.append('attachment', $('#artworkFile').get(0).files[0]);
    data.append('artwork.TeacherName', $('#teacherName').val());
    data.append('artwork.SchoolName', $('#schoolName').val());
    data.append('artwork.SchoolFullAddress', $('#schoolAddress').val());
    data.append('artwork.Principal', $('#headTeacherName').val());
    data.append('artwork.SchoolContactNumber', $('#schoolContactNumber').val());
    data.append('artwork.SchoolEmail', $('#schoolEmail').val());
    data.append('artwork.ParentGuardianName', $('#parentName').val());
    data.append('artwork.ParentGuardianEmail', $('#parentEmail').val());
    data.append('artwork.ParentGuardianNumber', $('#parentNumber').val());
    data.append('artwork.ChildName', $('#childName').val());
    data.append('artwork.ChildDOB', $('#childDOB').val());
    data.append('artwork.ChildFullAddress', $('#childAddress').val());
    data.append('artwork.IsSchoolBSBF', $('input[name=bsbf]:checked').val());

    console.log(data);
    $.ajax({
        type: 'POST',
        url: '/artwork',
        data: data,
        processData: false,
        contentType: false,
        success: function (data) {
            console.log('success', data);
        }
    });
}

5.4 静态资源版本

现在 rythm 模板中可以获得静态资源版本了:

@resource("conf/app.properties")
@asset("favicon.png?n=3")
@asset("foo/bar") #non exists resource
@resource("/xyz/abc") #non exist resource

上面的 rythm 模板代码在 PROD 模式下生成下面的 HTML 代码:

/conf/app.properties?checksum=ea2550ca9302a72096365d7a48883554e02c5fe5
/asset/favicon.png?n=3&checksum=595fdf00a210712a8643e34f99c9d95b48083f92
/asset/foo/bar
/xyz/abc

在 DEV 模式下则生成一下代码:

onf/app.properties?ts=1511465779934
asset/favicon.png?n=3&ts=1511465779934
asset/foo/bar?ts=1511465779934
/xyz/abc?ts=1511465779934

其中 ts 是生成代码时的时间戳

For complete list of 1.6.0 release please checkout https://github.com/actframework/actframework/milestone/37?closed=1

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大神带我来搬砖

spring boot中用注解进行服务器端参数校验

3477
来自专栏jeremy的技术点滴

Retrying_Library_For_Java

2825
来自专栏大内老A

通过一个模拟程序让你明白WCF大致的执行流程

在《通过一个模拟程序让你明白ASP.NET MVC是如何运行的》一文中我通过一个普通的ASP.NET Web程序模拟了ASP.NET MVC的执行流程,现在我们...

1906
来自专栏葬爱家族

一个别出心裁,但毫无卵用的缓存框架

目前大多数缓存框架都是用Java序列化的方式实现的持久化存储,我们自己公司的项目也是这么做的,功能全面而且效率也高, 使用起来得心应手,但是有一个小问题,如果数...

781
来自专栏java架构学习交流

从事务角度粗窥架构的可扩展性和可维护性:内容整理自java web轻量级开发面试教程

    大家多少了解过架构,也听说过使用架构后,代码和可维护性和重用性能大大提升。这里我们来通过一些关于事务的实例,来感性地体会下架构带来的在可维护性方面的便利...

1927
来自专栏Python

Django---Ajax

Ajax准备知识:json 什么是json? 定义: JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换...

21910
来自专栏一枝花算不算浪漫

[Java面试七]Mybatis总结以及在面试中的一些问题.

42014
来自专栏安恒信息

安恒信息研究员发现Struts 2高危漏洞,Apache官方致谢

ApacheStruts 2是世界上最流行的Java Web服务器框架之一。不久之前,安恒信息风暴中心安全研究员在Struts 2上发现了一枚远程代码执行漏洞,...

2355
来自专栏Java成神之路

java分层架构概念

1.JAVA中Action层, Service层 ,modle层 和 Dao层的功能区分?(下面所描述的service层就是biz)       首先这是现在...

804
来自专栏电光石火

springboot vue整合websocket

1663

扫码关注云+社区