目录
1. 框架
1.1 REST风格 URLS
1.2 标准 URLS
2. 控制器
2.1 控制器的标注
2.2 从请求中获取参数
2.3 在响应中附加参数
2.4 显示页面
3. 序列化
4. 服务配置
4.1 配置属性
5. 资源搜索风格
5.1 功能导向风格
5.2 基于模型的功能导向风格
5.3 模型导向风格
6. 非web客户端
1. 框架
SerfJ提供了一个MVC架构,但它不包含任何模型,其主要特点是使用控制器,通过这些控制器管理REST请求。
控制器代表了应用程序中的可用资源,通过REST风格的请求将消息发送给这些资源 。当一个请求发送给serfj
的主servlet(net.sf.serfj.RestServlet),一个能够处理这一请求的资源(控制器)被调用,并响应
客户端请求。这个响应可以是一个网页,一个序列化的对象,或HTTP状态代码204(这意味着没有任何内容)。
1.1 REST风格 URLS
由于REST请求控制了SerfJ应用程序的处理过程,因此阅读本节是非常重要的。不过这些概念解释起来很简单,
所以一切都将很容易理解。
SerfJ支持的HTTP方法有:
GET: 用于查询资源信息,或显示网页等。
POST: 用于创建新的资源。
PUT: 用于更新资源。
DELETE: 用于删除资源。
例如,如果您想显示的页面包含有更新或创建一个资源的Form,你需要发送一个GET请求,而不是一个PUT请求。
但Form的提交按钮(用于更新资源信息)将发送PUT请求。
REST请求的 URLS 模板是:
/accounts?query_string
/accounts/identifier?query_string
/accounts/non-standar-action?query_string
/accounts/identifier/edit?query_string
/accounts/identifier/newResource?query_string
/accounts/identifier/non-standar-action?query_string
注意资源的名称必须是复数,标识符必须以数字开头。但SerfJ是能够解析嵌套资源的URL:
PS:嵌套资源以最后一个资源为依据确定执行哪个控制器的哪个方法。
/banks/identifier/accounts?query_string
/banks/identifier/accounts/identifier?query_string
/banks/identifier/accounts/non-standar-action?query_string
/banks/identifier/accounts/identifier/edit?query_string
/banks/identifier/accounts/identifier/newResource?query_string
/banks/identifier/accounts/identifier/non-standar-action?query_string
如果你使用一个带扩展您的 URL,那么返回结果将不会是一个网页,而是一个序列化的对象。因此,根据所使用的扩展,
你可以收到为JSON,XML或序列化的对象:
/accounts.xml?query_string
/accounts/identifier.json?query_string
/accounts/non-standar-action.pdf?query_string
/accounts/identifier/non-standar-action.txt?query_string
译者注: REST URLS 模板应该是如下格式:
普通:/资源名称[[.扩展名][/标识符[.扩展名]][/方法名[.扩展名]]][?查询字符串]
嵌套:/资源1名称/资源1标识符/资源2名称/资源2标识符.../资源n名称/资源n标识符
1.2 标准 URLS
有一些 URLS,将始终调用相同的控制器的方法:
HTTP Method URLController's methodViewMeaning
GET /accountsindex()index显示每一条资源
POST /accountscreate()create新建一条资源
GET /accounts/1show()show显示一条ID为1的资源
PUT /accounts/1update()update更新一条ID为1的资源
DELETE /accounts/1delete()delete删除一条ID为1的资源
GET /accounts/newResourcenewResource()new显示一个用来创建资源的表单
GET /accounts/1/editedit()edit显示一个表单,用来更新ID为1资源的属性
2. 控制器
控制器是SerfJ的主要特色,REST请求被分派给他们,他们应答这些请求。应答可能是一个页面,一个序列化的对象,或什么也没有(比如HTTP 状态代码)。
有两种方法写一个控制器(下一个SerfJ版本会有更多方法),一个是扩展net.sf.serfj.RestController类,另一个是写了一个JavaBean。
最后一种情况很特殊,因为控制器将无法做一些动作比如从请求中获取参数,或者重定向到另一个网页,或者发送到JSP页面中的对象。
就现在来看,扩展RestController类是写一个控制器的最好方式。虽然处理的请求中不一定包含有参数,但方法可以返回对象并抛出异常。
例如,如果我们需要一个控制器出席/账户的要求,我们必须写一个这样的类:
public class Account extends RestController {
}
2.1 控制器的标注
有几个注释,告诉控制器哪些方法能接受哪一类HTTP请求。
@GET: 方法处理 GET 请求.
@POST: 方法处理 POST 请求.
@PUT: 方法处理 PUT 请求.
@DELETE: 方法处理 DELETE 请求.
此外,还有一个注释@DoNotRenderPage告诉控制器的方法执行后,没有将呈现页,但将回答一个HTTP204代码。如果方法返回一个对象而不是不使一个页面,
那它并不需要注明@DoNotRenderPage。只有方法不返回任何值(void方法),而开发者又不希望呈现一个结果页时,必须注明。
2.2 从请求中获取参数
很明显,我们之前写的类不会做任何事情,它也不会应答任何要求。让我们在此基础上写更多的代码。如果我们需要一个方法来更新一些account信息
(PUT /account/ 1),我们可以写一个这样的方法:
public class Account extends RestController {
@PUT
public void updateAccount() {
String accountId = this.getId("account");
}
}
我们看到,该方法中调用 getId(String)方法可以获取帐户的标识符。让我们看看如何得到嵌套的资源(PUT /banks/1/accounts/2)中其他账
户的标识符。
public class Account extends RestController {
@PUT
public void updateAccount() {
String accountId = this.getId("account");
String bankId = this.getId("bank");
}
}
如果你在请求中包含了很多参数,同样可以在这个方法中获取它们。这些参数可以位于查询字符串中,也可能位于请求正文中。
public class Account extends RestController {
@PUT
public void updateAccount() {
String accountId = this.getId("account");
String someInfo = this.getStringParam("some_info_param_name");
}
}
如果发送和接受的参数不是字符串而是对象, 我们需要在发送前先把对象序列化为Base64字符串,在控制器方法中接受到参数时再反序列化为对象。
SerfJ提供了一个类对象与Base64字符串之间序列化/反序列化的工具。你可以在任何需要的地方使用它。
public class Account extends RestController {
private Base64Serializer serializer = new Base64Serializer();
@PUT
public void updateAccount() {
String accountId = this.getId("account");
String someInfo = this.getStringParam("some_info_param_name");
Balance balance = (Balance) serializer.deserialize(this.getStringParam("balance"));
}
}
2.3 在响应中附加参数
现在我们知道如何获得请求参数了,但有时我们需要将对象发送到一个JSP,例如。显然,这些对象必须实现java.io.Serializable接口。
public class Account extends RestController {
@PUT
public void updateAccount() {
Account account = // some code to get an account
this.addObject2Request("my_object_param_name", account);
}
}
这将是很常见的返回对象的方法。正如我们在1.1节中看到,这些方法会被扩带有展名的 REST URLS 请求调用。扩展名指示框架使用什么样的
序列化对请求做出应答。 SerfJ提供了三种不同的序列化扩展XML,JSON或Base64(对应扩展名.XML,JSON,.64),但开发人员可以编写自
己的序列化扩展(见第3节)。
例如,应答 /accounts/1/balance.xml 这样的 URLS 请求的方法有两种写法,我们先看第一种:
public class Account extends RestController {
@GET
public Balance balance() {
Balance balance = new Balance();
return balance;
}
}
此方法总是尝试返回一个对象,如何序列化的对象是取决于所使用的扩展。但是有可能我们需要一个方法,在收到带扩展的 URLS 请求时返回一个
对象时,在其他情况下使用页面呈现:
public class Account extends RestController {
@GET
public void balance() {
Balance balance = new Balance();
if (response.getSerializer() != null) {
response.serialize(balance);
} else {
// This is optional, we need it only if we want to send the object to the page
this.addObject2Request("balance", balance);
response.renderPage("balance");
}
}
}
2.4 显示页面
控制器中的方法执行后总是显示某一个页面,除非该方法返回了对象,或者使用 @DoNotRenderPage 对方法进行了标注。显示页面保存路径是
views.directory/资源名称/方法名称.jsp(.html或.htm)。其中 views.directory 在 serfj.properties 配置文件中定义。举例如
下:
Controller Method View
Account void index() views.directory/account/index
Account void show() views.directory/account/show
Account void newResource() views.directory/account/new
Bank void edit()views.directory/bank/edit
Car void update()views.directory/car/update
Account void create() views.directory/account/create
Account void delete() views.directory/account/delete
Account void myMethod() views.directory/account/myMethod
Account Object myMethod() Returns a serialized object
Account @DoNotRenderPage void myMethod() Returns an HTTP 204 code
这是默认情况下框架显示的网页,但也有显示其他网页的方法。有三种方法来:
renderPage():
显示框架默认页面
renderPage(String pageName):
显示 views.directory/当前资源名称/pageName 页面,pageName可以带扩展名,如果没有扩展名就会搜素带有标准扩展名的页面文件(.jsp\.html\.htm)。
renderPage(String resourceName, String pageName):
显示 views.directory/resourceName/pageName 页面,pageName可以带扩展名,如果没有扩展名就会搜素带有标准扩展名的页面文件(.jsp\.html\.htm)。
3. 序列化
一个REST请求到达时,如果在查询字符串之前的部分包含有扩展名,框架会自动搜索能对请求资源进行够序列化的专用序列化类(读第5节学习如何搜索资源的专用序列化类)。
SerfJ提供XML,JSON和Base64(对应.XML,.JSON和.base64三种扩展名)的序列化,但开发人员可以针对不同的后缀,使用自定义的序列化类。
开发新的序列化类很容易,你只需要实现 net.sf.serfj.serializers.Serializer 接口,不需要更多的解释,可以参考它的Javadoc:
package net.sf.serfj.serializers;
/**
* Interface for Serializers.
*
* @author Eduardo
*/
public interface Serializer {
/**
* 把对象序列化为字符串
*
* @param object
* 需要序列化的对象
* @return 对象序列化字符串.
*/
public String serialize(Object object);
/**
* 从字符串反序列化为对象
*
* @param string
* 对象的序列化字符串
* @return 对象.
*/
public Object deserialize(String string);
/**
* Content type 用于 response 的响应类型.
*/
public String getContentType();
/**
* 返回本序列化类要处理的扩展名
*
* @return 不带点的扩展名
*/
public String getExtension();
}
序列化类的名称必须以扩展名开始(首字母大写),再加上资源名称,必须以Serializer结束(诸如以下格式):
XmlBankSerializer: 把Bank资源序列化为xml的实现.
JsonBookSerializer: 把Book资源序列化为json的实现.
PdfBookSerializer: 把Book资源序列化为pdf的实现.
4. 服务配置
框架遵循惯例优先原则,所以使用它几乎是不必要的配置。当然,它需要进行配置,但只有一点点。但是,如果开发
者想要得到它运行的更好,他们可以设定几个配置属性,避免SerfJ通过猜测找到某些资源。
SerfJ有只有一个配置文件就是classpath中的/config目录内的serfj.properties。为了让它开始工作只需
要配置一个main.package属性。此属性必须指向SerfJ将在哪里寻找控制器和序列化器,但它并不意味着所有控制器和
序列化,必须是在该软件包,框架对控制器和序列化器的寻找方式,将在下一节解释。
4.1 配置属性
main.package:
主要包名(搜索控制器,序列化器的主要依据)。默认值为net.sf.serfj。
views.directory:
搜索网页的目录。默认值为web根目录下的views。
package.style:
框架内寻找资源的方式。
有四种可能的值:FUNCTIONAL/functional, FUNCTIONAL_BY_MODEL/functional_by_model, MODEL/model or OFF。
如果该值是OFF,就相当于没有定义此属性。
alias.controllers.package:
控制器包的别名。
当 package.style 为 FUNCTIONAL, FUNCTIONAL_BY_MODEL 时,追加这个名称到main.package属性值后作为控制器包名。默认值是controllers。
alias.serializers.package:
序列化器包的别名。
当 package.style 为 FUNCTIONAL, FUNCTIONAL_BY_MODEL 时,追加这个名称到main.package属性值后作为序列化器包名。默认值是serializers。
suffix.controllers:
控制器类名的附加后缀。默认值是off,所以不使用后缀。
suffix.serializers:
序列化器类名的附加后缀。默认值是off,所以不使用后缀。
5. 资源搜索风格
有三种SerfJ资源搜索方式,分别是:
Functional.
Functional by model.
By Model.
如果在配置文件中没有定义任何一种方式,则框架顺序使用三种方式搜索资源。如果开发人员想要确定Serfj使用某种搜索方式,就必须定义此属性。
5.1 功能导向风格
如果是寻找一个控制器,则控制器类的限定名应该是:
main.package + "." + alias.controllers.package + "." + capitalized(singularized(resource name)) + suffix.controllers.
如果有以下配置环境:
main.package: net.sf.serfj.tests
alias.controllers.package not defined. The default value is controllers.
suffix.controllers: Ctrl
Resource: banks
框架将寻找一个如下限定名称的控制器:
net.sf.serfj.tests.controllers.BankCtrl
如果搜索的资源是一个序列化器,类的名称会用到前缀。不过这个前缀是不可配置的,固定使用的是扩展名首字母大写作为前缀。例如,有此配置:
main.package: net.sf.serfj.tests
alias.serializers.package 此属性没有定义。 默认值是 serializers。
suffix.serializers: 此属性没有定义。 默认值是 Serializer。
搜索一个account 资源的JSON序列化器类,这个类的全限定名称将是:
net.sf.serfj.tests.serializers.JsonAccountSerializer
5.2 基于模型的功能导向风格
在这一策略中,资源名称单数化后,附加的main.package名称后。如果是搜索一个控制器类,它的全限定类名如下:
main.package + "." + singularized(resource name) + "." + alias.controllers.package + "." + capitalized(singularized(resource name)) + suffix.controllers.
如果有以下配置属性的定义:
main.package: net.sf.serfj.tests
alias.controllers.package: ctrl
suffix.controllers: 没有定义此属性。 默认值是空字符串。
Resource: banks
框架将会搜索有如下限定名称的控制器类:
net.sf.serfj.tests.bank.ctrl.Bank
如果搜索的资源是一个序列化器,类的名称会用到前缀。不过这个前缀是不可配置的,固定使用的是扩展名首字母大写作为前缀。例如,有此配置:
main.package: net.sf.serfj.tests
alias.serializers.package: serial
suffix.serializers: 没有定义此属性。 默认值是 Serializer.
搜索accounts资源的XML序列化类,此类的限定名称如下:
net.sf.serfj.tests.account.serial.XmlAccountSerializer
5.3 模型导向风格
在这一策略中,资源名称单数化后,附加的main.package名称后,但不使用 alias 。如果是搜索一个控制器类,它的全限定类名如下:
main.package + "." + singularized(resource name) + "." + capitalized(singularized(resource name)) + suffix.controllers.
如果有以下配置属性的定义:
main.package: net.sf.serfj.tests
suffix.controllers: 没有定义此属性。 默认值是空字符串。
Resource: banks
框架将会搜索有如下限定名称的控制器类:
net.sf.serfj.tests.bank.Bank
如果搜索资源的序列化器,类的名称会用到前缀。不过这个前缀是不可配置的,固定使用的是扩展名首字母大写作为前缀。例如,有此配置:
main.package: net.sf.serfj.tests
suffix.serializers: 没有定义此属性。 默认值是 Serializer。
搜索accounts资源的CSV序列化类,此类的限定名称如下:
net.sf.serfj.tests.account.CsvAccountSerializer
6. 非web客户端
SerfJ提供了一个接口类 net.sf.serfj.client.Client 用以发起REST请求。它有4个公共方法(GET,POST,PUT和DELETE)用来发起请求。
接口是非常简单的,所以要使用这个类只要看看Javadoc就足够。
=================================================
PS:
1、新开发的对象记得要在 web.xml 文件中配置相应的servlet,类似:
<servlet-mapping>
<servlet-name>RestServlet</servlet-name>
<url-pattern>/phones/*</url-pattern>
</servlet-mapping>
2、资源的名称必须是复数,注意系统是把资源名称却掉s或es作为对象名,真尼玛变态呀。