Spring MVC PUT 请求返回 405 Method Not Allowed

Posted

技术标签:

【中文标题】Spring MVC PUT 请求返回 405 Method Not Allowed【英文标题】:Spring MVC PUT Request returns 405 Method Not Allowed 【发布时间】:2014-09-03 09:11:45 【问题描述】:

我正在使用spring mvc设置一个rest api,并且大部分配置都是通过spring boot项目自动设置的。在前端,我使用 angularjs 和他们的 $http 模块向服务器发出 ajax 请求以获取资源。资源 url 在我的控制器类中定义,但只有 GET url 被匹配。我试过 PUT 和 POST 但它们分别返回 405 方法不允许和 403 禁止。

我的控制器是这样的

@Controller
@RequestMapping("/api/users")
public class UserController 

    @Inject
    UserService svc;

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public List<User> home() 
        return svc.findAll();
    

    @RequestMapping(method = RequestMethod.GET, value = "/id")
    @ResponseBody
    public User findById(@PathVariable long id)
        return svc.findById(id);
    

    @RequestMapping(method = RequestMethod.PUT, value="/id")
    @ResponseBody
    public User updateUser(@PathVariable long id, @RequestBody User user)
        Assert.isTrue(user.getId().equals(id), "User Id must match Url Id");
        return svc.updateUser(id, user);
    


对与 url 不匹配的服务器的请求看起来像这样

$http(
            url: BASE_API + 'users/' + user.id,
            method: 'PUT',
            data:user
        )

这会向 localhost:8080/api/users/1 生成一个 PUT 请求,并且服务器会使用 405 Method Not Allowed 响应代码进行响应。

当服务器收到对 localhost:8080/api/users/1 的 HTTP GET 请求时,正确处理相同的请求映射但带有 RequestMethod.GET

任何见解都会有帮助。

PS 如果需要,包含的 Spring Boot 依赖项是

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>

谢谢

【问题讨论】:

【参考方案1】:

您在 Spring MVC 上的代码对我来说看起来不错,所以我怀疑 AngularJS 上的语法是正确的。我对AngularJS不熟悉,但是我找到了一个关于$http函数的在线教程

$http.get(url, config)
$http.post(url, data, config)
$http.put(url, data, config)
$http.delete(url, config)
$http.head(url, config)

从此链接http://tutorials.jenkov.com/angularjs/ajax.html

希望它会有所帮助。

【讨论】:

感谢您的回复,我对角边比弹簧边熟悉得多。 $http 请求有效并产生预期结果(对 localhost/api/users/1 的 HTTP PUT 请求)。我还使用 chrome 扩展 Postman 手动点击了这个端点。我对 spring boots 自动配置不是很熟悉,我认为可能需要进行某种手动配置来启用 PUT 和 POST 请求,但是我真的不知道是不是这种情况。 【参考方案2】:

查看日志(在 DEBUG 中使用安全性),您会发现问题所在。很可能您没有禁用 csrf 保护,并且您的客户端没有发送 csrf 令牌。 (标准 Spring Security,而不是 Spring Boot,尽管如果您使用 Boot security autoconfig,您可以使用外部配置设置关闭 csrf。)

【讨论】:

Spring Boot 的配置设置:security.enable-csrf=false【参考方案3】:

首先,在 Spring 4 中,您可以在休息控制器类上使用 @RestController 注释,这允许您从方法中删除 @RequestBody 注释。让它更干净。

其次,您可能会显示更多您的角度代码吗?我认为您应该考虑使用工厂服务向后端发出请求。比如有一个服务(这个服务已经默认提供了post、get、delete):

app.factory('User', function ($resource) 
    return $resource("/api/users/:id", 
        update: 
            method: "PUT"
        
    );
);

现在在您的 Angular 控制器中创建一个新的用户对象,用于调用此服务方法。

查询全部(GET):User.query();

查询一(GET):User.get(id: userId);

插入(POST):User.$save();

更新(PUT):User.$update();

删除(删除):User.delete(id: userId

我也使用 Spring boot 编写了类似的 Angular 应用程序并且运行良好。

【讨论】:

我使用 jspViewResolver 来定向到特定的 jsp 页面。一旦我将我的更改为@RestController,它就不再起作用了,我是否必须创建另一个使用@Controller 指向特定jsp 页面的类? 你不能同时使用它们。您要么 1) 返回 JSON 并在前端使用它 2) 将数据放入模型映射并返回视图。【参考方案4】:

由于 csrf 保护,我遇到了同样的错误。如果启用 csrf 保护,则需要在请求头中发送 csrf 参数。

您也可以查看 Spring 文档here。

我将此参数添加到我的 jsp 文件中

<input type="hidden" id="csrfToken" value="$_csrf.token"/>
<input type="hidden" id="csrfHeader" value="$_csrf.headerName"/>

并修改了我的 ajax 调用,如下所示。

var token = $('#csrfToken').val();
var header = $('#csrfHeader').val();

$.ajax(
    type : 'POST',
    url : contextPath + "/qd/translate",
    data: JSON.stringify(json),
    dataType : 'json',
    beforeSend: function(xhr) 
        xhr.setRequestHeader("Accept", "application/json");
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.setRequestHeader(header, token);
    ,
    success : function(result) 
        if (result.status === 'ok') 
            $('#translationModal').modal('hide');
            alert('Error when translating: ' + result.resultMessages.succeess);
         else 
            alert('Error when translating: ' + result.resultMessages.error);
        

    ,
    error : function(jqXHR, textStatus, errorThrown) 
        alert(jqXHR.status + " " + jqXHR.responseText);
    
);

【讨论】:

奥曼!你救了我的一天!这真的有效。 +1 表示非常努力。【参考方案5】:

我遇到了同样的问题,并通过使用 @RestController 注释而不是控制器上的“普通”@Controller 注释来解决它。

【讨论】:

这是唯一对我有用的解决方案。有谁知道为什么?我正在使用带有安全启动器的 Spring Boot,并希望将多部分上传到@Controller。将其更改为@RestController 后,它出于某种原因开始工作。我不喜欢不知道为什么......有什么想法吗? 更有趣的是,在控制器方法内部(方法退出时)成功处理请求后抛出异常。 实际上我遇到了同样的问题,当我从 RestController 注释中删除前缀并在它工作的每个方法上添加整个路径时,例如:@Controller(value = "/some) 和 @GetMapping(value = " /path") 更改为 @GetMapping(value = "/some/path"),有什么想法吗?【参考方案6】:

您的问题是 findById 和 updateUser 的端点相同,即 api/users/id 并且由于具有 GET 方法的端点首先出现,因此在您发送 PUT 请求时将首先对其进行评估,因此将在春季被拒绝。

我建议您将更新用户的端点更改为api/users/update/id,如下所示:

@Controller
@RequestMapping("/api/users")
public class UserController 

    @Inject
    UserService svc;

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public List<User> home() 
        return svc.findAll();
    

    @RequestMapping(method = RequestMethod.GET, value = "/id")
    @ResponseBody
    public User findById(@PathVariable long id)
        return svc.findById(id);
    

    @RequestMapping(method = RequestMethod.PUT, value="/update/id")
    @ResponseBody
    public User updateUser(@PathVariable long id, @RequestBody User user)
        Assert.isTrue(user.getId().equals(id), "User Id must match Url Id");
        return svc.updateUser(id, user);
    


【讨论】:

只要方法不同,就可以有相同的uri。

以上是关于Spring MVC PUT 请求返回 405 Method Not Allowed的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC HTTP 状态 405 - 不支持请求方法“POST”

405 - 请求方法“POST”不支持 Spring MVC + Spring Security

在 Spring MVC 中映射 Ajax 请求:不允许出现错误 405 方法

HTTP 状态 405 - 不支持请求方法“PUT”

HTTP 状态 405 - 不支持请求方法“POST”(Spring MVC)

不支持 Spring MVC 请求方法“POST”-> HTTP 405