`
ajax
  • 浏览: 251956 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Restlet实战(二十二)仿造PUT和DELETE

    博客分类:
  • REST
阅读更多

Restlet实战(七)-提交和处理Web Form 中提到,表单(form)的动作(action)仅仅支持GET和POST,所以那里我们如果要通过form提交修改的数据,不得不进入HTTP标准方法POST对应的方法执行。这实际上是不得已而为之的做法。当然,DELETE也不支持,也会调用POST方法。那么有没有什么办法可以访问到真正的PUT和DELETE标准方法对应在Restlet中要执行的方法呢?

 

是的,客户端可以通过“用重载的POST(overloaded POST)”来仿造PUT和DELETE的办法来绕过此限制。正如<<Restful Web Service>>书的作者所写:“我建议采用一种被如今许多REST式Web框架所采用的方法,即把真正的HTTP方法放到查询字符串里”。

 

下面就看看在Restlet是如何利用method查询参数在重载的POST(overloaded POST)之上实现PUT和DELETE请求。

 

 Restlet是通过在Application里面调用TunnelService设置方法参数,代码比较简单,如下:

 

public class UserApplication extends Application{
	
	public UserApplication(){
		getTunnelService().setMethodParameter("_method");
	}
	
	@Override
	public synchronized Restlet createRoot() {
		Router router = new Router(getContext());

		Route route = router.attach("/{userId}", UserResource.class);
		
		route.extractQuery("query", "test", true);
		
		router.attach("/{userId}/orders", UserOrdersResource.class);
		return router;
	}
}

 

基于上面的代码,假设要访问的URI是/users/1,HTTP标准方法是PUT,实际上,就是修改了用户的资料,然后请求到服务器,要求保存修改后的用户数据。表示(representation)里的form代码片断如下:

 

<form name="form1" action="<%=request.getContextPath()%>/resources/users/1?_method=put" method="POST">
	Name:<input type="text" name="name"/><br>
	Address:<input type="text" name="address"/><br>
	<input type="button" value="Submit" onclick="doSubmit()"/>
</form>

 

这里,如果提交form,那么UserResource里的PUT方法将被执行。过程很简单,不再多做解释。

 

不过有两个问题需要注意,也许你看到form里的action是****?_method=put,那么你也许会想,改成_method=hello可以吗,答案是不行的,如果你这么做了,系统会有异常:

 

java.lang.NoSuchMethodException: com.infosys.restlet.resource.UserResource.allowHello()

 

那我们能不能象allowPut定义allowHello呢,显而易见是不行的,这又说到REST上去了,HTTP标准方法常用的也就4个,所以,即使你自己定义allowHello,也不行。

 

另外一个问题,方法参数设置在application,那么如果有非常多的application,那岂不是需要设置很多次?大量的重复工作。

 

Restlet已经考虑到这点,因为在TunnelService的构造函数里面已经有指定methodParameter=method,这说明,如果不在application里设置,默认应该是method,也就是说,在form的action里,我可以象这样设置:method=put。

 

但是在之前的系列文章中,我有时候给出的配置如下(即不通过Application):

 

<bean id="defaultRouter" class="org.restlet.ext.spring.SpringRouter">
	<property name="attachments">
		<map>
			<entry key="/customers">
				<bean class="org.restlet.ext.spring.SpringFinder">
					<lookup-method name="createResource" bean="customersResource" />
				</bean>
			</entry>
			<entry key="/customers/{customerId}">
				<bean class="org.restlet.ext.spring.SpringFinder">
					<lookup-method name="createResource" bean="customerResource" />
				</bean>
			</entry>
			<entry key="/users">
				<bean class="com.infosys.restlet.application.UserApplication"/>
			</entry>
		</map>
	</property>
</bean>

 

上述/users是通过配置Application,而/customers和/customers/{customerId}是直接配置Resource,当然了,如果是直接配置Resource,Restlet会自己实例化一个默认的Application。但是根据我的测试,问题恰恰出在这里,如果是我们自己配置的Application,如UserApplication,则上述仿造PUT和DELETE过程没有任何问题,但是如果是直接配置了Resource,则即使在action指定可method=put,始终会调用POST对应的方法,而不会执行仿造的PUT和DELETE方法。  

 

分享到:
评论
1 楼 teamlet 2010-02-10  
restlet中的 Application 提供了一些服务,比如connectorService、decoderService、metadataService、tunnelService等,同时还对并发做了处理。

在ServerServlet的init()方法中会初始化component和application。如果没有在web.xml或restlet.xml中配置org.restlet.application的值,application就不能创建,值为null。

你说的不能仿造的原因可能就是application没有导致的。

建议在restlet的SpringComponent下设置defaultTarget为application,而不是router。通过继承restlet的Application覆写createRoot()方法来创建router.

相关推荐

Global site tag (gtag.js) - Google Analytics