删除带有有效负载或表单数据的请求会导致错误请求

DELETE Request with Payload or Form Data causes Bad Request

我正在使用 Java Jersey 2.17 构建一个 RESTful Web 服务。它的客户。我正在使用 ExtJS 5 进行开发。

我的类上了服务

Main.java

 public class Main
 {
    public static final String BASE_URI = "http://localhost:8080/app/rest/";
    public static HttpServer startServer() {
        final ResourceConfig rc = new ResourceConfig();

        rc.packages(true, "app");
        rc.register(ResponseCorsFilter.class);

        return GrizzlyHttpServerFactory.createHttpServer(URI.create(Main.BASE_URI), rc);
    }

    public static void main(final String[] args) throws IOException {
        final HttpServer server = Main.startServer();
        System.out.println(String.format("Jersey app started with WADL available at " + "%sapplication.wadl\nHit enter to stop it...",
                Main.BASE_URI));
        System.in.read();
        server.stop();
    }
}

UserRi.java

@Path("/user")
public class UsersRi {
    @DELETE
    @Path("/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public String deleteUser(@PathParam("id") final String id) {
        final String res = "{\"success\":true,\"msg\":\"User " + id + " successfully deleted.\"}";
        return res;
    }
}

ResponseCorsFilter.java

public class ResponseCorsFilter implements ContainerResponseFilter {

    @Override
    public void filter(final ContainerRequestContext req, final ContainerResponseContext contResp) {

        contResp.getHeaders().add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        contResp.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        contResp.getHeaders().add("Access-Control-Allow-Origin", "*");

        final String reqHead = req.getHeaderString("Access-Control-Request-Headers");
        if ((null != reqHead) && StringUtils.isNotBlank(reqHead)) {
            contResp.getHeaders().add("Access-Control-Request-Headers", reqHead);
        }

    }

}

目前我无法删除内容。在客户端上,我从表单面板获取记录并调用 erase function。 Request/Response 看起来像这样:

General:
    Remote Address:127.0.0.1:8080
    Request URL:http://localhost:8080/app/rest/user/user4
    Request Method:DELETE
    Status Code:400 Bad Request

Response Headers
    Connection:close
    Content-Length:0
    Date:Tue, 14 Apr 2015 19:26:05 GMT

Request Headers
    Accept:*/*
    Accept-Encoding:gzip, deflate, sdch
    Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
    Connection:keep-alive
    Content-Length:14
    Content-Type:application/json
    Host:localhost:8080
    Origin:http://localhost
    Referer:http://localhost/app/
    User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36
    X-Requested-With:XMLHttpRequest

Request Payload
    {"id":"user4"}

在控制台上我看到 XMLHttpRequest cannot load http://localhost:8080/app/rest/user/user4. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. The response had HTTP status code 400.

它也可以通过以下 jQuery.ajax() 调用重现:$.ajax({method:'DELETE',url:"http://localhost:8080/app/rest/user/user4",data:{"id":"user4"}})

正在发送没有表单数据或负载的请求。

是否有另一种方法可以解决此问题而不覆盖 ExtJS 框架中的内容?

问候
索伦

DELETE 以及 GET 请求不应该有负载(实体主体)。我不知道这是否在任何地方指定,但你可以看到讨论 here.

这就是导致 400 Bad Request 的原因(对于 GET 也会发生)。摆脱它,它就会起作用。但无论如何,甚至不需要像你一样发送那个正文,唯一的信息是 id,它已经包含在 URL 路径中。

如果这只是一个示例,并且您需要随请求发送一些其他任意信息,则使用查询参数,例如

 /app/rest/user/user4?some=value1&other=value2&data=value3

 public String deleteUser(@PathParam("id") final String id,
                          @QueryParam("some") String some,
                          @QueryParam("other") String other,
                          @QueryParam("data") String data) {}

有了jQuery,你可以做到

var params = {
    some:"valeu1",
    other:"value2",
    data:"value3"
}

var encoded = $.param(params);
var url = baseUrl + "?" + encoded;
$.ajax({
    url: url,
    ...
})

更新

经过一番调查,这似乎是 Grizzly 的问题。我用 Jetty 测试过,它工作正常。

查看类似问题

  • Here - 最后的评论说它已修复,但我无法提供一个工作示例
  • Here

更新 2

正如下面评论中@alexey所述

You're right, Grizzly by default doesn't allow payload for HTTP methods, for which HTTP spec doesn't explicitly state that. Like HTTP GET, DELETE, HEAD. But you can switch the support on by calling: httpServer.getServerConfiguration().setAllowPayloadForUndefinedHttpMethods(true)‌​; Please not the method should be called before starting HttpServer

所以解决方法是做类似

的事情
public static HttpServer createServer() {
    final ResourceConfig rc = new ResourceConfig();
    rc.packages(true, "app");
    rc.register(ResponseCorsFilter.class);
    
    HttpServer server = GrizzlyHttpServerFactory.createHttpServer(
                               URI.create(BASE_URI), rc, false);
    server.getServerConfiguration().setAllowPayloadForUndefinedHttpMethods(true);
    return server;
}

public static void main(String[] args) throws IOException {
    final HttpServer server = createServer();
    server.start();
    System.out.println(String.format("Jersey app started with WADL available at "
            + "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
    System.in.read();
    server.stop();
}

已测试,按预期工作。

通过在方法级别使用 @Path,您将覆盖 class 级别定义的 @Path

如果您使用的是 Apache Tomcat,请使用他们的 CORS 库:Tomcat CORS filter。这样它更干净并且也涵盖了所有异常。