基于 Java8
+ Netty4
创造的轻量级、高性能、简洁优雅的Web框架 😋
花 1小时 学会它做点有趣的项目,一款除了 Spring 系框架的不二之选。
🐾 快速开始 | 🌚 官方文档 | 💰 捐赠我们 | 🌾 English
Blade
是一款追求简约、高效的 Web 框架,让 JavaWeb
开发如虎添翼,在性能与灵活性上同时兼顾。
如果你喜欢尝试有趣的事物,相信你会爱上它。
如果觉得这个项目不错可以 star 支持或者 捐赠 它 😊
- 新一代MVC框架,不依赖更多的库
- 摆脱SSH的臃肿,模块化设计
- 源码不到
500kb
,学习也简单 - Restful风格路由设计
- 模板引擎支持,视图开发更灵活
- 高性能,100 并发下qps 20w/s
- 运行
JAR
包即可开启 web 服务 - 支持
CSRF
和XSS
防御 - 支持
BasicAuth
和权限管理 - 流式API风格
- 支持插件扩展
- 支持 webjars 资源
-
cron
表达式的定时任务 - 内置多种常用中间件
- 内置JSON输出
- JDK8+
» 简洁的:框架设计简单,容易理解,不依赖于更多第三方库。Blade框架目标让用户在一天内理解并使用。
» 优雅的:Blade
支持 REST 风格路由接口, 提供 DSL 语法编写,无侵入式的拦截器。
» 易部署:支持 maven
打成 jar
包直接运行。
Maven
配置:
创建一个基础的 Maven
工程
<dependency>
<groupId>com.hellokaton</groupId>
<artifactId>blade-core</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
不需要创建
webapp
项目骨架, Blade 没这么麻烦。
或者 Gradle
:
compile 'com.hellokaton:blade-core:2.1.2.RELEASE'
编写 main
函数写一个 Hello World
:
public static void main(String[] args) {
Blade.create().get("/", ctx -> ctx.text("Hello Blade")).start();
}
用浏览器打开 http://localhost:9000 这样就可以看到第一个 Blade
应用了!
注册路由
请求参数
获取环境配置
获取Header
获取Cookie
静态资源
上传文件
下载文件
设置会话
渲染到浏览器
模板渲染
重定向
写入Cookie
路由拦截
日志输出
Basic认证
修改服务端口
配置SSL
自定义异常处理
public static void main(String[] args) {
// 使用 Blade 实例创建多种路由 GET、POST、PUT、DELETE
Blade.create()
.get("/user/21", getting)
.post("/save", posting)
.delete("/remove", deleting)
.put("/putValue", putting)
.start();
}
@Path
public class IndexController {
@GET("/login")
public String login(){
return "login.html";
}
@POST(value = "/login", responseType = ResponseType.JSON)
public RestResponse doLogin(RouteContext ctx){
// do something
return RestResponse.ok();
}
}
下面是个例子:
使用 RouteContext 获取
public static void main(String[] args) {
Blade.create().get("/user", ctx -> {
Integer age = ctx.queryInt("age");
System.out.println("age is:" + age);
}).start();
}
使用 @Query
注解获取
@GET("/user")
public void savePerson(@Query Integer age){
System.out.println("age is:" + age);
}
在命令行下发送数据测试
curl -X GET http://127.0.0.1:9000/user?age=25
下面是个例子:
使用 RouteContext 获取
public static void main(String[] args) {
Blade.create().get("/user", ctx -> {
Integer age = ctx.fromInt("age");
System.out.println("age is:" + age);
}).start();
}
使用 @Form
注解获取
@POST("/save")
public void savePerson(@Form String username, @Form Integer age){
System.out.println("username is:" + username + ", age is:" + age);
}
在终端下发送数据测试
curl -X POST http://127.0.0.1:9000/save -F username=jack -F age=16
使用 RouteContext 获取
public static void main(String[] args) {
Blade blade = Blade.create();
// Create a route: /user/:uid
blade.get("/user/:uid", ctx -> {
Integer uid = ctx.pathInt("uid");
ctx.text("uid : " + uid);
});
// Create two parameters route
blade.get("/users/:uid/post/:pid", ctx -> {
Integer uid = ctx.pathInt("uid");
Integer pid = ctx.pathInt("pid");
String msg = "uid = " + uid + ", pid = " + pid;
ctx.text(msg);
});
// Start blade
blade.start();
}
使用注解获取
@GET("/users/:username/:page")
public void userTopics(@PathParam String username, @PathParam Integer page){
System.out.println("username is:" + username + ", page is:" + page);
}
在终端下发送数据测试
curl -X GET http://127.0.0.1:9000/users/hellokaton/2
public static void main(String[] args) {
Blade.create().post("/body", ctx -> {
System.out.println("body string is:" + ctx.bodyToString());
}).start();
}
使用 @Body
注解
@POST("/body")
public void readBody(@Body String data){
System.out.println("data is:" + data);
}
在终端下发送数据测试
curl -X POST http://127.0.0.1:9000/body -d '{"username":"hellokaton","age":22}'
这是 User
类结构
public class User {
private String username;
private Integer age;
// getter and setter
}
使用注解获取
@POST("/users")
public void saveUser(@Form User user) {
System.out.println("user => " + user);
}
在终端下发送数据测试
curl -X POST http://127.0.0.1:9000/users -F username=jack -F age=16
自定义 model
名称
@POST("/users")
public void saveUser(@Form(name="u") User user) {
System.out.println("user => " + user);
}
在终端下发送数据测试
curl -X POST http://127.0.0.1:9000/users -F u[username]=jack -F u[age]=16
Body 参数转对象
@POST("/body")
public void body(@Body User user) {
System.out.println("user => " + user);
}
在终端下发送数据测试
curl -X POST http://127.0.0.1:9000/body -d '{"username":"hellokaton","age":22}'
Environment environment = WebContext.blade().environment();
String version = environment.get("app.version", "0.0.1");
使用 RouteContext 获取
@GET("header")
public void readHeader(RouteContext ctx){
System.out.println("Host => " + ctx.header("Host"));
// get useragent
System.out.println("UserAgent => " + ctx.userAgent());
// get client ip
System.out.println("Client Address => " + ctx.address());
}
使用注解获取
@GET("header")
public void readHeader(@Header String host){
System.out.println("Host => " + host);
}
使用 RouteContext 获取
@GET("cookie")
public void readCookie(RouteContext ctx){
System.out.println("UID => " + ctx.cookie("UID"));
}
使用注解获取
@GET("cookie")
public void readCookie(@Cookie String UID){
System.out.println("Cookie UID => " + UID);
}
Blade 内置了一些静态资源目录,只要将资源文件保存在 classpath
下的 static
目录中,然后浏览 http://127.0.0.1:9000/static/style.css
如果要自定义静态资源URL,可以使用下面的代码
Blade.create().addStatics("/mydir");
当然你也可以在配置文件中指定 application.properties
(位于classpath之下)
mvc.statics=/mydir
使用Request获取
@POST("upload")
public void upload(Request request){
request.fileItem("img").ifPresent(fileItem -> {
fileItem.moveTo(new File(fileItem.getFileName()));
});
}
使用注解获取
@POST("upload")
public void upload(@Multipart FileItem fileItem){
// 保存到新位置
fileItem.moveTo(new File(fileItem.getFileName()));
}
@GET(value = "/download", responseType = ResponseType.STREAM)
public void download(Response response) throws IOException {
response.write("abcd.pdf", new File("146373013842336153820220427172437.pdf"));
}
如果你想在浏览器预览某些文件
@GET(value = "/preview", responseType = ResponseType.PREVIEW)
public void preview(Response response) throws IOException {
response.write(new File("146373013842336153820220427172437.pdf"));
}
默认情况不开启会话功能,首先要开启会话
Blade.create()
.http(HttpOptions::enableSession)
.start(Application.class, args);
💡 也可以使用配置文件开启,
http.session.enabled=true
public void login(Session session){
// if login success
session.attribute("login_key", SOME_MODEL);
}
使用 RouteContext 渲染
@GET("users/json")
public void printJSON(RouteContext ctx){
User user = new User("hellokaton", 18);
ctx.json(user);
}
使用注解获取
这种形式看起来更简洁 😶
@GET(value = "/users/json", responseType = ResponseType.JSON)
public User printJSON(){
return new User("hellokaton", 18);
}
@GET("text")
public void printText(RouteContext ctx){
ctx.text("I Love Blade!");
}
or
@GET(value = "/text", responseType = ResponseType.TEXT)
public String printText(RouteContext ctx){
return "I Love Blade!";
}
@GET("html")
public void printHtml(RouteContext ctx){
ctx.html("<center><h1>I Love Blade!</h1></center>");
}
or
@GET(value = "/html", responseType = ResponseType.HTML)
public String printHtml(RouteContext ctx){
return "<center><h1>I Love Blade!</h1></center>";
}
默认情况下,所有模板文件都 在templates
目录中,大多数情况下你不需要更改它。
默认情况下,Blade使用内置的模板引擎,如果你真的做一个Web项目可以尝试其他几个扩展,这很简单。
public static void main(String[] args) {
Blade.create().get("/hello", ctx -> {
ctx.attribute("name", "hellokaton");
ctx.render("hello.html");
}).start(Hello.class, args);
}
hello.html
模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello Page</title>
</head>
<body>
<h1>Hello, ${name}</h1>
</body>
</html>
配置 Jetbrick 模板引擎
实现一个 BladeLoader
加载初始化的操作
@Bean
public class TemplateConfig implements BladeLoader {
@Override
public void load(Blade blade) {
blade.templateEngine(new JetbrickTemplateEngine());
}
}
写一点数据让模板渲染
public static void main(String[] args) {
Blade.create().get("/hello", ctx -> {
User user = new User("hellokaton", 50);
ctx.attribute("user", user);
ctx.render("hello.html");
}).start(Hello.class, args);
}
hello.html
模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello Page</title>
</head>
<body>
<h1>Hello, ${user.username}</h1>
#if(user.age > 18)
<p>Good Boy!</p>
#else
<p>Gooood Baby!</p>
#end
</body>
</html>
@GET("redirect")
public void redirectToGithub(RouteContext ctx){
ctx.redirect("https://github.com/hellokaton");
}
@GET("write-cookie")
public void writeCookie(RouteContext ctx){
ctx.cookie("hello", "world");
ctx.cookie("UID", "22", 3600);
}
WebHook
是Blade框架中可以在执行路由之前和之后拦截的接口。
public static void main(String[] args) {
// All requests are exported before execution before
Blade.create().before("/*", ctx -> {
System.out.println("before...");
}).start();
}
Blade 使用 slf4-api
作为日志接口,默认实现一个简单的日志(从simple-logger修改),如果你需要复杂的日志记录你也可以使用其他的日志框架,你只需要在依赖关系中排除 blade-log
然后添加你喜欢的。
private static final Logger log = LoggerFactory.getLogger(Hello.class);
public static void main(String[] args) {
log.info("Hello Info, {}", "2017");
log.warn("Hello Warn");
log.debug("Hello Debug");
log.error("Hello Error");
}
Blade 内置了几个中间件,当你需要Basic认证时可以使用如下代码,当然也可以定制来实现。
public static void main(String[] args) {
Blade.create().use(new BasicAuthMiddleware()).start();
}
在 application.properties
配置文件中指定用户名和密码。
http.auth.username=admin
http.auth.password=123456
有三种方式修改端口,硬编码,配置文件,启动命令行参数。
硬编码
Blade.create().listen(9001).start();
配置文件 application.properties
server.port=9001
命令行
java -jar blade-app.jar --server.port=9001
配置文件 application.properties
server.ssl.enable=true
server.ssl.cert-path=cert.pem
server.ssl.private-key-path=private_key.pem
server.ssl.private-key-pass=123456
默认情况下,Blade 已经实现了一个异常处理器,有时你需要处理自定义异常,因此你可以尝试像下面这样使用。
@Bean
public class GolbalExceptionHandler extends DefaultExceptionHandler {
@Override
public void handle(Exception e) {
if (e instanceof CustomException) {
CustomException customException = (CustomException) e;
String code = customException.getCode();
// do something
} else {
super.handle(e);
}
}
}
这一切看起来多么的简单,不过上面的功能可是冰山一角,查看文档和示例项目有更多惊喜:
- Twitter: hellokaton
- Mail: hellokaton@gmail.com
非常感谢下面的开发者朋友对本项目的帮助,如果你也愿意提交PR,非常欢迎!
请查看 Apache License