2011-10-20 47 views
1

---------------- UPDATE ----------------春季控制器获得405错误

我我已经完全重写了我的问题,试图澄清显然写得不好的问题。我还包含了更多的代码,希望有人能够提供一些帮助。我很抱歉前面提供的令人困惑的东西。

基本上我的问题似乎是我并没有完全理解Spring和REST。所以我希望有人可以为我澄清一些事情,也许看看我的代码,并特别告诉我为什么它不起作用。虽然我有一些关于原因的想法,但我不明白为什么它是这样。

我有一个非常基本的Spring应用程序。用户显示一个页面,该页面列出(从数据库)一个表格,该表格由填充了usernams的两列以及是否已启用的布尔值组成。

@RequestMapping(value="/admin/modifyUser", method=RequestMethod.GET) 
public void showModifyUser() {} 

单击已启用列中的链接只需切换其状态。该链接是通过将用户发送到/ admin/access并附加用户名和访问变量来创建的。所以,一个例子是http://localhost:8080/myApp/admin/access?username=test&access=true(或者任何确切的语法)。该代码是:

@RequestMapping(value="/admin/access",method=RequestMethod.GET) 
public String submitModifyAccess(@RequestParam("username")String username, 
           @RequestParam("access")String access) { 
    .... 
    return "redirect:/admin/modifyUser"; 
} 

工作正常。它将更新用户的访问权限,并返回包含表格和用户数据的页面。 (也许不是实现它的最佳方式?)稍后,我想在Dojo网格中填充数据,因此需要将数据放入JSON格式。因此,我在我的关于REST等的Spring书中读到过。所以,从容易开始,我决定让上面的Handler RESTful。所以,我把它改为:

@RequestMapping(value="/admin/access/{username}/{access}",method=RequestMethod.GET) 
public String submitModifyAccess(@PathVariable String username, 
           @PathVariable String access) { 
    .... 
    return "redirect:/admin/modifyUser"; 
} 

我也更新了JSP,使链接到/管理/访问/用户名/访问,因此,例如:http://localhost:8080/myApp/admin/access/test/true。瞧,事情仍然有效。我认为我已经使它成为RESTful。虽然有几件事让我觉得很奇怪。

首先,当点击链接时,它确实正确地更新了状态,但是当返回到/ admin/modifyUser页面(这是它发送给你的页面)时,这两个变量将被追加到URL中。所以不是显示http://localhost:8080/myApp/admin/modifyUser,而是显示http://localhost:8080/myApp/admin/modifyUser?username=test&access=true。很确定这不应该发生。

其次,我意识到submitModifyAccess的RequestMethod应该是POST(或者也许是PUT)。

但正如我所说,它仍然有效,所以我并不担心它太多。

接下来我试图修改其他链接,即用户名链接。当点击该链接时,用户将被带到填有该人数据的表单中。最初只是通过GET请求将用户名附加到URL来显示表单。所以代码是:

@RequestMapping(value="/admin/editUser", method=RequestMethod.GET) 
public void showEditUser(Model model, @RequestParam("username") String username) { 
    NFIUser user = userService.getUser(username); 
    UserDetails userDetails = userDetailsManager.loadUserByUsername(username); 

    .... 
    model.addAttribute("user", user); 
} 

工作正常。所以,我更新了JSP这样的用户名链接调用正确的URL,然后我试图将其更改为到的RESTify这个方法:

@RequestMapping(value="/admin/editUser/{username}", method=RequestMethod.GET) 
public String showEditUser(Model model, @PathVariable String username) { 
    NFIUser user = userService.getUser(username); 
    UserDetails userDetails = userDetailsManager.loadUserByUsername(username); 

    .... 
    model.addAttribute("user", user); 
    return "redirect:/admin/editUser"; 
} 

一旦这样做,我开始看到405错误,我现在意识到我清楚不理解的东西。首先,我相信为了做一个REST PUT或POST,你必须有一个完全相同的URL。那是对的吗?人们认为我应该在这种情况下做什么?

哦,万一有人需要它,形式我是派人到如下(虽然它不是有朝一日能加载时的链接用户点击他们得到405错误):

<div align="center"> 
<b>If you change the Username you MUST change the password as well.</b> 
<s:url value="/admin/editUser" var="edit_url" /> 
<sf:form method="POST" modelAttribute="user" dojoType="dijit.form.Form" action="${edit_url}"> 
<script type="dojo/method" event="onSubmit"> 
    if (!this.validate()) { 
     return false; 
    } 
    return true; 
</script> 
<sf:hidden path="username"/> 
<table> 
<tr> 
<td align="right">Username: </td> 
<td> 
    <sf:input path="newUsername" dojoType="dijit.form.ValidationTextBox" trim="true" required="true" value="${user.username}"/> 
</td> 
</tr> 
<tr> 
<td align="right">Password: </td> 
<td> 
    <sf:input path="password" type="password" dojoType="dijit.form.ValidationTextBox" required="true"/> 
</td> 
</tr> 
<tr> 
<td align="right">Enabled: </td> 
<td> 
    Yes<sf:radiobutton path="enabled" value="true" dojoType="dijit.form.RadioButton"/> 
    No<sf:radiobutton path="enabled" value="false" dojoType="dijit.form.RadioButton"/> 
</td> 
</tr> 
<tr> 
<td align="right">Admin: </td> 
<td> 
    Yes<sf:radiobutton path="isAdmin" value="true" dojoType="dijit.form.RadioButton"/> 
    No<sf:radiobutton path="isAdmin" value="false" dojoType="dijit.form.RadioButton"/> 
</td> 
</tr> 
<tr> 
<td align="right" colspan="2"> 
    <button dojoType="dijit.form.Button" type="submit">Submit</button> 
</td> 
</tr> 
</table> 
</sf:form> 
</div> 

所以希望这会让事情更加清晰。再说一遍,如果你对我做错了什么有所了解,如果你可以解释我明显没有得到的关于REST的信息,或者任何其他可以改进我的代码的评论,请通知我。非常感谢你。

+1

您是否更新了您的(JSP/Velocity模板/通用视图技术)以反映链接的新路径? –

+0

是的,我确保更新所有链接以调用/ admin/editUser/username的新路径。 – cardician

+1

你可以发表你的表格吗? –

回答

1

首先是主要问题,您的405错误。 405表示“方法不支持”,即不支持HTTP方法(GET/PUT/POST /等)。您重定向到/admin/editUser将返回一个302到客户端(浏览器),并带有指示重定向URL的标头。浏览器然后将针对该URL发出GET(即,它将自己重定向)。从您的映射看来,您可以处理的最接近的匹配请求是GET /admin/editUser/{username} - 但您将重定向到GET /admin/editUser。我的猜测是这个问题 - 你的重定向与你声明的任何端点不匹配。

注意我假设您的示例中的/admin/editUser/username网址应该确实为/admin/editUser/{username},因为您需要使用大括号@PathVariable的大括号。

另请注意,从GET重定向有点不常见。你刚刚得到了一些东西,大概是为了回到客户端 - 为什么重定向到其他东西?

关于http://localhost:8080/myApp/admin/access/test/true是否是“RESTful”可能是一个意见,但我的意见是,它不是。对我来说资源是用户的“访问”。要授予访问权限,我可能会执行PUT /users/{username}/access。要拒绝访问,我可能会做一个DELETE /users/{username}/access。注意统一接口:在同一个URL上运行不同的HTTP方法。

如果你在你的网址(如“modifyUser”)动词或在网址中传递数据(例如,在access=truetrue/access/true)你可能不遵守REST原则。

最后,我觉得你可以从一本关于REST的好书中获益,而不是我猜测你正在使用的是在线发现的博客/文章。我发现Rest in Practice是迄今为止最好的。

+0

非常感谢您的回复。当我稍后查看时应该会有所帮助。此外,我的意思是它是{用户名}(它在代码中)。最后,我同意其中一些方法不应该是GETS,我自己也很困惑,但担心改变它们会让事情变得糟糕。我列出的第二种方法通常是按照我一直在写东西的方式进行POST,但是我最初错过了它并使其成为GET。然后,在RESTifying该方法后,我尝试将其更改为PUT,但出现错误。看起来可能是因为我缺少/ admin/editUser句柄 – cardician

+0

另外,你说的对,我可能会从书中受益。感谢您的建议。我一直在使用的这本书实际上是Spring in Action 3,虽然它不错,但我不得不说它留下了大量可能有用的信息。因此,为什么我很清楚地使用REST不当和不恰当。 – cardician

0

modifyUser vs. editUser

你写的链接是: http://localhost:8080/myApp/admin/modifyUser 但你映射到 /admin/editUser/{username}

如果这不是问题,那么尽量让你的问题更清楚一点 - 尤其是什么是当前实现什么目前不工作。

+0

我认为'modifyUser'是用户的表格,'editUser'是编辑用户的表单。 –

+0

这是正确的。如果可以的话,让我试着再澄清一点。 – cardician