:rocket: Lightning fast and elegant mvc framework for Java8

Overview

Based on Java8 + Netty4 to create a lightweight, high-performance, simple and elegant Web framework 😋

Spend 1 hour to learn it to do something interesting, a tool in addition to the other available frameworks.

🐾 Quick Start | 🎬 Video Tutorial | 🌚 Documentation | 📗 Guidebook | 💰 Donate | 🇨🇳 简体中文


What Is Blade?

Blade is a pursuit of simple, efficient Web framework, so that JavaWeb development 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 a star or by donating 😊

Features

  • A new generation MVC framework that doesn't depend on other libraries
  • Get rid of SSH's bloated, modular design
  • Source is less than 500kb, learning it is also simple
  • RESTful-style routing design
  • Template engine support, view development more flexible
  • High performance, 100 concurrent qps 20w/s
  • Run the JAR package to open the web service
  • Streams-style API
  • CSRF and XSS defense
  • Basic Auth and Authorization
  • Supports plug-in extensions
  • Support webjars resources
  • Tasks based on cron expressions
  • Built-in a variety of commonly used middleware
  • Built-in JSON 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: blade supports the RESTful style routing interface, has no invasive interceptors and provides the writing of a DSL grammar.
» Easy deploy: supports maven package jar file running.

Quick Start

Create a basic Maven or Gradle project.

Do not create a webapp project, Blade does not require much trouble.

Run with Maven:

<dependency>
    <groupId>com.bladejava</groupId>
    <artifactId>blade-mvc</artifactId>
    <version>2.0.15.RELEASE</version>
</dependency>

or Gradle:

compile 'com.bladejava:blade-mvc:2.0.15.RELEASE'

Write the main method and the Hello World:

public static void main(String[] args) {
    Blade.of().get("/", ctx -> ctx.text("Hello Blade")).start();
}

Open http://localhost:9000 in your browser to see your first Blade application!

Contents

Register Route

HardCode

public static void main(String[] args) {
    // Create Blade,using GET、POST、PUT、DELETE
    Blade.of()
        .get("/user/21", getting)
        .post("/save", posting)
        .delete("/remove", deleting)
        .put("/putValue", putting)
        .start();
}

Controller

@Path
public class IndexController {

    @GetRoute("signin")
    public String signin(){
        return "signin.html";
    }

    @PostRoute("signin")
    @JSON
    public RestResponse doSignin(RouteContext ctx){
        // do something
        return RestResponse.ok();
    }

}

Get Request Parameters

Form Parameters

Here is an example:

By Context

public static void main(String[] args) {
    Blade.of().get("/user", ctx -> {
        Integer age = ctx.fromInt("age");
        System.out.println("age is:" + age);
    }).start();
}

By Annotation

@PostRoute("/save")
public void savePerson(@Param String username, @Param Integer age){
    System.out.println("username is:" + username + ", age is:" + age)
}

Test it with sample data from the terminal

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

Path Parameters

By RouteContext

public static void main(String[] args) {
    Blade blade = Blade.of();
    // 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();
}

By Annotation

@GetRoute("/users/:username/:page")
public void userTopics(@PathParam String username, @PathParam Integer page){
    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/biezhi/2

Body Parameters

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

Test it with sample data from the terminal

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

Parse To Model

This is the User model.

public class User {
    private String username;
    private Integer age;
    // getter and setter
}

By Annotation

@PostRoute("/users")
public void saveUser(@Param User user){
    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

@PostRoute("/users")
public void saveUser(@Param(name="u") User user){
    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

public void getUser(@BodyParam User user){
    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":"biezhi","age":22}'

Get Environment

Environment environment = WebContext.blade().environment();
String version = environment.get("app.version", "0.0.1");

Get Header

By Context

@GetRoute("header")
public void getHeader(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());
}

By Annotation

@GetRoute("header")
public void getHeader(@HeaderParam String Host){
    System.out.println("Host => " + Host);
}

Get Cookie

By Context

@GetRoute("cookie")
public void getCookie(RouteContext ctx){
    System.out.println("UID => " + ctx.cookie("UID"));
}

By Annotation

@GetRoute("cookie")
public void getCookie(@CookieParam String UID){
    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 browse http://127.0.0.1:9000/static/style.css

If you want to customize the static resource URL

Blade.of().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

@PostRoute("upload")
public void upload(Request request){
    request.fileItem("img").ifPresent(fileItem -> {
        fileItem.moveTo(new File(fileItem.getFileName()));
    });
}

By Annotation

@PostRoute("upload")
public void upload(@MultipartParam FileItem fileItem){
    // Save to new path
    fileItem.moveTo(new File(fileItem.getFileName()));
}

Set Session

public void login(Session session){
    // if login success
    session.attribute("login_key", SOME_MODEL);
}

Render To Browser

Render JSON

By Context

@GetRoute("users/json")
public void printJSON(RouteContext ctx){
    User user = new User("biezhi", 18);
    ctx.json(user);
}

By Annotation

This form looks more concise 😶

@GetRoute("users/json")
@JSON
public User printJSON(){
    return new User("biezhi", 18);
}

Render Text

@GetRoute("text")
public void printText(RouteContext ctx){
    ctx.text("I Love Blade!");
}

Render Html

@GetRoute("html")
public void printHtml(RouteContext ctx){
    ctx.html("<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.

public static void main(String[] args) {
    Blade.of().get("/hello", ctx -> {
        ctx.attribute("name", "biezhi");
        ctx.render("hello.html");
    }).start(Hello.class, args);
}

The hello.html template

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello Page</title>
</head>
<body>

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

</body>
</html>

Jetbrick Template

Config Jetbrick Template

Create a BladeLoader class and load some config

@Bean
public class TemplateConfig implements BladeLoader {

    @Override
    public void load(Blade blade) {
        blade.templateEngine(new JetbrickTemplateEngine());
    }

}

Write some data for the template engine to render

public static void main(String[] args) {
    Blade.of().get("/hello", ctx -> {
        User user = new User("biezhi", 50);
        ctx.attribute("user", user);
        ctx.render("hello.html");
    }).start(Hello.class, args);
}

The hello.html template

<!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>

Render API

Redirects

@GetRoute("redirect")
public void redirectToGithub(RouteContext ctx){
    ctx.redirect("https://github.com/biezhi");
}

Redirect API

Write Cookie

@GetRoute("write-cookie")
public void writeCookie(RouteContext ctx){
    ctx.cookie("hello", "world");
    ctx.cookie("UID", "22", 3600);
}

Cookie API

Web Hook

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

public static void main(String[] args) {
    // All requests are exported before execution before
    Blade.of().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 the blade-log from the dependencies.

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");
}

Basic Auth

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

public static void main(String[] args) {
    Blade.of().use(new BasicAuthMiddleware()).start();
}

Specify the user name and password in the application.properties configuration 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.of().listen(9001).start();

Configuration For application.properties

server.port=9001

Command Line

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

Configuration SSL

Configuration For 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

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
public class GlobalExceptionHandler 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);
        }
    }

}

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 see Apache License

Comments
  • Who's using it?

    Who's using it?

    If you are using Blade, please send your web site, company or project name record below. We'll synchronize that information right away, who's using it? The format is as follows:

    • Company:Google
    • Project: My Blog
    • WebSite:https://example.com

    Thank you for choosing Blade 💖

    feedback discussion beginner 
    opened by hellokaton 22
  • Exception passing string containing =

    Exception passing string containing =

    I've created a simple POST service using Blade. In my request class I have a String field. When I send a request containing an equals character, "=", I get the following exception:

    io.netty.handler.codec.http.multipart.HttpPostRequestDecoder$ErrorDataDecoderException: java.io.IOException: The filename, directory name, or volume label syntax is incorrect at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.parseBodyAttributes(HttpPostStandardRequestDecoder.java:615) at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.parseBody(HttpPostStandardRequestDecoder.java:366) at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.offer(HttpPostStandardRequestDecoder.java:295) at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.offer(HttpPostStandardRequestDecoder.java:47) at io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.offer(HttpPostRequestDecoder.java:223) at com.blade.mvc.http.HttpRequest.init(HttpRequest.java:376) at com.blade.server.netty.HttpServerHandler.buildWebContext(HttpServerHandler.java:96) at com.blade.server.netty.HttpServerHandler.lambda$channelRead0$0(HttpServerHandler.java:85) at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616) at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:591) at java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:456) at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164) at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:497) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at java.lang.Thread.run(Thread.java:748) Caused by: java.io.IOException: The filename, directory name, or volume label syntax is incorrect at java.io.WinNTFileSystem.createFileExclusively(Native Method) at java.io.File.createTempFile(File.java:2024) at java.io.File.createTempFile(File.java:2070) at io.netty.handler.codec.http.multipart.AbstractDiskHttpData.tempFile(AbstractDiskHttpData.java:90) at io.netty.handler.codec.http.multipart.AbstractDiskHttpData.addContent(AbstractDiskHttpData.java:162) at io.netty.handler.codec.http.multipart.DiskAttribute.addContent(DiskAttribute.java:98) at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.setFinalBuffer(HttpPostStandardRequestDecoder.java:624) at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.parseBodyAttributes(HttpPostStandardRequestDecoder.java:595)

    Here is the full request payload, only the formatString is material. Any string containing an equals sign results in the exception, other strings are ok:

    {"formatString":"=","delimiters":null,"params":["","","","","","","","","",""]}

    It's not reaching my code, as it is failing in Netty while parsing the message body.

    opened by sd205 18
  • No JSON response body

    No JSON response body

    I saw my issue #427 was closed. In order to test the fix, I've cloned your repository and built the 2.1.0.BETA JARs. (For which I had to disable some unit tests that were failing: DateKitTest, HttpRequestTest, ANYTest) I then refactored my code, since there is zero backwards compatibility (annoying!)

    I am able to confirm that the original issue is fixed - I can pass in a JSON body with a value contain an equals sign, e.g. {"str":"="}

    However, now I have a worse problem - there is no body at all on the response! It just returns empty with 200. I have read the README and updated my annotations, e.g. @POST(value = "/api/parse", responseType = ResponseType.JSON)

    You can find my simple test case in the original issue. You'll obviously have to refactor...

    invalid question 
    opened by sd205 13
  • 坑太多,不敢上生产,搞一天遇到很多严重问题!

    坑太多,不敢上生产,搞一天遇到很多严重问题!

    我的环境

    • Operating system win10
    • Build tools maven
    • JDK 1.8.0_231

    问题一:请求json读取就有问题,这个问题不是必现(我也是醉了)

    我的请求内容

    {
      "domainName": "http://jk.com",
      "originalUrl": "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=2&tn=monline_3_dg&wd=IDEA%20HTTP%E5%B7%A5%E5%85%B7%20head&rsv_spt=1&oq=ID%2526gt%253BA%2520HTTP%25E5%25B7%25A5%25E5%2585%25B7&rsv_pq=bb4d99e900012007&rsv_t=26523EkyonuMuvG5guYz%2FWJ6DVRA4JyAqpRcSGGmJ1e4R%2Fiv%2F5Ge4jm6md3hnVuZEqyF&rqlang=cn&rsv_enter=1&rsv_dl=tb&inputT=1818&sug=java%2520%25E6%2595%25B0%25E6%258D%25AE%25E5%25BA%2593&rsv_sug3=150&rsv_sug2=0&rsv_sug4=1986"
    }
    

    控制台报错

    2020-03-02 22:36:55.358 [worker@thread-2] ERROR com.blade.server.netty.HttpServerHandler -  500          POST /api/register
    2020-03-02 22:36:55.361 [worker@thread-2] ERROR com.blade.mvc.handler.DefaultExceptionHandler - 
    io.netty.handler.codec.http.multipart.HttpPostRequestDecoder$ErrorDataDecoderException: java.io.IOException: Unable to create temporary file, C:\Users\lizw\AppData\Local\Temp\Attr_5509425574122559294_{   "domainName": "http:\jk.com",   "originalUrl": "https:\www.baidu.com\s?ie.att
    	at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.parseBodyAttributes(HttpPostStandardRequestDecoder.java:605)
    	at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.parseBody(HttpPostStandardRequestDecoder.java:360)
    	at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.offer(HttpPostStandardRequestDecoder.java:289)
    	at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.offer(HttpPostStandardRequestDecoder.java:46)
    	at io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.offer(HttpPostRequestDecoder.java:227)
    	at com.blade.mvc.http.HttpRequest.init(HttpRequest.java:359)
    	at com.blade.server.netty.HttpServerHandler.buildWebContext(HttpServerHandler.java:95)
    	at com.blade.server.netty.HttpServerHandler.lambda$channelRead0$0(HttpServerHandler.java:84)
    	at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:602)
    	at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:577)
    	at java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:442)
    	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
    	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
    	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:462)
    	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
    	at java.lang.Thread.run(Thread.java:748)
    Caused by: java.io.IOException: Unable to create temporary file, C:\Users\lizw\AppData\Local\Temp\Attr_5509425574122559294_{   "domainName": "http:\jk.com",   "originalUrl": "https:\www.baidu.com\s?ie.att
    	at java.io.File$TempDirectory.generateFile(File.java:1921)
    	at java.io.File.createTempFile(File.java:2010)
    	at java.io.File.createTempFile(File.java:2070)
    	at io.netty.handler.codec.http.multipart.AbstractDiskHttpData.tempFile(AbstractDiskHttpData.java:90)
    	at io.netty.handler.codec.http.multipart.AbstractDiskHttpData.addContent(AbstractDiskHttpData.java:163)
    	at io.netty.handler.codec.http.multipart.DiskAttribute.addContent(DiskAttribute.java:99)
    	at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.setFinalBuffer(HttpPostStandardRequestDecoder.java:614)
    	at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.parseBodyAttributes(HttpPostStandardRequestDecoder.java:539)
    	... 15 common frames omitted
    

    问题二:客户端发送http请求后就假死,服务端一直不返回响应,断点到PostRoute方法中也不进断点,服务端的控制台没有任何反应!请求也不超时,一直假死!

    问题三:自定义中间价Middleware,实现beforeafter 方法 无论怎样就是不进after 方法

    问题四:配合你们家的anima使用竟然不支持执行存储过程!

    问题五:优雅停机貌似也有问题,关闭进程竟然没有释放数据源连接池

    问题六:github issues 上好多人提出了各种资源泄漏问题竟然没有回复,也不知道修复了没有

    结论:不能使用生产!!!

    建议使用 https://github.com/perwendel/spark 或者 https://gitee.com/jfinal/jfinal

    bug 
    opened by Lzw2016 11
  • 筛选要执行的的method逻辑不严谨

    筛选要执行的的method逻辑不严谨

    blade-mvc-2.0.9.ALPHA1-sources.jar!/com/blade/server/netty/RouteMethodHandler.java:200 在该行中,对指定的方法进行执行,但如果有如下的情况,会出现无法正确识别方法的问题。

        @GetRoute(TaleConst.REGISTER)
        public String register(Request request) {
        ......
        }
        @PostRoute(TaleConst.REGISTER)
        @JSON
        public RestResponse<?> register(SweetPersonParam sweetPersonParam) {
        .....
        }
    

    原因是:调用了下面的这个方法进行执行方法的筛选:

    com.blade.reflectasm.MethodAccess#getIndex(java.lang.String, int)
    

    该方法在遇到入参数量一致的方法就直接返回了,并没有对参数列表进行检查,导致ClassCaseException的错误。 麻烦维护组确认下……

    维护组辛苦了

    enhancement 
    opened by iWuYc 10
  • 如何将输入流  返回个请求客户端 然后以流的形式下载文件

    如何将输入流 返回个请求客户端 然后以流的形式下载文件

    目前我的写法是这样的 但是并没有效果 StreamBody streamBody = new StreamBody(fileName, httpResponeBody.body().byteStream()); response.contentType("video/mp4"); response.body(streamBody);

    question 
    opened by songlongkuan 8
  • post json decodeHexByte

    post json decodeHexByte

    controller

    @PostRoute("/orders")
    @JSON
    public ResponseBase updateOrders(@BodyParam  OrderRequest req) {
        ResponseBase<Boolean> saveOrder = new ResponseBase<Boolean>();
        if (req==null){
            saveOrder.error();
            saveOrder.setCode(ResCode.IllegalArgument.getcode());
        }
        paymentService.saveOrders(req.getList(),req.getAlimamaAccount());
        saveOrder.success();
        return saveOrder;
    }
    

    postbody

    public class OrderRequest {
        private String alimamaAccount;
        private ArrayList<OrderRo> list;
    }
    
    public class OrderRo {
        //创建时间
        private LocalDateTime createTime;
        // 点击时间
        private LocalDateTime clickTime;
        // 商品信息
        private String productInfo;
        //'商品ID
        private String productId;
        //'掌柜旺旺
        private String wangwangName;
        //'所属店铺
        private String shopName;
        //'商品数
        private Float procuctCount;
        //'商品单价
        private BigDecimal productPrice;
        /**
         *ORDER_PAY("订单付款", 1),
         ORDER_INVALD("订单失效", 2),
         ORDER_BILL("订单结算",3),
         ORDER_SUCCESS("订单成功",4);
         */
        @BeanIgnore
        private String orderStatusStr;
    
        /**
         * ORDER_PAY("订单付款", 1),
         ORDER_INVALD("订单失效",2),
         ORDER_BILL("订单结算",3);
         */
        @BeanIgnore
        private String orderTypeStr;
    
        private Integer  orderStatus;
        private Integer orderType;
        // 收入比率
        private String incomePercent;
        // 分成比率
        private String getPercent;
        // 付款金额
        private BigDecimal payPrice;
        // 效果预估
        private BigDecimal estimatedEffect;
        // 结算金额
        private BigDecimal billPrice;
        // 预估收入
        private BigDecimal estimatedRevenue;
        // 结算时间
        private String billTime;
        // 佣金比率
        private String commissionRate;
        // 佣金金额
        private BigDecimal commissionAmount;
        // 补贴比率
        private String subsidyRatio;
        // 补贴金额
        private BigDecimal subsidyAmount;
        // 补贴类型
        private String subsidyType;
        // 成交平台
        private String dealingPlatform;
        // 第三方服务来源
        private String thirdServiceSource;
        // 订单编号
        private String orderId;
        // 类目名称
        private String cate;
        // 来源媒体ID
        private String mediaId;
        // 来源媒体名称
        private String mediaName;
        // 广告位ID
        private String adId;
        //广告位名称
        private String adName;
    }
    

    请求内容

    {
        "list": [
                 {
                "createTime": "2018-06-16 09:47:06",
                "clickTime": "2018-06-16 09:41:01",
                "productInfo": "\u3010\u72c2\u6b22\u4ef7\u3011\u7231\u9e2d\u719f\u98df\u5c0f\u5403\u9ebb\u8fa3\u9e2d\u7fc535g&times;15\u5305\u5364\u5473\u96f6\u98df\u771f\u7a7a\u88c5\u4f11\u95f2\u96f6\u98df",
                "productId": "527092752911",
                "wangwangName": "\u7231\u9e2d\u98df\u54c1\u65d7\u8230\u5e97",
                "shopName": "\u7231\u9e2d\u98df\u54c1\u65d7\u8230\u5e97",
                "procuctCount": 1.0,
                "productPrice": 100.0,
                "orderStatusStr": "\u8ba2\u5355\u4ed8\u6b3e",
                "orderTypeStr": "\u805a\u5212\u7b97",
                "incomePercent": "2.00 %",
                "getPercent": "100.00 %",
                "payPrice": 13.08,
                "estimatedEffect": 0.27,
                "billPrice": 0.0,
                "estimatedRevenue": 0.0,
                "billTime": "",
                "commissionRate": "1.50 %",
                "commissionAmount": 0.0,
                "subsidyRatio": "0.50 %",
                "subsidyAmount": 0.0,
                "subsidyType": "\u5929\u732b",
                "dealingPlatform": "\u65e0\u7ebf",
                "thirdServiceSource": "--",
                "orderId": "179004923251216232",
                "cate": "\u98df\u54c1-\u96f6\u98df/\u7279\u4ea7",
                "mediaId": "45798219",
                "mediaName": "\u8fd4\u5229\u8d2d_10_\u673a\u5668\u4eba",
                "adId": "676184983",
                "adName": "\u8fd4\u5229\u8d2d_10_\u673a\u5668\u4eba"
            }, {
                "createTime": "2018-06-16 09:47:06",
                "clickTime": "2018-06-16 09:41:01",
                "productInfo": "\u3010\u72c2\u6b22\u4ef7\u3011\u7231\u9e2d\u719f\u98df\u5c0f\u5403\u9ebb\u8fa3\u9e2d\u7fc535g&times;15\u5305\u5364\u5473\u96f6\u98df\u771f\u7a7a\u88c5\u4f11\u95f2\u96f6\u98df",
                "productId": "527092752911",
                "wangwangName": "\u7231\u9e2d\u98df\u54c1\u65d7\u8230\u5e97",
                "shopName": "\u7231\u9e2d\u98df\u54c1\u65d7\u8230\u5e97",
                "procuctCount": 1.0,
                "productPrice": 100.0,
                "orderStatusStr": "\u8ba2\u5355\u4ed8\u6b3e",
                "orderTypeStr": "\u805a\u5212\u7b97",
                "incomePercent": "2.00 %",
                "getPercent": "100.00 %",
                "payPrice": 13.08,
                "estimatedEffect": 0.27,
                "billPrice": 0.0,
                "estimatedRevenue": 0.0,
                "billTime": "",
                "commissionRate": "1.50 %",
                "commissionAmount": 0.0,
                "subsidyRatio": "0.50 %",
                "subsidyAmount": 0.0,
                "subsidyType": "\u5929\u732b",
                "dealingPlatform": "\u65e0\u7ebf",
                "thirdServiceSource": "--",
                "orderId": "179004923251216232",
                "cate": "\u98df\u54c1-\u96f6\u98df/\u7279\u4ea7",
                "mediaId": "45798219",
                "mediaName": "\u8fd4\u5229\u8d2d_10_\u673a\u5668\u4eba",
                "adId": "676184983",
                "adName": "\u8fd4\u5229\u8d2d_10_\u673a\u5668\u4eba"
            }
        ],
        "alimamaAccount": "\u7389\u4e66"
    }
    

    异常信息

    Caused by: java.lang.IllegalArgumentException: invalid hex byte '",' at index 488 of 'times;15\u5305\u5364\u5473\u96f6\u98df\u771f\u7a7a\u88c5\u4f11\u95f2\u96f6\u98df",
                "productId": "527092752911",
                "wangwangName": "\u7231\u9e2d\u98df\u54c1\u65d7\u8230\u5e97",
                "shopName": "\u7231\u9e2d\u98df\u54c1\u65d7\u8230\u5e97",
                "procuctCount": 1.0,
                "productPrice": 100.0,
                "orderStatusStr": "\u8ba2\u5355\u4ed8\u6b3e",
                "orderTypeStr": "\u805a\u5212\u7b97",
                "incomePercent": "2.00 %",
                "getPercent": "100.00 %",
                "payPrice": 13.08,
                "estimatedEffect": 0.27,
                "billPrice": 0.0,
                "estimatedRevenue": 0.0,
                "billTime": "",
                "commissionRate": "1.50 %",
                "commissionAmount": 0.0,
                "subsidyRatio": "0.50 %",
                "subsidyAmount": 0.0,
                "subsidyType": "\u5929\u732b",
                "dealingPlatform": "\u65e0\u7ebf",
                "thirdServiceSource": "--",
                "orderId": "179004923251216232",
                "cate": "\u98df\u54c1-\u96f6\u98df/\u7279\u4ea7",
                "mediaId": "45798219",
                "mediaName": "\u8fd4\u5229\u8d2d_10_\u673a\u5668\u4eba",
                "adId": "676184983",
                "adName": "\u8fd4\u5229\u8d2d_10_\u673a\u5668\u4eba"
            },    {
                "createTime": "2018-06-16 09:47:06",
                "clickTime": "2018-06-16 09:41:01",
                "productInfo": "\u3010\u72c2\u6b22\u4ef7\u3011\u7231\u9e2d\u719f\u98df\u5c0f\u5403\u9ebb\u8fa3\u9e2d\u7fc535g'
    	at io.netty.util.internal.StringUtil.decodeHexByte(StringUtil.java:234)
    	at io.netty.handler.codec.http.QueryStringDecoder.decodeComponent(QueryStringDecoder.java:351)
    	at io.netty.handler.codec.http.QueryStringDecoder.decodeComponent(QueryStringDecoder.java:309)
    	at io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder.decodeAttribute(HttpPostStandardRequestDecoder.java:628)
    	... 47 more
    

    单独提交一个订单数据是正常的,提交两个就会解析json失败

    at io.netty.util.internal.StringUtil.decodeHexByte(StringUtil.java:234)
    
    bug feedback 
    opened by shan333chao 7
  • Ioc依赖注入问题

    Ioc依赖注入问题

    hello,我看到源码中Ioc对bean的管理都是单例,static void injection(Ioc ioc, BeanDefine beanDefine)也是从容器中将已生成的实例注入目标字段,那就是说所有bean的字段都是单例的,那么BeanDefine中isSingle字段的意义是?如果需要每个请求一个实例bean,能支持么?

    discussion enhancement 
    opened by jetablezhu 7
  • Consuming Third party Rest resource

    Consuming Third party Rest resource

    Please provide the following information if applicable:

    • Operating system and its version: Ubuntu Version 20
    • Build tools: Maven
    • JDK version and blade version: JDK 8, latest blade version

    How can I consume a rest endpoint from a third party in blade just the way Spriingbbit provides RestTemplate and WebClient for such.

    Thanks.

    opened by Patrick5455 6
  • ctx.render() doesn't work from

    ctx.render() doesn't work from "java -jar"

    Describe the bug

    The ctx.render() method, as shown in the following code:

    public static void main(String[] args) {
        Blade.of()
    	 .get("/", ctx -> ctx.render("index.html"))
    	 .start(Application.class, args);
    }
    

    it DOES work when run from the IDE (Run as Java Application from Eclipse) it DOES NOT work when run from the CLI (java -jar target\blade-app.jar)

    To Reproduce

    Run the attached package with java -jar.

    app.zip

    Screenshots

    From CLI it doesn't work: immagine

    From Eclipse it works:

    immagine

    Additional context

    I suspect everything is bound to the app.classpath.

    Run with the IDE:

    c.b.s.n.NettyServer : app.classpath    => C:/Users/a.ligios/git/blade/app/target/classes/
    

    Run with java -jar:

    c.b.s.n.NettyServer : app.classpath    => C:\Users\a.ligios\git\blade\app\target\blade-app.jar 
    
    feedback 
    opened by andrea-ligios 6
  • 静态资源问题

    静态资源问题

    Please provide the following information if applicable: 我跑了demo,设置了静态资源的路径,但是返回得到的是404,

    • deepin 15.5
    • Build tools (gradle)
    • JDK version(1.8) and blade version(e.g 2.0.8-R1)
    question 
    opened by starfire-lzd 6
  • ⬆️ Bump netty-codec-http from 4.1.76.Final to 4.1.86.Final

    ⬆️ Bump netty-codec-http from 4.1.76.Final to 4.1.86.Final

    Bumps netty-codec-http from 4.1.76.Final to 4.1.86.Final.

    Commits
    • cde0e2d [maven-release-plugin] prepare release netty-4.1.86.Final
    • fe18adf Merge pull request from GHSA-hh82-3pmq-7frp
    • cd91cf3 Merge pull request from GHSA-fx2c-96vj-985v
    • 7cc8428 fixing some naming and typos that caused wrong value to be updated (#13031)
    • 22d3151 Save promises type pollution due to interface type checks (#12980)
    • 1baf9ef Enable SocketHalfClosedTest for epoll (#13025)
    • 91527ff Correctly handle unresolvable InetSocketAddress when using DatagramChannel (#...
    • b64a6e2 Revert#12888 for potential scheduling problems (#13021)
    • 3bff0be Replace LinkedList with ArrayList (#13016)
    • d24defc WebSocketClientHandshaker: add public accessors for parameters (#13009)
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Add ability to customize TLS implementation

    Add ability to customize TLS implementation

    The current mechanism for configuring SSL allows me to specify a certificate file, a private key file and a password. However there are times it may be desirable to use a Keystore, a Truststore, or both depending on how a user chooses to provide their certificates.

    I added an interface INettySslCustomizer which provides a single method: public SslContext getCustomSslContext(Blade blade) This is registered with the Blade class using the setNettySslCustomizer(INettySslCustomizer customizer).

    In NettyServer I added a few lines to check for this customizer and if present we call it to ask for a SslContext. Otherwise, if the regular properties are present we will attempt to just do the current way.

    This way without touching the Blade code a user can design and register a customizer and prepare his SslContext in a number of ways that add functionality.

    I updated the README.md to document usage.

    opened by txwordtamer 0
  • Fix webjars when file is null

    Fix webjars when file is null

    Add a webjars dependency for example:

    <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.6.1</version>
    </dependency>
    

    Now verify the lib adding the script into a HTML page: <script src="/webjars/jquery/3.6.1/jquery.min.js"></script> In the console we see: image

    opened by denjossal 0
  • 2.1.2 RELEASE  static resources 500   substring error  -1

    2.1.2 RELEASE static resources 500 substring error -1

    • System Version (e.g. Mac Os 10.14.3):
    • Build tools (e.g. maven/gradle):
    • JDK Version (e.g. 1.8):
    • Blade Version (e.g. 2.1.2.RELEASE):

    Describe the bug

    ERROR [ worker@thread-1 ] c.h.b.m.h.DefaultExceptionHandler : java.lang.StringIndexOutOfBoundsException: String index out of range: -1 at java.lang.String.substring(String.java:1931) at com.hellokaton.blade.server.StaticFileHandler.getResourceListing(StaticFileHandler.java:470) at com.hellokaton.blade.server.StaticFileHandler.writeJarResource(StaticFileHandler.java:380) at com.hellokaton.blade.server.StaticFileHandler.writeWithJarFile(StaticFileHandler.java:343) at com.hellokaton.blade.server.StaticFileHandler.handle(StaticFileHandler.java:135) at com.hellokaton.blade.server.HttpServerHandler.executeLogic(HttpServerHandler.java:139) at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616) at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:591) at java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:456) at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164) at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at java.lang.Thread.run(Thread.java:748)

    To Reproduce

    Steps to reproduce the behavior:

    1. Operating system and its version
    2. Build tools (e.g. maven or gradle)
    3. JDK version and blade version(e.g 1.8)
    4. Blade Version (e.g. 2.1.2.RELEASE):
    5. See error

    Expected behavior:

    A clear and concise description of what you expected to happen.

    Screenshots:

    If applicable, add screenshots to help explain your problem.

    Additional context:

    Add any other context about the problem here.

    opened by kalshen2018 2
  • failed: Too many open files error issue

    failed: Too many open files error issue

    • System Version: Ubuntu 18.04
    • Build tools: maven
    • JDK Version: 1.8
    • Blade Version: 2.0.15.RELEASE

    Describe the bug

    I get this error where the running blade instance throws an exception with message: failed: Too many open files

    on a closer look i see: com.blade.exception.TemplateException: java.nio.file.FileSystemException: /home/server/resources/templates/index.html: Too many open files, this file does exist on that path in production and it is the only file within that folder.

    when it happens the entire application breaks.

    Screenshots:

    Screenshot 2022-08-06 at 7 48 06 AM

    opened by ebottabi 1
Releases(2.1.2.RELEASE)
Owner
Blade Framework
Simple elegant, high-performance full-stack web framework
Blade Framework
A web MVC action-based framework, on top of CDI, for fast and maintainable Java development.

A web MVC action-based framework, on top of CDI, for fast and maintainable Java development. Downloading For a quick start, you can use this snippet i

Caelum 347 Nov 15, 2022
jetbrick web mvc framework

jetbrick-webmvc Web MVC framework for jetbrick. Documentation http://subchen.github.io/jetbrick-webmvc/ Dependency <dependency> <groupId>com.githu

Guoqiang Chen 25 Nov 15, 2022
Ninja is a full stack web framework for Java. Rock solid, fast and super productive.

_______ .___ _______ ____. _____ \ \ | |\ \ | | / _ \ / | \| |/ | \ | |/ /_\ \ / | \

Ninja Web Framework 1.9k Jan 5, 2023
The modular web framework for Java and Kotlin

∞ do more, more easily Jooby is a modern, performant and easy to use web framework for Java and Kotlin built on top of your favorite web server. Java:

jooby 1.5k Dec 16, 2022
ZK is a highly productive Java framework for building amazing enterprise web and mobile applications

ZK ZK is a highly productive Java framework for building amazing enterprise web and mobile applications. Resources Documentation Tutorial ZK Essential

ZK 375 Dec 23, 2022
☄️ LiteCommands - Command framework for Velocity and your other implementations.

☄️ LiteCommands Command framework for Velocity and your other implementations. Helpful links: Support Discord GitHub issues Panda Repository (Maven or

Norbert Dejlich 38 Dec 1, 2022
A server-state reactive Java web framework for building real-time user interfaces and UI components.

RSP About Maven Code examples HTTP requests routing HTML markup Java DSL Page state model Single-page application Navigation bar URL path UI Component

Vadim Vashkevich 33 Jul 13, 2022
Jersey is a REST framework that provides JAX-RS Reference Implementation and more.

About Jersey Jersey is a REST framework that provides JAX-RS Reference Implementation and more. Jersey provides its own APIs that extend the JAX-RS to

Eclipse EE4J 621 Jan 5, 2023
Javalin - A simple web framework for Java and Kotlin

Javalin is a very lightweight web framework for Kotlin and Java which supports WebSockets, HTTP2 and async requests. Javalin’s main goals are simplicity, a great developer experience, and first class interoperability between Kotlin and Java.

David (javalin.io) 6.2k Jan 6, 2023
Spring Framework

Spring Framework This is the home of the Spring Framework: the foundation for all Spring projects. Collectively the Spring Framework and the family of

Spring 50.4k Jan 9, 2023
Play Framework

Play Framework - The High Velocity Web Framework The Play Framework combines productivity and performance making it easy to build scalable web applica

Play Framework 12.3k Dec 29, 2022
The Grails Web Application Framework

Build Status Slack Signup Slack Signup Grails Grails is a framework used to build web applications with the Groovy programming language. The core fram

grails 2.7k Jan 5, 2023
Vaadin 6, 7, 8 is a Java framework for modern Java web applications.

Vaadin Framework Vaadin allows you to build modern web apps efficiently in plain Java, without touching low level web technologies. This repository co

Vaadin 1.7k Jan 5, 2023
Apache Wicket - Component-based Java web framework

What is Apache Wicket? Apache Wicket is an open source, java, component based, web application framework. With proper mark-up/logic separation, a POJO

The Apache Software Foundation 657 Dec 31, 2022
Micro Java Web Framework

Micro Java Web Framework It's an open source (Apache License) micro web framework in Java, with minimal dependencies and a quick learning curve. The g

Pippo 769 Dec 19, 2022
True Object-Oriented Java Web Framework

Project architect: @paulodamaso Takes is a true object-oriented and immutable Java8 web development framework. Its key benefits, comparing to all othe

Yegor Bugayenko 748 Dec 23, 2022
Firefly is an asynchronous web framework for rapid development of high-performance web application.

What is Firefly? Firefly framework is an asynchronous Java web framework. It helps you create a web application Easy and Quickly. It provides asynchro

Alvin Qiu 289 Dec 18, 2022
An Intuitive, Lightweight, High Performance Full Stack Java Web Framework.

mangoo I/O mangoo I/O is a Modern, Intuitive, Lightweight, High Performance Full Stack Java Web Framework. It is a classic MVC-Framework. The foundati

Sven Kubiak 52 Oct 31, 2022
A simple expressive web framework for java. Spark has a kotlin DSL https://github.com/perwendel/spark-kotlin

Spark - a tiny web framework for Java 8 Spark 2.9.3 is out!! Changeset <dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-core</a

Per Wendel 9.4k Dec 29, 2022