欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java开发笔记(一百一十一)POST方式的HTTP调用

程序员文章站 2023-01-01 10:27:33
前面介绍了GET方式的HTTP调用,该方式主要用于向服务器索取数据,不管是字符串形式的应答报文,还是二进制形式的网络文件,都属于服务器提供的信息。当然调用方也可以向服务地址传送请求参数,除了通过连接对象设置的HTTP参数,还能给url地址添加形如“?参数A名称=A参数值&参数B名称=B参数值”这样的 ......

前面介绍了get方式的http调用,该方式主要用于向服务器索取数据,不管是字符串形式的应答报文,还是二进制形式的网络文件,都属于服务器提供的信息。当然调用方也可以向服务地址传送请求参数,除了通过连接对象设置的http参数,还能给url地址添加形如“?参数a名称=a参数值&参数b名称=b参数值”这样的业务参数,服务地址根据url后面的业务参数,再返回符合条件的应答数据。倘若服务器不仅仅作为信息提供方,还想成为信息接收方,例如保存调用方提交的表单数据,或者保存调用方待上传的文件,那便要求调用方的程序能够传送复杂的数据信息。通过get方式固然也能在url后方填写简单的请求参数,但是这并非信息传送的可靠手段,原因有三:
1、往url末尾添加的请求参数,全为明文传输,不利于数据的保密措施;
2、url格式的请求串只支持键值对形式的参数,难以表达复杂的结构化数据,譬如数组形式的参数;
3、url本身是个字符串,query部分的请求参数也只能是字符串,这叫二进制形式的文件上传如何是好?
鉴于种种不可避免的困难,get方式实在不适合向服务器提交数据,必须采用post方式提交数据才行。post方式同样需要服务器给个调用地址,但该方式的业务参数没放到url末尾,而是放在了请求报文当中。所谓的请求报文与应答报文相对应,应答报文要从连接对象的输入流中获取,而请求报文要写入连接对象的输出流。编码实现post请求的时候,除了调用setrequestmethod要将请求方式设置为post,还需留意连接对象的下列几种方法:
setrequestproperty:设置请求属性。该方法可设置特定名称的属性值。
setdooutput:准备让连接执行输出操作。默认为false(get方式),post方式需要设置为true。
setdoinput:准备让连接执行输入操作。默认为true,通常无需特意调用该方法。
getoutputstream:从连接对象中获取输出流,后续会把请求报文写入输出流。
getheaderfield:获取应答报文头部指定名称的字段值。该方法可得到特定名称的参数值,例如getheaderfield("content-length")返回的是应答报文的长度,getheaderfield("content-type")返回的是应答报文的内容类型,conn.getheaderfield("content-encoding")返回的是应答报文的压缩方式。
上述几种方法中尤为值得注意的是setrequestproperty,依据不同的请求属性名称,该方法将会设置各式各样的属性值,以此提醒服务器做好相应的准备工作。其中常见的属性名称及其属性值罗列如下:
content-type:请求报文的内容类型。如果请求报文采取形如“参数a名称=a参数值&参数b名称=b参数值”的url参数格式,则内容类型应设置为“application/x-www-form-urlencoded”;如果请求报文是json格式,则内容类型应设置为“application/json”;如果请求报文是xml格式,则内容类型应设置为“application/xml”;如果请求报文是分段传输的文件数据,则内容类型应设置为“multipart/form-data;boundary=***”。
connection:指定连接的保持方式。如果是文件上传,则必须设置为“keep-alive”,表示建议服务器保留连接,以便能够持续发送文件的分段数据。
user-agent:指定调用方的浏览器类型。
accept:指定可接受的应答报文类型。如果不设置则默认“*/*”,表示允许返回任何类型的应答报文;如果设置为“image/png”,则表示只接受返回png图片。
accept-language:指定可接受的应答报文语言。通常无需设置,如果只接受中文则可设置为“zh-cn”。
accept-encoding:指定可接受的应答报文压缩方式。如果不设置则默认identity,表示不允许应答报文使用压缩;如果设置为gzip,则表示允许应答报文采用gzip压缩,此时服务器可能返回gzip压缩的应答数据,也可能返回未压缩的应答数据。
接下来举个请求报文是json串的http接口例子,采用post方式的调用方法代码如下所示:

	// 对指定url发起post调用
	private static void testcallpost(string callurl, string body) {
		try {
			url url = new url(callurl); // 根据网址字符串构建url对象
			// 打开url对象的网络连接,并返回httpurlconnection连接对象
			httpurlconnection conn = (httpurlconnection) url.openconnection();
			conn.setrequestmethod("post"); // 设置请求方式为post调用
			conn.setrequestproperty("content-type", "application/json"); // 请求报文为json格式
			conn.setdooutput(true); // 准备让连接执行输出操作。默认为false,post方式需要设置为true
			conn.connect(); // 开始连接
			outputstream os = conn.getoutputstream(); // 从连接对象中获取输出流
			os.write(body.getbytes()); // 往输出流写入请求报文
			// 打印http调用的应答内容长度、内容类型、压缩方式
			system.out.println( string.format("应答内容长度=%s, 内容类型=%s, 压缩方式=%s", 
					conn.getheaderfield("content-length"), conn.getheaderfield("content-type"),
					conn.getheaderfield("content-encoding")) );
			// 对输入流中的数据进行解压和字符编码,得到原始的应答字符串
			string content = streamutil.getunzipstring(conn);
			// 打印http调用的应答状态码和应答报文
			system.out.println( string.format("应答状态码=%d, 应答报文=%s", 
					conn.getresponsecode(), content) );
			conn.disconnect(); // 断开连接
		} catch (exception e) {
			e.printstacktrace();
		}
	}

 

然后由外部在调用testcallpost时输入服务地址和请求报文,具体代码示例如下:

		testcallpost("http://localhost:8080/netserver/checkupdate", "{\"package_list\":[{\"package_name\":\"com.qiyi.video\"}]}");

 

运行上述的post代码,从以下的接口日志可知post方式正确发送了请求报文,且正常收到了应答报文。

请求报文={"package_list":[{"package_name":"com.qiyi.video"}]}
应答内容长度=152, 内容类型=text/plain;charset=utf-8, 压缩方式=null
应答状态码=200, 应答报文={"package_list":[{"package_name":"com.qiyi.video","download_url":"https://3g.lenovomm.com/w3g/yydownload/com.qiyi.video/60020","new_version":"10.2.0"}]}

通过http接口上传文件也要采用post方式,只是文件上传还需遵守一定的数据规则,除了内容类型设置为“multipart/form-data;boundary=***”(***处要替换成边界字符串),请求报文也得依顺序填入报文头、报文体和报文尾,详细的上传过程代码如下所示:

	// 把本地文件上传给指定url
	private static void testupload(string filepath, string uploadurl) {
		// 从本地文件路径获取文件名
		string filename = filepath.substring(filepath.lastindexof("/"));
		string end = "\r\n"; // 结束字符串
		string hyphens = "--"; // 连接字符串
		string boundary = "wum4580jbtwfjhnp7zi1djfeo3wnnm"; // 边界字符串
		try (fileinputstream fis = new fileinputstream(filepath)) {
			url url = new url(uploadurl); // 根据网址字符串构建url对象
			// 打开url对象的网络连接,并返回httpurlconnection连接对象
			httpurlconnection conn = (httpurlconnection) url.openconnection();
			conn.setdooutput(true); // 准备让连接执行输出操作。默认为false,post方式都要设置为true
			conn.setrequestmethod("post"); // 设置请求方式为post调用
			// 连接过程要保持活跃
			conn.setrequestproperty("connection", "keep-alive");
			// 请求报文要求分段传输,并且各段之间以边界字符串隔开
			conn.setrequestproperty("content-type", "multipart/form-data;boundary=" + boundary);
			// 根据连接对象的输出流构建数据输出流
			dataoutputstream ds = new dataoutputstream(conn.getoutputstream());
			// 以下写入请求报文的头部
			ds.writebytes(hyphens + boundary + end);
			ds.writebytes("content-disposition: form-data; "
					+ "name=\"file\";filename=\"" + filename + "\"" + end);
			ds.writebytes(end);
			// 以下写入请求报文的主体
			byte[] buffer = new byte[1024];
			int length;
			// 先将文件数据写入到缓冲区,再将缓冲数据写入输出流
			while ((length = fis.read(buffer)) != -1) {
				ds.write(buffer, 0, length);
			}
			ds.writebytes(end);
			// 以下写入请求报文的尾部
			ds.writebytes(hyphens + boundary + hyphens + end);
			ds.close(); // 关闭数据输出流
			// 对输入流中的数据进行解压和字符编码,得到原始的应答字符串
			string content = streamutil.getunzipstring(conn);
			// 打印http上传的应答状态码和应答报文
			system.out.println( string.format("应答状态码=%d, 应答报文=%s", 
					conn.getresponsecode(), content) );
			conn.disconnect(); // 断开连接
		} catch (exception e) {
			e.printstacktrace();
		}
	}

 

然后由外部在调用testupload方法时输入上传地址和待上传的文件路径,具体代码示例如下:

		testupload("e:/bliss.jpg", "http://localhost/netserver/uploadservlet");

 

运行上述的上传代码,从以下的上传日志可知文件已经成功上传至服务器。

应答状态码=200, 应答报文=文件上传成功,文件大小为1912k

  

更多java技术文章参见《java开发笔记(序)章节目录