在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方法。