1

我正在尝试调用我的控制器的删除方法:

春天:

@RestController
@RequestMapping("/api")
@CrossOrigin
public class Controller
{
    @DeleteMapping("thing/item/{name}")
    public void deleteThing(@PathVariable String name)
    {
        System.out.println("CALL"); // Never called!!!
    }
}

角度 7:

deleteTemplate(name: string) {
    const url = `host/api/thing/item/${name}`;
    return this.http.delete(url);
}

我发现了一些关于包括选项的东西:

httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

deleteTemplate(name: string) {
    const url = `${host}/api/thing/item/${name}`; // host is not relevant, same path with GET works.
    return this.http.delete(url, this.httpOptions);
}

但我认为甚至不需要它,因为我发送的所有内容都在链接本身中(没有 json)。

每次它:

WARN 15280 --- [io-8443-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'DELETE' not supported]

如何解决这个问题?

DELETE 发送时带有 405 错误和前面的 OPTIONS(带有 200 代码)。它到达服务器并执行此错误。

编辑

上面是一个经过简化但仍然不起作用的代码示例。我理解你的意见,但主机可以是任何东西,在这里无关紧要。它现在和前面提到的本地主机 - 它在到达端点的意义上起作用。

为了检查我是否错了,我这样做了:

@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;

this.requestMappingHandlerMapping.getHandlerMethods()
                .forEach((e, k) -> System.out.println(e + "   OF   " + k));

启动后,它打印了我期望的所有内容,包括:

{GET /api/thing/item/{name}}   OF   public myPackage.Thing myPackage.Controller.getThing(java.lang.String)
{DELETE /api/thing/item/{name}}   OF   public void myPackage.Controller.deleteThing(java.lang.String)

有任何想法吗?

4

2 回答 2

2

该死的。原来问题出在 CSRF 中,主要是隐藏的 Angular 文档和误导性的 Spring 过滤器日志。

我花了大约 10 小时才让它工作,但只在生产中(Angular 与 Spring 在同一主机/端口上)。我会尝试让它在 dev 中工作,但这基本上需要重写 Angular 提供的绑定到其默认值的整个模块。

现在关于问题:

当我们想要使用 CSRF(我们确实这样做了)时,一切都从 Spring Security 开始。显然CsrfFilter,如果它不能匹配它期望的 CSRF 令牌并回退到/error. 这一切都在没有任何特定日志消息的情况下发生,但很简单Request method 'DELETE' not supported,从我的问题中我们知道它存在。

这不仅适用于DELETE,而且适用于所有操作请求 ( non-GET/HEAD)。

这一切都指向“如何使用 Angular 设置 CSRF?”。好吧,我们开始:

它默认工作。基于 CSRF 的 Cookie-Header 机制。

但是等等,还有更多!

Spring 需要为 Cookie-Header 机制配置 bo。很高兴我们得到:

@Override
protected void configure(HttpSecurity http) throws Exception
{
    http.csrf().csrfTokenRepository(this.csrfRepo());
}

private CookieCsrfTokenRepository csrfRepo()
{
    CookieCsrfTokenRepository repo = new CookieCsrfTokenRepository();
    repo.setCookieHttpOnly(false);
    return repo;
}

它内置在 spring 中,实际上使用与 Angular 相同的 cookie/header 名称。但是怎么了repo.setCookieHttpOnly(false);?嗯...另一个记录不佳的东西。您只需要将其设置为 false,否则 Angular 将无法正常工作。

再说一遍 - 这仅适用于生产版本,因为 Angular 不支持外部链接。更多信息:Angular 6 不会将 X-XSRF-TOKEN 标头添加到 http 请求

所以是的......为了让它在具有 2 个服务器的 dev localhost 中工作,我需要第一个重新创建 https://angular.io/api/common/http/HttpClientXsrfModule机制来拦截非默认请求。

编辑

根据 JB Nizet 的评论,我制作了适用于开发和生产的设置。确实代理很棒。启用 CSRF 和 SSL 后(在我的例子中),您还需要在 Angular CLI 上启用 SSL,否则 Angular 将无法使用启用 SSL 的代理后端的 CSRF,因此无法工作。

"serve": {
    "options": {
            "proxyConfig": "proxy.conf.json",
            "sslKey": "localhost.key",
            "sslCert": "localhost.crt",
            "ssl": true
    }
}

请注意,如果未找到证书和密钥,将自动生成 :) 对于 Spring 后端,您不需要相同的证书。我在那里使用单独的 JKS。

干杯!

在此处输入图像描述

于 2018-12-29T00:44:24.827 回答
0

在路径的开头添加斜杠 /。

@DeleteMapping("/thing/item/{name}")
于 2018-12-27T23:15:38.077 回答