深入理解HTTP请求:POST、PUT、DELETE及各类Web服务请求
1. HTTP POST请求
在进行HTTP POST请求时,不能像GET请求那样使用URL类的
getText()
快捷方式。必须获取
URLConnection
对象,将请求方法设置为POST(GET是默认方法)。对于GET请求,查询字符串会附加在URL对象的末尾;而POST请求的查询字符串则嵌入在请求体中。要实现这一点,需要完成以下三个步骤:
1. 将
URLConnection
的
doOutput
值设置为
true
。
2. 获取
outputStream
。
3. 在调用
connect()
之前,将查询字符串写入
outputStream
。
以下是一个简单的HTTP POST请求示例:
def url = new URL("http://search.yahoo.com/search")
def connection = url.openConnection()
//switch the method to POST (GET is the default)
connection.setRequestMethod("POST")
//write the data
def queryString = "n=20&vf=pdf&p=groovy+grails"
connection.doOutput = true
Writer writer = new OutputStreamWriter(connection.outputStream)
writer.write(queryString)
writer.flush()
writer.close()
connection.connect()
//print the results
println connection.content.text
从列表构建查询字符串
从列表构建查询字符串的关键是确保值进行URL编码,然后用
&
将元素连接起来。示例代码如下:
def queryString = []
queryString << "n=" + URLEncoder.encode("20")
queryString << "vf=" + URLEncoder.encode("pdf")
queryString << "p=" + URLEncoder.encode("groovy grails")
def url = new URL("http://search.yahoo.com/search")
def connection = url.openConnection()
connection.setRequestMethod("POST")
connection.doOutput = true
Writer writer = new OutputStreamWriter(connection.outputStream)
writer.write(queryString.join("&"))
writer.flush()
writer.close()
connection.connect()
def results = conn.content.text
创建便利的Post类
将复杂的连接逻辑封装到
Post
类中,结合之前创建的
QueryString
类,可以提供更好的开发体验。示例代码如下:
class Post{
String url
QueryString queryString = new QueryString()
URLConnection connection
String text
String getText(){
def thisUrl = new URL(url)
connection = thisUrl.openConnection()
connection.setRequestMethod("POST")
connection.doOutput = true
Writer writer = new OutputStreamWriter(connection.outputStream)
writer.write(queryString.toString())
writer.flush()
writer.close()
connection.connect()
return connection.content.text
}
String toString(){
return "POST:\n" +
url + "\n" +
queryString.toString()
}
}
def post = new Post(url:"http://search.yahoo.com/search")
post.queryString.add("n", 20)
post.queryString.add("vf", "pdf")
post.queryString.add("p", "groovy grails")
println post
println post.text
模拟HTML表单进行测试
有了
Post
类后,可以使用代码轻松模拟HTML表单提交。以下是一个示例:
<form method="post" action="http://localhost:8888/jaw/controller">
<input type="hidden" name="action" value="saveCar" />
Make:
<input type="text" name="make" value="" /></td>
Model: <input type="text" name="model" value="" /></td>
Year:
<input type="text" name="modelYear" value="" /></td>
<input type="submit" name="save" value="Save" />
</form>
对应的模拟代码如下:
def post = new Post(url:"http://localhost:8888/jaw/controller")
post.queryString.add("action", "saveCar")
post.queryString.add("make", "Toyota")
post.queryString.add("model", "Prius")
post.queryString.add("modelYear", 2012)
println post.text
RESTful POST请求使用XML
在RESTful Web服务中,HTTP动词具有深刻的语义含义。通常,RESTful服务期望POST请求的主体是XML,而不是之前演示的查询字符串。要传递XML,需要对代码进行两个小的更改:
1. 将
Content-Type
从
application/x-www-form-urlencoded
(POST的默认值)更改为
application/xml
(具体的
Content-Type
取决于所调用的RESTful服务)。
2. 不对数据进行URL编码,XML有效负载应按其原始格式传输。
示例代码如下:
def xml = """<car>
<make>Toyota</make>
<model-year>2012</model-year>
<model>Prius</model>
</car>"""
def url = new URL("http://localhost:8888/jaw/car")
def connection = url.openConnection()
//set the metadata
connection.setRequestMethod("POST")
connection.setRequestProperty("Content-Type","application/xml")
//write the data
connection.doOutput = true
Writer writer = new OutputStreamWriter(connection.outputStream)
writer.write(xml)
writer.flush()
writer.close()
connection.connect()
def results = connection.content.text
2. HTTP PUT请求
执行HTTP PUT请求在语法上与POST请求相同,唯一的区别是将请求方法设置为
PUT
。PUT在语义上相当于数据库的UPDATE操作。示例代码如下:
def xml = """<car id="142">
<make>Toyota</make>
<model-year>2012</model-year>
<model>Prius, Luxury Edition</model>
</car>"""
def url = new URL("http://localhost:8888/jaw/car/142")
def connection = url.openConnection()
connection.setRequestMethod("PUT")
connection.setRequestProperty("Content-Type","application/xml")
connection.doOutput = true
Writer writer = new OutputStreamWriter(connection.outputStream)
writer.write(xml)
writer.flush()
writer.close()
connection.connect()
def result = connection.content.text
创建便利的Put类
Put
类与
Post
类几乎相同,但有三个区别:
1.
queryString
字段替换为普通的
String body
字段。
2. 暴露
contentType
字段,以便根据需要进行更改。
3. 请求方法设置为
PUT
。
示例代码如下:
class Put{
String url
String body
String contentType = "application/xml"
URLConnection connection
String text
String getText(){
def thisUrl = new URL(url)
connection = thisUrl.openConnection()
connection.setRequestMethod("PUT")
connection.setRequestProperty("Content-Type", contentType)
connection.doOutput = true
Writer writer = new OutputStreamWriter(connection.outputStream)
writer.write(body)
writer.flush()
writer.close()
connection.connect()
return connection.content.text
}
String toString(){
return "PUT:\n" +
contentType + "\n" +
url + "\n" +
body
}
}
def xml = """<car id="142">
<make>Toyota</make>
<model-year>2012</model-year>
<model>Prius, Luxury Edition</model>
</car>"""
def put = new Put(url:"http://localhost:8888/jaw/car/142")
put.body = xml
println put
def result = put.text
3. HTTP DELETE请求
执行HTTP DELETE请求在语法上与GET请求相同,唯一的区别是将请求方法设置为
DELETE
。与POST和PUT请求不同,GET和DELETE请求(以及HEAD、OPTION等其他HTTP动词)只包含URL。示例代码如下:
def url = new URL("http://localhost:8888/jaw/car/142")
def connection = url.openConnection()
connection.setRequestMethod("DELETE")
connection.connect()
def result = connection.content.text
创建便利的Delete类
Delete
类与
Get
类几乎相同,唯一的区别是请求方法设置为
DELETE
。示例代码如下:
class Delete{
String url
QueryString queryString = new QueryString()
URLConnection connection
String text
String getText(){
def thisUrl = new URL(this.toString())
connection = thisUrl.openConnection()
connection.setRequestMethod("DELETE")
if(connection.responseCode == 200){
return connection.content.text
}
else{
return "Something bad happened\n" +
"URL: " + this.toString() + "\n" +
connection.responseCode + ": " +
connection.responseMessage
}
}
String toString(){
return "DELETE:\n" +
url + "?" + queryString.toString()
}
}
def delete = new Delete(url:"http://localhost:8888/jaw/car/142")
println delete
def results = delete.text
4. RESTful请求
可以使用RESTful请求获取地址的经纬度信息。示例代码如下:
def partialRestRequest = "http://geocoder.us/service/rest/geocode?address="
def address = "1600 Pennsylvania Ave, Washington DC"
def restUrl = new URL(partialRestRequest + URLEncoder.encode(address))
def restResponse = restUrl.text
解析RESTful响应
使用
XmlSlurper
可以避免处理命名空间,提取相关字段。示例代码如下:
//Response:
<rdf:RDF
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<geo:Point rdf:nodeID="aid76408515">
<dc:description>
1600 Pennsylvania Ave NW, Washington DC 20502
</dc:description>
<geo:long>-77.037684</geo:long>
<geo:lat>38.898748</geo:lat>
</geo:Point>
</rdf:RDF>
def restResponse = restUrl.text
def RDF = new XmlSlurper().parseText(restResponse)
println RDF.Point.description
println RDF.Point.long
println RDF.Point.lat
5. 地理编码服务案例研究:Geocoder.us
许多主流地图网站都提供地理编码API,而Geocoder.us是一个独立的地理编码网站,在多个示例中发挥了重要作用。它允许以多种不同的Web服务方言进行相同的基本查询,并且对于非商业用途是免费的,基于美国人口普查局的免费数据。目前,Geocoder.us不支持RSS和Atom格式,但未来可能会添加这些格式。
以下是不同请求方式的对比表格:
| 请求方式 | 用途 | 示例代码 |
| ---- | ---- | ---- |
| POST | 向服务器提交数据,如表单数据 | 上述POST请求代码 |
| PUT | 更新服务器上的资源 | 上述PUT请求代码 |
| DELETE | 删除服务器上的资源 | 上述DELETE请求代码 |
| RESTful | 获取资源信息,如地理编码 | 上述RESTful请求代码 |
下面是一个简单的mermaid流程图,展示HTTP请求的基本流程:
graph TD;
A[创建URL对象] --> B[打开连接];
B --> C{设置请求方法};
C -- POST --> D[设置doOutput为true并写入数据];
C -- PUT --> D;
C -- DELETE --> E[连接服务器];
C -- GET --> E;
D --> E;
E --> F[获取响应内容];
深入理解HTTP请求:POST、PUT、DELETE及各类Web服务请求
6. CSV请求
可以通过发送CSV请求获取地址的经纬度信息。以下是具体的操作步骤:
1. 构建请求URL,将地址进行URL编码后附加到请求URL后面。
2. 打开URL连接并获取响应内容。
示例代码如下:
def partialCsvRequest = "http://geocoder.us/service/csv/geocode?address="
def address = "1600 Pennsylvania Ave, Washington DC"
def csvUrl = new URL(partialCsvRequest + URLEncoder.encode(address))
def csvResponse = csvUrl.text
解析CSV响应
解析CSV响应可以使用
split(",")
方法将响应字符串按逗号分割成数组,然后提取所需的字段。示例代码如下:
//Response:
38.898748,-77.037684,1600 Pennsylvania Ave NW,Washington,DC,20502
def csvResponse = csvUrl.text
def tokens = csvResponse.split(",")
println "Latitude: [${tokens[0]}]"
println "Longitude: [${tokens[1]}]"
println "Address: [${tokens[2]}]"
println "City: [${tokens[3]}]"
println "State: [${tokens[4]}]"
println "Zip: [${tokens[5]}]"
7. SOAP请求
发送SOAP请求获取地址的经纬度信息,需要以下操作步骤:
1. 构建SOAP请求消息,包含请求的地址信息。
2. 打开URL连接,设置请求方法为POST,设置
Content-Type
为
application/xml
。
3. 将SOAP请求消息写入输出流,连接服务器并获取响应内容。
示例代码如下:
def address = "1600 Pennsylvania Av, Washington, DC"
def soapRequest = """<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns:tns="http://rpc.geocoder.us/Geo/Coder/US/">
<SOAP-ENV:Body>
<tns:geocode
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<location xsi:type="xsd:string">${address}</location>
</tns:geocode>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>"""
def soapUrl = new URL("http://geocoder.us/service/soap")
def connection = soapUrl.openConnection()
connection.setRequestMethod("POST")
connection.setRequestProperty("Content-Type","application/xml")
connection.doOutput = true
Writer writer = new OutputStreamWriter(connection.outputStream)
writer.write(soapRequest)
writer.flush()
writer.close()
connection.connect()
def soapResponse = connection.content.text
解析SOAP响应
使用
XmlSlurper
解析SOAP响应,提取经纬度信息。由于Geocoder.us的SOAP接口存在一些不稳定因素,如命名空间和元素名可能会变化,需要使用更灵活的方式来处理。示例代码如下:
//Response:
<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<namesp9:geocodeResponse
xmlns:namesp9="http://rpc.geocoder.us/Geo/Coder/US/">
<geo:s-gensym111 xsi:type="SOAP-ENC:Array"
xmlns:geo="http://rpc.geocoder.us/Geo/Coder/US/"
SOAP-ENC:arrayType="geo:GeocoderAddressResult[1]">
<item xsi:type="geo:GeocoderAddressResult">
<number xsi:type="xsd:int">1600</number>
<lat xsi:type="xsd:float">38.898748</lat>
<street xsi:type="xsd:string">Pennsylvania</street>
<state xsi:type="xsd:string">DC</state>
<city xsi:type="xsd:string">Washington</city>
<zip xsi:type="xsd:int">20502</zip>
<suffix xsi:type="xsd:string">NW</suffix>
<long xsi:type="xsd:float">-77.037684</long>
<type xsi:type="xsd:string">Ave</type>
<prefix xsi:type="xsd:string" />
</item>
</geo:s-gensym111>
</namesp9:geocodeResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
def soapResponse = connection.content.text
def Envelope = new XmlSlurper().parseText(soapResponse)
println Envelope.Body.geocodeResponse.'s-gensym111'.item.long
println Envelope.Body.geocodeResponse.'s-gensym111'.item.lat
//since the array's name ('s-gensym111') changes with each request
// we can deal with it generically as such:
def itor = Envelope.Body.geocodeResponse.breadthFirst()
while(itor.hasNext()){
def fragment = itor.next()
if(fragment.name() == "item"){
println fragment.lat
println fragment.long
}
}
8. XML-RPC请求
发送XML-RPC请求获取地址的经纬度信息,具体操作步骤如下:
1. 构建XML-RPC请求消息,包含请求的方法名和参数。
2. 打开URL连接,设置请求方法为POST,设置
Content-Type
为
application/xml
。
3. 将XML-RPC请求消息写入输出流,连接服务器并获取响应内容。
示例代码如下:
def address = "1600 Pennsylvania Av, Washington, DC"
def xmlrpcRequest = """<methodCall>
<methodName>geocode</methodName>
<params>
<param>
<value><string>${address}</string></value>
</param>
</params>
</methodCall>"""
def xmlrpcUrl = new URL("http://geocoder.us/service/xmlrpc")
def connection = xmlrpcUrl.openConnection()
connection.setRequestMethod("POST")
connection.setRequestProperty("Content-Type","application/xml")
connection.doOutput = true
Writer writer = new OutputStreamWriter(connection.outputStream)
writer.write(xmlrpcRequest)
writer.flush()
writer.close()
connection.connect()
def xmlrpcResponse = connection.content.text
解析XML-RPC响应
使用
XmlSlurper
解析XML-RPC响应,提取经纬度信息。示例代码如下:
//Response:
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse><params><param><value><array><data><value><struct>
<member><name>number</name><value><int>1600</int></value></member>
<member><name>lat</name><value><double>38.898748</double></value></member>
<member><name>street</name><value><string>Pennsylvania</string></value></member>
<member><name>state</name><value><string>DC</string></value></member>
<member><name>city</name><value><string>Washington</string></value></member>
<member><name>zip</name><value><int>20502</int></value></member>
<member><name>suffix</name><value><string>NW</string></value></member>
<member><name>long</name><value><double>-77.037684</double></value></member>
<member><name>type</name><value><string>Ave</string></value></member>
<member><name>prefix</name><value><string/></value></member>
</struct></value></data></array></value></param></params></methodResponse>
def xmlrpcResponse = connection.content.text
def methodResponse = new XmlSlurper().parseText(xmlrpcResponse)
methodResponse.params.param.value.array.data.value.struct.member.each{member ->
if(member.name == "lat" || member.name == "long"){
println "${member.name}: ${member.value.double}"
}
}
以下是不同请求类型在数据格式和处理方式上的对比表格:
| 请求类型 | 数据格式 | 数据处理方式 |
| ---- | ---- | ---- |
| CSV请求 | 逗号分隔值 | 使用
split(",")
方法解析 |
| SOAP请求 | XML | 使用
XmlSlurper
解析,需处理不稳定的命名空间和元素名 |
| XML-RPC请求 | XML | 使用
XmlSlurper
解析 |
下面是一个mermaid流程图,展示不同类型请求的处理流程:
graph TD;
A[选择请求类型] --> B{CSV请求};
B -- 是 --> C[构建CSV请求URL并获取响应];
B -- 否 --> D{SOAP请求};
D -- 是 --> E[构建SOAP请求消息并发送];
D -- 否 --> F{XML-RPC请求};
F -- 是 --> G[构建XML-RPC请求消息并发送];
F -- 否 --> H[其他请求类型];
C --> I[解析CSV响应];
E --> J[解析SOAP响应];
G --> K[解析XML-RPC响应];
综上所述,通过不同的HTTP请求方式(POST、PUT、DELETE、RESTful、CSV、SOAP、XML - RPC)可以实现向服务器提交数据、更新资源、删除资源以及获取各种格式的资源信息等功能。在实际应用中,需要根据具体的需求选择合适的请求方式,并正确处理响应内容。
2208



被折叠的 条评论
为什么被折叠?



