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

Posted

技术标签:

【中文标题】带有有效负载或表单数据的 DELETE 请求导致错误请求【英文标题】:DELETE Request with Payload or Form Data causes Bad Request 【发布时间】:2015-06-20 14:31:46 【问题描述】:

我正在使用 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。请求/响应如下所示:

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 框架中的内容的情况下解决这个问题?

问候 索伦

【问题讨论】:

您需要将访问控制允许来源设置为*,然后才能工作。 我在 ResponseCorsFilter.java 中设置它还是你的意思是别的?对于没有负载或表单数据的请求,它已经在工作了。 【参考方案1】:

DELETEGET 请求不应包含有效负载(实体主体)。我不知道这是否在任何地方指定,但你可以看到讨论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,
    ...
)

更新

所以经过一番调查,这似乎是灰熊的问题。我用 Jetty 测试过,效果很好。

查看类似问题

Here - 最后一条评论说它已修复,但我无法提供一个可行的示例 Here

更新 2

正如@alexey 在下面的评论中所述

你是对的,Grizzly 默认情况下不允许 HTTP 方法的有效负载,HTTP 规范没有明确说明这一点。像 HTTP GET、DELETE、HEAD。但是您可以通过调用来打开支持:httpServer.getServerConfiguration().setAllowPayloadForUndefinedHttpMethods(true)‌​; 请不要在启动之前调用该方法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();

经过测试,它按预期工作。

【讨论】:

你说得对,Grizzly 默认情况下不允许 HTTP 方法的有效负载,HTTP 规范没有明确说明,例如 HTTP GET、DELETE、HEAD。但是您可以通过调用打开支持: httpServer.getServerConfiguration().setAllowPayloadForUndefinedHttpMethods(true);请注意,该方法应在启动 HttpServer 之前调用。 我提供的例子是 ExtJS 如何向服务器发送 DELETE 请求。感谢您的解决方案,我能够在不调整 ExtJS 框架的情况下运行它。非常感谢:) 对于遇到此 Payara 问题的其他人,请参阅 github.com/payara/Payara/issues/1545#issuecomment-296399223。 DELETE 请求不应该有负载的说法不正确。 http RFC tools.ietf.org/html/rfc7231#section-4.3.5 简单说明: DELETE 请求消息中的有效负载没有定义的语义;在 DELETE 请求上发送有效负载正文可能会导致某些现有实现拒绝该请求。【参考方案2】:

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

如果您使用的是 Apache Tomcat,请使用他们的 CORS 库:Tomcat CORS filter。这种方式更简洁,也涵盖了所有异常。

【讨论】:

以上是关于带有有效负载或表单数据的 DELETE 请求导致错误请求的主要内容,如果未能解决你的问题,请参考以下文章

Angular $resource 服务以表单数据而不是请求有效负载发送数据

PDF 表单提交 - 请求有效负载 PHP 解析器

在 Chrome 开发工具网络选项卡中看到的“请求有效负载”与“表单数据”有啥区别

如何在scrapy中发送带有标头和有效负载的Post请求

在 Lumen API 中接受带有删除请求的表单数据

NodeJS Express - 幕后GET / POST / PATCH / DELETE之间的区别