Skip to content

lets-blade/blade

Repository files navigation

Based onJava8+Netty4to create a lightweight, high-performance, simple and elegant Web framework 😋

Spend1 hourto learn it to do something interesting, a tool in addition to the other available frameworks.

🐾Quick Start| 🌚Documentation| 📗Guidebook| 💰Donate| 🇨🇳Giản thể tiếng Trung


What Is Blade?

Bladeis a pursuit of simple, efficient Web framework, so thatJavaWebdevelopment becomes even more powerful, both in performance and flexibility. If you like to try something interesting, I believe you will love it. If you think it's good, you can support it with astaror bydonating😊

Features

  • A new generation MVC framework that doesn't depend on other libraries
  • Get rid of SSH's bloated, modular design
  • Source is less than500kb,learning it is also simple
  • RESTful-style routing design
  • Template engine support, view development more flexible
  • High performance, 100 concurrent qps 20w/s
  • Run theJARpackage to open the web service
  • Streams-style API
  • CSRFandXSSdefense
  • Basic AuthandAuthorization
  • Supports plug-in extensions
  • Support webjars resources
  • Tasks based oncronexpressions
  • Built-in a variety of commonly used middleware
  • Built-in Response output
  • JDK8 +

Overview

» Simplicity: The design is simple, easy to understand and doesn't introduce many layers between you and the standard library. The goal of this project is that the users should be able to understand the whole framework in a single day.
» Elegance:bladesupports the RESTful style routing interface, has no invasive interceptors and provides the writing of a DSL grammar.
» Easy deploy: supportsmavenpackagejarfile running.

Quick Start

Create a basicMavenorGradleproject.

Do not create awebappproject, Blade does not require much trouble.

Run withMaven:

<dependency>
<groupId>com.hellokaton</groupId>
<artifactId>blade-core</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>

orGradle:

compile'com.hellokaton:blade-core:2.1.2.RELEASE'

Write themainmethod and theHello World:

publicstaticvoidmain(String[]args) {
Blade.create().get("/",ctx->ctx.text("Hello Blade")).start();
}

Openhttp://localhost:9000in your browser to see your firstBladeapplication!

Contents

Register Route

HardCode

publicstaticvoidmain(String[]args) {
// Create multiple routes GET, POST, PUT, DELETE using Blade instance
Blade.create()
.get("/user/21",getting)
.post("/save",posting)
.delete("/remove",deleting)
.put("/putValue",putting)
.start();
}

Controller

@Path
publicclassIndexController{

@GET("/login")
publicStringlogin(){
return"login.html";
}

@POST(value="/login",responseType=ResponseType.JSON)
publicRestResponsedoLogin(RouteContextctx){
// do something
returnRestResponse.ok();
}

}

Request Parameter

URL Parameter

Using RouteContext

publicstaticvoidmain(String[]args) {
Blade.create().get("/user",ctx-> {
Integerage=ctx.queryInt("age");
System.out.println("age is:"+age);
}).start();
}

Using@Queryannotation

@GET("/user")
publicvoidsavePerson(@QueryIntegerage){
System.out.println("age is:"+age);
}

Test it with sample data from the terminal

curl -X GET http://127.0.0.1:9000/user?age=25

Form Parameter

Here is an example:

Using RouteContext

publicstaticvoidmain(String[]args) {
Blade.create().get("/user",ctx-> {
Integerage=ctx.fromInt("age");
System.out.println("age is:"+age);
}).start();
}

Using@FormAnnotation

@POST("/save")
publicvoidsavePerson(@FormStringusername,@FormIntegerage){
System.out.println("username is:"+username+",age is:"+age);
}

Test it with sample data from the terminal

curl -X POST http://127.0.0.1:9000/save -F username=jack -F age=16

Path Parameter

Using RouteContext

publicstaticvoidmain(String[]args) {
Bladeblade=Blade.create();
// Create a route: /user/:uid
blade.get("/user/:uid",ctx-> {
Integeruid=ctx.pathInt("uid");
ctx.text("uid:"+uid);
});

// Create two parameters route
blade.get("/users/:uid/post/:pid",ctx-> {
Integeruid=ctx.pathInt("uid");
Integerpid=ctx.pathInt("pid");
Stringmsg="uid ="+uid+",pid ="+pid;
ctx.text(msg);
});

// Start blade
blade.start();
}

Using@PathParamAnnotation

@GET("/users/:username/:page")
publicvoiduserTopics(@PathParamStringusername,@PathParamIntegerpage){
System.out.println("username is:"+usernam+",page is:"+page);
}

Test it with sample data from the terminal

curl -X GET http://127.0.0.1:9000/users/hellokaton/2

Body Parameter

publicstaticvoidmain(String[]args) {
Blade.create().post("/body",ctx-> {
System.out.println("body string is:"+ctx.bodyToString());
}).start();
}

Using@BodyAnnotation

@POST("/body")
publicvoidreadBody(@BodyStringdata){
System.out.println("data is:"+data);
}

Test it with sample data from the terminal

curl -X POST http://127.0.0.1:9000/body -d'{ "username": "hellokaton", "age":22}'

Parse To Model

This is theUsermodel.

publicclassUser{
privateStringusername;
privateIntegerage;
// getter and setter
}

By Annotation

@POST("/users")
publicvoidsaveUser(@FormUseruser) {
System.out.println("user =>"+user);
}

Test it with sample data from the terminal

curl -X POST http://127.0.0.1:9000/users -F username=jack -F age=16

Custom model identification

@POST("/users")
publicvoidsaveUser(@Form(name="u")Useruser) {
System.out.println("user =>"+user);
}

Test it with sample data from the terminal

curl -X POST http://127.0.0.1:9000/users -F u[username]=jack -F u[age]=16

Body Parameter To Model

@POST("/body")
publicvoidbody(@BodyUseruser) {
System.out.println("user =>"+user);
}

Test it with sample data from the terminal

curl -X POST http://127.0.0.1:9000/body -d'{ "username": "hellokaton", "age":22}'

Get Environment

Environmentenvironment=WebContext.blade().environment();
Stringversion=environment.get("app.version","0.0.1");

Get Header

By Context

@GET("header")
publicvoidreadHeader(RouteContextctx){
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());
}

By Annotation

@GET("header")
publicvoidreadHeader(@HeaderStringhost){
System.out.println("Host =>"+host);
}

Get Cookie

By Context

@GET("cookie")
publicvoidreadCookie(RouteContextctx){
System.out.println("UID =>"+ctx.cookie("UID"));
}

By Annotation

@GET("cookie")
publicvoidreadCookie(@CookieStringuid){
System.out.println("Cookie UID =>"+uid);
}

Static Resource

Blade builds a few static resource catalog, as long as you will save the resource file in the static directory under the classpath, and then browsehttp://127.0.0.1:9000/static/style.css

If you want to customize the static resource URL

Blade.create().addStatics("/mydir");

Of course you can also specify it in the configuration file.application.properties(location in classpath)

mvc.statics=/mydir

Upload File

By Request

@POST("upload")
publicvoidupload(Requestrequest){
request.fileItem("img").ifPresent(fileItem-> {
fileItem.moveTo(newFile(fileItem.getFileName()));
});
}

By Annotation

@POST("upload")
publicvoidupload(@MultipartFileItemfileItem){
// Save to new path
fileItem.moveTo(newFile(fileItem.getFileName()));
}

Download File

@GET(value="/download",responseType=ResponseType.STREAM)
publicvoiddownload(Responseresponse)throwsIOException{
response.write("abcd.pdf",newFile("146373013842336153820220427172437.pdf"));
}

If you want to preview certain files in your browser

@GET(value="/preview",responseType=ResponseType.PREVIEW)
publicvoidpreview(Responseresponse)throwsIOException{
response.write(newFile("146373013842336153820220427172437.pdf"));
}

Set Session

The session is disabled by default, you must enable the session.

Blade.create()
.http(HttpOptions::enableSession)
.start(Application.class,args);

💡 It can also be enabled using a configuration file,http.session.enabled=true

publicvoidlogin(Sessionsession){
// if login success
session.attribute("login_key",SOME_MODEL);
}

Render To Browser

Render Response

By Context

@GET("users/json")
publicvoidprintJSON(RouteContextctx){
Useruser=newUser("hellokaton",18);
ctx.json(user);
}

By Annotation

This form looks more concise 😶

@GET(value="/users/json",responseType=ResponseType.JSON)
publicUserprintJSON(){
returnnewUser("hellokaton",18);
}

Render Text

@GET("text")
publicvoidprintText(RouteContextctx){
ctx.text("I Love Blade!");
}

or

@GET(value="/text",responseType=ResponseType.TEXT)
publicStringprintText(RouteContextctx){
return"I Love Blade!";
}

Render Html

@GET("html")
publicvoidprintHtml(RouteContextctx){
ctx.html("<center><h1>I Love Blade!</h1></center>");
}

or

@GET(value="/html",responseType=ResponseType.HTML)
publicStringprintHtml(RouteContextctx){
return"<center><h1>I Love Blade!</h1></center>";
}

Render Template

By default all template files are in the templates directory; in most of the cases you do not need to change it.

Default Template

By default, Blade uses the built-in template engine, which is very simple. In a real-world web project, you can try several other extensions.

publicstaticvoidmain(String[]args) {
Blade.create().get("/hello",ctx-> {
ctx.attribute("name","hellokaton");
ctx.render("hello.html");
}).start(Hello.class,args);
}

Thehello.htmltemplate

<!DOCTYPE html>
<htmllang= "en">
<head>
<metacharset= "UTF-8">
<title>Hello Page</title>
</head>
<body>

<h1>Hello, ${name}</h1>

</body>
</html>

Jetbrick Template

Config Jetbrick Template

Create aBladeLoaderclass and load some config

@Bean
publicclassTemplateConfigimplementsBladeLoader{

@Override
publicvoidload(Bladeblade) {
blade.templateEngine(newJetbrickTemplateEngine());
}

}

Write some data for the template engine to render

publicstaticvoidmain(String[]args) {
Blade.create().get("/hello",ctx-> {
Useruser=newUser("hellokaton",50);
ctx.attribute("user",user);
ctx.render("hello.html");
}).start(Hello.class,args);
}

Thehello.htmltemplate

<!DOCTYPE html>
<htmllang= "en">
<head>
<metacharset= "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>

Render API

Redirects

@GET("redirect")
publicvoidredirectToGithub(RouteContextctx){
ctx.redirect("https://github /hellokaton");
}

Redirect API

Write Cookie

@GET("write-cookie")
publicvoidwriteCookie(RouteContextctx){
ctx.cookie("hello","world");
ctx.cookie("UID","22",3600);
}

Cookie API

Web Hook

WebHookis the interface in the Blade framework that can be intercepted before and after the execution of the route.

publicstaticvoidmain(String[]args) {
// All requests are exported before execution before
Blade.create().before("/*",ctx-> {
System.out.println("before...");
}).start();
}

Logging

Blade uses slf4j-api as logging interface, the default implementation of a simple log package (modified from simple-logger); if you need complex logging you can also use a custom library, you only need to exclude theblade-logfrom the dependencies.

privatestaticfinalLoggerlog=LoggerFactory.getLogger(Hello.class);

publicstaticvoidmain(String[]args) {
log.info("Hello Info, {}","2017");
log.warn("Hello Warn");
log.debug("Hello Debug");
log.error("Hello Error");
}

Basic Auth

Blade includes a few middleware, like Basic Authentication; of course, it can also be customized to achieve more complex goals.

publicstaticvoidmain(String[]args) {
Blade.create().use(newBasicAuthMiddleware()).start();
}

Specify the user name and password in theapplication.propertiesconfiguration file.

http.auth.username=admin
http.auth.password=123456

Change Server Port

There are three ways to modify the port: hard coding it, in a configuration file, and through a command line parameter.

Hard Coding

Blade.create().listen(9001).start();

Configuration Forapplication.properties

server.port=9001

Command Line

java -jar blade-app.jar --server.port=9001

Configuration SSL

Configuration Forapplication.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

** Configuration using INettySslCustomizer **

#Specify any properties your customizer needs, for example
server.ssl.enable=true
server.keystore.path=fully qualified path
server.keystore.type=PKCS12
server.keystore.password=mypass
server.keystore.alias=optionalalias
  • Create your implementation of INettySslCustomizer
  • Register it with Blade class
MyNettySslCustomizernc=newMyNettySslCustomizer();
Blade.create()
.setNettySslCustomizer(nc)
.start(App.class,args);
}

Sample implementation of INettySslCustomizer

publicclassMyNettySSLCustomizerimplementsINettySslCustomizer{

publicSslContextgetCustomSslContext(Bladeblade) {
SslContextsslctx=null;

// get my custom properties from the environment
StringkeystoreType=blade.getEnv("server.keystore.type",null);
StringkeystorePath=blade.getEnv("server.keystore.path",null);
StringkeystorePass=blade.getEnv("server.keystore.password",null);

if(verifyKeystore(keystoreType,keystorePath,keystorePass)) {

try(FileInputStreaminstream=newFileInputStream(newFile(keystorePath))) {

// verify I can load store and password is valid
KeyStorekeystore=KeyStore.getInstance(keystoreType);
char[]storepw=keystorePass.toCharArray();
keystore.load(instream,storepw);

KeyManagerFactorykmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keystore,storepw);
sslctx=SslContextBuilder.forServer(kmf).build();

}catch(Exceptionex) {
log.error("Keystore validation failed"+ex.getMessage());
}

}else{
log.error("Unable to load keystore, sslContext creation failed.");
}

returnsslctx;
}

Custom Exception Handler

Blade has an exception handler already implemented by default; if you need to deal with custom exceptions, you can do it like follows.

@Bean
publicclassGlobalExceptionHandlerextendsDefaultExceptionHandler{

@Override
publicvoidhandle(Exceptione) {
if(einstanceofCustomException) {
CustomExceptioncustomException= (CustomException)e;
Stringcode=customException.getCode();
// do something
}else{
super.handle(e);
}
}

}

Besides looking easy, the features above are only the tip of the iceberg, and there are more surprises to see in the documentation and sample projects:

Change Logs

See Here

Contact

Contributors

Thanks goes to these wonderful people

contributors.svg

Contributions of any kind are welcome!

Licenses

Please seeApache License