使用带有请求体的HttpDelete方法

42
我正在尝试使用HttpDelete对象调用Web服务的delete方法。Web服务的代码从消息体中解析JSON。但是,我不明白如何向HttpDelete对象添加消息体。是否有方法可以做到这一点?
对于HttpPut和HttpPost,我调用setEntity方法并传递我的JSON。对于HttpDelete似乎没有这样的方法。
如果无法为HttpDelete对象设置消息体,请链接到使用HttpDelete的超类的资源,以便我可以设置方法(删除)并设置消息体。我知道这不是理想的,但此时我无法更改Web服务。
5个回答

98

您尝试过按以下方式覆盖HttpEntityEnclosingRequestBase吗:

import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import java.net.URI;
import org.apache.http.annotation.NotThreadSafe;

@NotThreadSafe
class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase {
    public static final String METHOD_NAME = "DELETE";
    public String getMethod() { return METHOD_NAME; }

    public HttpDeleteWithBody(final String uri) {
        super();
        setURI(URI.create(uri));
    }
    public HttpDeleteWithBody(final URI uri) {
        super();
        setURI(uri);
    }
    public HttpDeleteWithBody() { super(); }
}

这将创建一个类似于HttpDelete的东西,它有一个setEntity方法。 我认为这个抽象类已经几乎为您完成了所有工作,所以可能只需要这些。

顺便说一句,该代码基于谷歌找到的HttpPost源代码


糟糕!这似乎无法与HttpURLConnection一起使用。我收到了“java.net.ProtocolException:DELETE不支持写入”的错误消息。https://dev59.com/-2kv5IYBdhLWcg3wzj41 - Scott Roberts
我和Scott在一起...我该如何使用HttpURLConnection完成这个任务?我必须重写我的网络代码以使用Apache HTTP吗? - Greg Ennis
使用 HttpURLConnection 无法使用带有请求体的 DELETE 方法。我需要那个功能,最终使用了 HttpClient。 - NikosDim

9

按照Walter Mudnt的建议,您可以使用以下代码。 它能够正常工作,我在测试REST webservice时创建了它。

try {
        HttpEntity entity = new StringEntity(jsonArray.toString());
        HttpClient httpClient = new DefaultHttpClient();
        HttpDeleteWithBody httpDeleteWithBody = new HttpDeleteWithBody("http://10.17.1.72:8080/contacts");
        httpDeleteWithBody.setEntity(entity);

        HttpResponse response = httpClient.execute(httpDeleteWithBody);

    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (ClientProtocolException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

要访问响应,您只需执行以下操作:response.getStatusLine();

4
问题中是否允许在HTTP DELETE请求中使用主体有不同的解释。例如,请参见此处。在HTTP 1.1规范中,没有明确禁止。但在我看来,您不应该在HTTP DELETE中使用主体

尽管如此,我认为您应该使用类似于mysite/myobject/objectIdshop.com/order/1234)的URL,其中objectIdURL的一部分)是附加信息。作为替代方案,您可以使用URL参数mysite/myobject?objectName=table&color=red,以在HTTP DELETE请求中向服务器发送附加信息。以'?'开头的部分是用'&'分隔的urlencoded参数。

如果要发送更复杂的信息,则可以使用DataContractJsonSerializerJavaScriptSerializer将数据转换为JSON,然后将转换后的数据(我称之为myJsonData)作为参数发送:mysite/myobject?objectInfo=myJsonData

如果您需要在HTTP DELETE请求中发送太多附加数据,以至于URL长度成为问题,则最好更改应用程序的设计。

更新:如果您确实想要通过HTTP DELETE发送一些正文,可以按以下方式执行。

// somewhere above add: using System.Net; and using System.IO;

WebClient myWebClient = new WebClient ();

// 1) version: do simple request    
string t= myWebClient.UploadString ("http://www.examples.com/", "DELETE", "bla bla");
// will be send following:
//
// DELETE http://www.examples.com/ HTTP/1.1
// Host: www.examples.com
// Content-Length: 7
// Expect: 100-continue
// Connection: Keep-Alive
//
//bla bla

// 2) version do complex request    
Stream stream = myWebClient.OpenWrite ("http://www.examples.com/", "DELETE");

string postData = "bla bla";
byte[] myDataAsBytes = Encoding.UTF8.GetBytes (postData);
stream.Write (myDataAsBytes, 0, myDataAsBytes.Length);
stream.Close (); // it send the data
// will be send following:
// 
// DELETE http://www.examples.com/ HTTP/1.1
// Host: www.examples.com
// Content-Length: 7
// Expect: 100-continue
// 
// bla bla

// 3) version
// create web request 
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create ("http://www.examples.com/");
webRequest.Method = "DELETE";
webRequest.ServicePoint.Expect100Continue = false;

// post data 
Stream requestStream = webRequest.GetRequestStream ();
StreamWriter requestWriter = new StreamWriter (requestStream);
requestWriter.Write (postData);
requestWriter.Close ();

//wait for server response 
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse ();
// send following:
// DELETE http://www.examples.com/ HTTP/1.1
// Host: www.examples.com
// Content-Length: 7
// Connection: Keep-Alive
// 
// bla bla

完整的代码可能会更加复杂,但这个已经可以工作了。尽管如此,我仍然要说,需要在HTTP DELETE请求的正文中提供数据的Web服务设计不好。

1
提问者非常明确地表示他无法更改Web服务,而且他可能也没有设计它。由于此帖中的每个建议都涉及更改不受控制的代码,因此在当前情况下完全没有帮助。 - Walter Mundt
我可以问一下为什么拥有一个带有删除方法的主体不是一个好主意吗?仅仅因为人们通常不这样做吗?当然可以这样做;那么为什么认为这是一个坏主意呢?(我并不反对。我只是在询问。我是 Web 服务的新手) - Andrew
@Andrew: 大多数HTTP库类及其实现都认为DELETE方法不应该有请求体。除非你有一个非常好的理由去设计一个带有DELETE方法请求体的网络服务实现。在RESTful服务中很少使用非HTTP DELETE方法。在RESTful中,URL应当用来标识资源,因此HTTP DELETE方法可以通过URL获取到需要删除对象的所有信息。虽然使用HTTP GET方法带参数改变资源或者使用HTTP PUT方法删除资源不是被禁止的,但这是一种不好的风格(不良设计)。 - Oleg
@Oleg,你能分享一下关于上述解决方案的库详情吗?maven依赖详情是什么? - Pruthvi Chitrala
@PruthviChitrala:抱歉,但我的代码示例中使用的是C#和.NET,而不是Java。因此,我的代码没有maven依赖项-所有基类都存在于.Net中:WebClientStreamStreamWriterHttpWebRequest来自System.NetSystem.IO(请参见我的代码第一行)。 - Oleg
显示剩余2条评论

3

使用这个:

class MyDelete extends HttpPost{
    public MyDelete(String url){
        super(url);
    }
    @Override
    public String getMethod() {
        return "DELETE";
    }
}

0

在Retrofit中

import okhttp3.Request;

private final class ApiInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request oldRequest = chain.request();
        Request.Builder builder = oldRequest.newBuilder();
        if(condition) {
            return chain.proceed(builder.build().newBuilder().delete(builder.build().body()).build());
        }
        return chain.proceed(builder.build());
    }
}

你必须通过某些方式触发条件,并且可能需要对url/header/body进行一些过滤以去除触发器,

除非删除的url/body/header足够独特,不会与post或get请求冲突。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接