Spring控制器使用REST获得405错误

时间:2011-10-20 14:42:01

标签: spring rest spring-mvc

---------------- UPDATE ----------------

我完全重写了我的问题,试图澄清显然写得不好的内容。我还包括更多的代码,希望有人可以提供一些帮助。我为之前提供的令人困惑的事情道歉。

基本上我的问题似乎是我不完​​全理解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,使链接转到/ admin / access / username / access,例如: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)。

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

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

@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,然后我尝试通过将其更改为以下方法来恢复此方法:

@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的GET。那是对的吗?在这种情况下,人们认为我应该做些什么?

哦,如果有人想要它,我发送给人的表格如下(虽然它没有被加载,因为当用户点击链接时他们得到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的内容,或者任何其他可以改进我的代码的评论,请务必告诉我。非常感谢你。

2 个答案:

答案 0 :(得分:1)

首先是主要问题,即405错误。 405表示“不支持的方法”,即不支持HTTP方法(GET / PUT / POST /等)。您重定向到/admin/editUser会向客户端(浏览器)返回302,并带有指示重定向网址的标头。然后,浏览器将针对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”)或在您的网址中传递数据(例如trueaccess=true中的/access/true),您可能不会遵守REST原理

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

答案 1 :(得分:0)

modifyUsereditUser

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

如果这不是问题所在,那么请尝试更清楚地说明问题 - 特别是当前的实施方式和当前不起作用的内容。