Skip to content

用ninja开发网站,先来看看ninja的路由机制---Routing #1

@JavaProgrammerLB

Description

@JavaProgrammerLB

Ninja web framework是一个java web框架,本文档为ninja的文档中文翻译,大家在阅读的过程中如果发现问题,可以给我提出来,我们共同学习ninja web framework框架

Routing
Ninja features one central route file. We think this is important, because you immediately can see what routes your application provides. It especially facilitates designing a nice restful application and Api, because you have a good overview what is going on.

Routing

Ninja的特性"一个中央路由文件"。我们认为这是重要的,这样你马上可以看到你的应用提供了怎样的路由。它特别有利于设计一个优秀的restful应用和API,因为你对于即将发生什么早已经胸有成竹。

Basics

The route file is a plain old java file living at conf/Routes.java.

Routes.java implements the interface ApplicationRoutes.java. This interface defines a method called public void init(Router router) {…} which allows us to map incoming requests to controllers and their methods.

Basics

路由文件是一个简单原始的Java文件在conf/ routes.java

Routes.java实现接口Applicationroutes.java。这个接口定义的方法称为public void init(路由器路由器){……}这让我们地图的传入请求的控制器及其方法。

public class Routes implements ApplicationRoutes {
@OverRide
public void init(Router router) {

    // a GET request to "index" will be handled by a class called
    // "AppController" its method "index".
    router.GET().route("/index").with(AppController.class, "index");
    ...

}

}
The init(…) method provides us with the Router and router allows us to define what happens for GET, POST, PUT, OPTIONS, HEAD and DELETE requests.

在Router类中提供了inti(...)方法,router允许我们去定义GET,POST,PUT,OPTIONS,HEAD和DELETE等不同的请求将发生什么。

And if you want to route a http method not yet supported by Ninja out of the box you can always use route.METHOD(“MY_CUSTOM_HTTP_METHOD”).route(…)….

如果你想路由一个还没有被Ninja支持的http方法,立即可用的是你总是可以使用route.METHOD("MY_CUTOM_HTTP_METHOD").route(...)...

Routes are matched top down. If an incoming request potentially maps to two routes, the route defined first is executed.
Routes将会从上到下配对。如果一个请求潜在的映射到两个路线,会被执行的路线是第一条定义的线路。
With result directly

直接给出结果

You can use Result object to render a static page simply without controller.

不使用控制器类,使用Result对象是就可以容易的提供一个静态页面

public class Routes implements ApplicationRoutes {
@Overridepublic void init(Router router) {
// a GET request to "/" will be redirect to "/dashboard"
router.GET().route("/").with(Results.redirect("/dashboard"));
// show a static page
router.GET().route("/dashboard").with(Results.html().template("/dashboard.html"));...}
}

Regex in your routes

在你的路线中使用正则表达式

Routes can contain arbitrary Java regular expressions. You can use these regular expressions simply by putting them into the route definition.

Routes可以包含任何的Java正则表达式。要使用那些正则表达式你可以直接把他们放在路线的定义中。

Some examples:

下面是一些例子:

// matches for instance "/assets/00012", "/assets/12334", ...
router.GET().route("/assets/\d_").with(AssetsController.class, "serveDigits");
// matches for instance "/assets/myasset.xml", "/assets/boing.txt", ...
router.GET().route("/assets/.").with(AssetsController.class, "serveArbitrary");
// 匹配这样的实例: "/assets/00012", "/assets/12334", ...
router.GET().route("/assets/\d
").with(AssetsController.class, "serveDigits");
// 匹配这样的实例: "/assets/myasset.xml", "/assets/boing.txt", ...
router.GET().route("/assets/._").with(AssetsController.class, "serveArbitrary");
In the first example \d* tells the router to match digits (defined by \d) of arbitrary length (defined by ). In the second example . lets the router match arbitrary characters (.) of arbitrary length (defined by *).

在第一个例子当中\d_告诉路由器去匹配任意长度的(通过_定义)的十进制数(通过\d定义)。第二个例子中._让路由器匹配任意长度的(通过_定义)任意的字符(通过.定义)。

This example also shows what happens if two routes match. For instance a request to /assets/00012 is matched by both route definitions. In that case the first matching route from top will be executed. In our case method serveDigits.

这个例子也给我们展示了如果一个请求同时匹配了两条线路会发生什么。对于请求到/assets/00012的例子,这个请求同时匹配到两条定义的线路。在这个实例中按照从上往下的顺序第一条匹配的路线会被执行,本例中具体被执行的方法是serveDigits.

More on regular expressions:

访问如下的网站了解更多:

http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html

Getting variable url parts into your controller method

把可变的url部分放到你的控制器方法中

A controller usually not only renders stuff, but also takes some inputs and does something with them.

一个控制器不仅提供一个东西,也可以提供一些输入,用这些输入做些事。

Let’s assume we got a request like that:

假设我们得到了一个如下的请求:

GET /user/12345/my@email.com/userDashboard
Looks like a GET request to the userDashboard. The request seems to contain an id and an email. As application developer you want to have a convenient way to get id and email inside your controller. Ninja allows you to define and name parts of your routes via curly braces {…}:

这个看起来像是指向userDashboard的GET请求。这个请求似乎包含一个id和一个emaiil。作为一个应用的开发者你想要在你的控制器中有一个方便的方式去获得id和email。Ninja允许你通过大括号定义和命名你的线路。

router.GET().route("/user/{id}/{email}/userDashboard").with(ApplicationController.class, "userDashboard");
We can then inject id and email into our controller via annotation @PathParam.

在之后我们可以通过@PathParam声明注入id和email到我们的控制器中。

package controllers;
@Singletonpublic
class AppController {
public Result userDashboard(@PathParam("id") String id,@PathParam("email") String email) {
//do something with the parameters...
}
}
By default Ninja replaces curly braces with the following regex to match a variable part: ([^/]). That means that variable parts match arbitrary characters but do not span over any path separators "/". If you need more control please you'll find more in the next section.
默认的Ninja不使用大括号你可以使用如下的正则表达式来匹配一个可变的部分:([^/]
)。这意味着可变的部分匹配任意字符但是不从任何的路径分隔符"/"跨越。如果你需要更多的控制敬请期待下一部分。
Regular expressions in variable route parts

在可变的线路部分使用正则表达式

Ninja allows you to specify regular expressions that will be used to match arbitrary parts of your url.

Ninja允许你详述将被使用到匹配你的url中的任意部分的正则表达式。

The syntax is {PRAMETER_NAME: REGEX} (with a whitespace after the “:”).

语法是{PARAMETER_NAME: REGEX}(在冒号后面有一个空格)

For instance a route like /assets/{fileName: .*} will match everything after /assets/. Imagine a request to /assets/css/app.css. This request will be handled by the route and accessing the path parameter fileName will return css/app.css.

实现一个像 /assets/{fileName: .*}这样的路线会匹配满足 /assets/.的一切。想象有一个指向/assets/css/app.css的请求。这个请求会被这条路线处理而且会连接路径参数文件会返回css/app.css。

Routes can contain multiple variable parts with regular expressions.

Routes中可以包含多个由正则表达组成的可变部分。

For example, for a request to /categories/1234/products/5678, where category is expected to be an integer value and product to be either integer or string value, you can define routes like that:

例如,对于到/catagories/1234/products/5678的请求,其中category需要一个int值,product可以是int或是String型数据,你可以定义如下的路线:

router.GET().route("/categories/{catId: [0-9]+}/products/{productId: [0-9]+}").with(ProductController.class, "product");
router.GET().route("/categories/{catId: [0-9]+}/products/{productName: .*}").with(ProductController.class, "productByName");
The request above will be handled by first route, and request to /categories/1234/products/mouse will be handled by second route.

上面说到的请求会被第一条线路处理,到/categories/1234/products/mouse的请求则会被第二条线路处理。

Values of variable parts of a route are injected into our controller (explained above) and are implicitly validated with regular expressions. So our controller for routes above would be like that (look at @PathParam argument types):

可变部分的值被注入到我们的控制器(在上面解释过)而且连带着讲了正则表达式。因此上面对于路线的控制会像这样(注意看@PathParam参数的类型):

package controllers;
@Singletonpublic class ProductController {
public Result product(@PathParam("catId") int catId,@PathParam("productId") int productId) {
// find product by id in given category
}
public Result productByName(@PathParam("catId") int catId,@PathParam("productName") String productName) {
// find product(s) by name in given category
}
}
Note at how regular expressions in routes can be used to validate path parameters and to define fine grained routing.

注意其中正则表达式是怎么被使用到去验证路径参数和定义好清晰的路由。

You can use any Java compliant regular expressions for matching. Please refer to the official documentation at http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html.

你可以使用任何你顺手的正则表达式来进行匹配。想了解正则表达式的官方文档查看如下链接:

http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html.

Injecting objects into the Router

注入对象到Router

Because the Route.java is just a plain old java file we can also inject arbitrary stuff into it (via Guice).

因为Route是相对古老的java文件,我们也可以注入进去任意的东西(通过Guice)

A popular use case is to inject ninjaProperties. This allows us to activate / deactivate certain routes based on the environment we are in.

一个常见的使用案例是注入ninjaProperties。这允许我们去激活/不激活特定的路线根据我们所在的环境。

The following example shows that a setup route is not available when running in production:

接下来的例子展示了在产品运行中一个设置为不可用的路线:

public class Routes implements ApplicationRoutes {
@InjectNinjaProperties ninjaProperties;
@OverRide
public void init(Router router) {
// a GET request to "index" will be handled by a class called
// "AppController" its method "index".router.GET().route("/index").with(AppController.class, "index");
...
// only active when not in production mode:if (!ninjaProperties.isProd) {
router.GET().route("/setup").with(AppController.class, "setup");
}
}
}
Reverse routing

路由反转

Let’s say you want to know the final route of a class ApplicationController.class, method “index”. Assume that the original raw route looked like a simple “/”.

You can get the final url by injecting the router into your controller and then calling getReverseRoute

让我们看到你想知道一个类ApplicationController.class,“index”方法最终的路线。假设原始的路线看起来像一个简单的"/"。你可以通过把路线注入到你的控制器中获得最终的url,然后调用getReverseRoute

@InjectRouter router;
...
public void myMethod() {
// will result into "/"String generatedReverseRoute = router.getReverseRoute(ApplicationController.class, "index");
...
} Now consider a more complex example. Say the original raw route contained placeholders on the following form: /user/{id}/{email}/userDashboard. You can now ask the router for the final url, but you must provide a map containing mappings from placeholders to final values. If the key cannot be found as placeholders their value will be added as query parameters:
现在我们来考虑一个更复杂的例子。接下来看以这种形式包含占位符的原始路由:/user/ {id} /{email}/ userdashboard 。你现在可以让Router对象给你最后的url,但是你必须提供一个包含从占位符映射到最终值得map。如果不能找到替代占位符的key,占位符的值将被添加做查询参数。

@InjectRouter router;...public void myMethod() {

map = Maps.newHashMap();
map.put("id","myId");
map.put("email","myEmail");
map.put("paging_size","100");
map.put("page","1");// this will result into "/user/myId/myEmail/userDashboard?paging_size=100&page=1"String generatedReverseRoute 
    = router.getReverseRoute(ApplicationController.class,"userDashboard", 
        map);...}      

A note on encoding / decoding

在编码/解码的一个note

Encoding / Decoding of Urls is not as easy as you think it is. Ninja tries to simplify everything as much as possible, but as user of the Api you have to know what you are submitting to Ninja.

Url的编码/解码不是你想象中的那么容易。Ninja试图尽可能的简化一切,但是作为ninja API的用户你必须清楚你提交的内容。

We recommend the following excellent article from Lunatech before you use encoding / decoding actively in your application.

在你的应用程序使用编码/解码之前,我们推荐阅读这篇来自Lunatech的优秀文章。

Let’s reconsider the controller method from above:

让我们重新考虑上面的控制器方法:

package controllers;
@singleton
public class ApplicationController {
public Result index(@PathParam("id") String id,@PathParam("email") String email,@param("debug") String debug) {
//使用参数……
}
}
You can expect that String id and String debug are both correctly decoded values. BUT This assumes that you are encoding the values correctly on the client side. And encoding is different for query parameters or stuff in the path. Do not even think about using URLEncoder for encoding urls. This is wrong.

你可以期望字符串id 和字符串debug都正确解码值。但这里的前提是你在客户端正确的编码了传递的值。编码是不同于查询参数或路径下的其他事情。甚至不要考虑用URLEncoder来编码url。这是错误的。

Simple example that outlines some of the difficulties: Think of a route “/user/{id}/userDashboard”.

接下来看一些和大多数的例子有一些不同的例子:考虑这样一个路由:"/user/{id}/userDashboard".

Let’s say your id is “rootuser/domain”. If you do not encode the slash in the middle you end up with a url like /user/rootuser/domain/userDashboard. And the this url does not match the route because of the “/”.

假如说你的id 是“rootuser /domain”。如不你不解码中间的斜线,你将会得到一个这样的url:/user/rootuser/domain/userDashboard。但是这个url不能匹配路由对象因为“/”。

Therefore you have to encode your id correctly. In that case it would be: rootuser%2Fdomain. When the user then visits /user/rootuser%2Fdomain/userDashboard the route matches and a @PathParam(“id”) would then be rootuser/domain as it is decoded by Ninja.

因此你不得不正确的解码你的id。在这种情况应该使用:rootuser%2Fdomain。当用户访问/user/ rootuser%2Fdomain/ userdashboard时路由成功匹配而且通过Ninja一个@ pathparam(“id”)将被作为rootuser /domain的解码。

In principle it is really simple. But it is even simpler to mess encoding / decoding up. The article from Lunatech mentioned earlier is awesome and explains everything.

原则上它真的很简单。但是解码/编码也是特别容易弄混。从Lunatech前面的文章提到了而且解释了一切。

JAX-RS-style Annotated Routes in Ninja (optional)

在Ninja中JAX-RS-style类型注解(可选)
Ninja-jaxy-routes allows you to register your routes using annotations similar to JAX-RS.

Ninja-jaxy-routes允许你使用类似JAX-RS的方式注解的使用去注册你的路由。

You may use the standard Ninja route registration in combination with this route builder or you may replace all your route registrations with annotations.

你可以使用标准Ninja路由注册方式结合这种路由建造模式或者你可以使用声明替换你所有的路由注册方式。

NOTE: Your annotated controllers must be located somewhere within your application’s configured controller package or a subpackage thereof.

注意:你声明的控制器必须放在你应用配置控制控制器的包或应用配置控制器的子包里。

Add the ninja-jaxy-routes dependency

在maven配置中增加ninja-jaxy-routes 依赖

org.ninjaframework
ninja-jaxy-routes
${ninja.version}


org.ninjaframework
ninja-jaxy-routes
${ninja.version}

Initialize JaxyRoutes in your conf.Routes class.

在你的conf路由类中初始化JaxyRoutes。
@Inject
JaxyRoutes jaxyRoutes;
@OverRide
public void init(Router router) {
jaxyRoutes.init(router);
}
@Inject
JaxyRoutes jaxyRoutes;

@OverRide
public void init(Router router) {

jaxyRoutes.init(router);

}
Annotate Your Controllers

对你的控制器进行注解
Now you are ready to start annotating your controllers.

现在你已经准备好注解你的控制器了。

Paths

路径
Ninja-jaxy-routes supports multiple @path specs per controller method and also supports controller class inheritance.

Ninja-jaxy-routes支持多个@path规范每一个控制器方法,而且支持控制器类的继承。

The following example will register two GET routes /base/middle/app/get and /base/middle/app/retrieve for the same controller method.

接下来的例子将对于同一个控制器方法注册两个GET路由:/base/middle/app/get和/base/middle/app/retrieve.

@path("/base")
class Base {
}
@path("/middle")
class Middle extends Base {
}
@path("/app")
class App extends Middle {
@path({"/get", "/retrieve"})
@get
Result get() {
return Results.text().renderRaw("Yahoo!");
}
}
@path("/base")
class Base {
}

@path("/middle")
class Middle extends Base {
}

@path("/app")
class App extends Middle {

@Path({"/get", "/retrieve"})
@GET
Result get() {
    return Results.text().renderRaw("Yahoo!"); 
}        

}
If the Base and Middle parent classes had each specified multiple paths, all permutations of the complete routes would be registered too.

如果Base和Middle的父类每个都指定多个路劲,所有的完成的路由的排列分布都会被注册。

Http Methods

Http方法
By default, all routes are assumed to be GET routes unless they are specifically annotated.

默认情况下,所有的路由假设为GET 路由除非它们有特别的注解。

The following common HTTP method annotations are available:

如下的普通HTTP方法注解是可用的:

@delete
@get
@Head
@options
@patch
@post
@put
@delete
@get
@Head
@options
@patch
@post
@put
If the built-in methods are insufficient, you may implement your own custom HTTP methods:

如果内嵌的方法不够充足,你可以实现你自定义的HTTP方法:

@target(ElementType.METHOD)
@retention(RetentionPolicy.RUNTIME)
@HttpMethod("CUSTOM")
public @interface CUSTOM {
}
@target(ElementType.METHOD)
@retention(RetentionPolicy.RUNTIME)
@HttpMethod("CUSTOM")
public @interface CUSTOM {
}
Registration Order

注册顺序
Since your controllers and methods are reflectively loaded we can not expect a predictable route registration order from the JVM.

尽管你的控制器和方法是放射加载的,但是我们不能通过java虚拟机预测路由注册顺序。

To compensate for this, you may specify the @order annotation on a controller method to dictate the ordering of routes.

为了补偿这一点,你可以在一个控制器方法指定@order声明来规定路由的顺序。

It is not necessary to specify an @order for every method.
Lower numbers are registered first, higher numbers later.
If two methods have the same order, the registration order is determined by String comparison of the complete method address (controller+method).
1.不是每个方法都必须指定一个@order声明。

2.数字小的先注册,数字大的在后面注册。

3.如果两个方法有同样的顺序,注册顺序由完成的方法地址(控制器+方法)的String值来进行比较。

Here is an example of specifying an @order.

下面是一个指定@order注解的例子。

@path({"/get", "/retrieve"})
@get
@order(10)
Result something() {
}
@path({"/get", "/retrieve"})
@get
@order(10)
Result something() {
}
Runtime Mode Inclusions/Exclusions

运行时模式包含/不包含
You may include/exclude routes based on the Ninja runtime mode.

根据Ninja的运行时模式你可以包括/排除路由。

If no mode annotations are specified, then the route is available in all modes.

You may specify multiple mode annotations on a controller method.

@dev

@prod

@test

1.如果没有指定任何的模式注解,这个路线在所有的模式中都可用、

2.你可以在一个控制器方法中指定多个模式声明

3.@dev

4.@prod

5.@test

Here is an example of specifying @dev and @test.

这是一个指定@dev@test的例子。

@path("/diagnostics")
@get
@dev @test
Result diagnostics() {
}
@path("/diagnostics")
@get
@dev @test
Result diagnostics() {
}
NinjaProperties Inclusions/Exclusions

Ninja参数包含与排除
It is also possible to include/exclude a route based on a NinjaProperties key.

If the key does not exist in your runtime config, the route is not registered.

通过一个Ninja参数key包含或者排除一个路由也是可能的。

如果这个key不存在于你的运行时配置,这个路由不会被注册。

@path("/sneaky")
@get
@requires("sneaky.key")
Result somethingSneaky() {
}
@path("/sneaky")
@get
@requires("sneaky.key")
Result somethingSneaky() {
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions