ActFramework 1.6.0 是一个很重要的版本,包括 22 项新特性和增强, 以及 7 个 bug 修正. 这里列出一些值得关注的特性和增强. 更多详细的信息, 可以关注 https://github.com/actframework/actframework/milestone/37?closed=1
一行配置即可打开 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."
}
ActFramework 现在支持对特定服务端点进行流控以防止 DoS 攻击
@Throttled
注解在响应方法上设置流控 @GetAction("gh/435")
@Throttled(1) // maximum 1 requests per second from the same ip
public String test() {
return "GH435 - throttle control";
}
throttled
注解在路由表中设置流控GET /gh/435/txt resource[throttled]:asset/gh435.txt
注意 路由表中设置的流控依赖与 req.throttle
配置来设定每秒最大访问数
ActFramework 1.6.0 内置了对 webjars 的支持
现在我们可以将配置注入到复杂类型, 包括 Map
, List
和 接口实现.
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
}
List
/array
注入对于以下配置:
myconf.list.demo=1,2,3
注入 List
/array
类型的 Java 代码:
@Configuration("myconf.list.demo")
int[] list;
@Configuration("myconf.list.demo")
List<Integer> list2;
假设我们定义了下面的接口以及实现类
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
}
注意 如需更多配置方面的信息, 可以参考文档.
ActFramework 对配置管理有非常友好的支持,包括基于 profile 的配置管理. 不过应用的某些依赖库需要加载自己独立的配置文件, 比如使用了 mybatis 来访问数据库的应用通常需要加载 mybatis-config.xml
文件来构建 SqlSessionFactory
实例. 现在 1.6.0 版中提供了基于 profile 的资源文件加载特性来解决这个问题:
@LoadConfig("mybatis.xml")
private InputStream is;
上面的代码会按照一下顺序来加载 mybatis.xml
文件:
当任意一次加载成功之后, 尝试过程将会终止, 并将加载的内容注入到所需类型中 (参考注入资源文件内容).
现在开发人员可以使用 @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
文件中的内容加载到不同的类型字段中
现在可以在路由表中使用路由指令注解:
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]
都是路由指令注解, 可以让开发人员更容易的表达复杂的路由需求
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
) {
...
}
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);
}
}
现在我们可以把整个模板的内容传入 render
方法中:
@GetAction("test")
public void hello(@DefaultValue("world") String who) {
render("@args String who\nHello @who", who);
}
注意 传入的内容必须对应用默认视图引擎有效
SerializeFilter
和 SerializerFeature
的支持现在应用开发可以方便地指定 FastJson SerializeFilter
和 SerializerFeature
, 示例如下:
@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=Foo
和 count=3
的请求将返回下面的 JSON 内容:
{
"BarCount":3,
"Flag":false,
"Name":"Tom"
}
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 响应.
以前我们可以针对 JSON 格式的请求 body 注入 AdaptiveRecord
类型的参数. 1.6.0 版可以让这个特性针对 multipart/form-data
或 application/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);
}
});
}
现在 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