事情的起因在于请求透传这么一个任务,我的认知中,透传需要三步,第一步知道对方的请求方式和参数传递方式,第二步是我应该怎样处理传过来的参数,第三部是透传目的接口的请求方式和参数请求方式。
首先是对于Content-Type的常见格式的认知
常见的媒体格式类型如下:
- text/html : HTML格式
- text/plain :纯文本格式
- text/xml : XML格式
- image/gif :gif图片格式
- image/jpeg :jpg图片格式
- image/png:png图片格式
以application开头的媒体格式类型:
- application/xhtml+xml :XHTML格式
- application/xml : XML数据格式
- application/atom+xml :Atom XML聚合格式
- application/json : JSON数据格式
- application/pdf :pdf格式
- application/msword : Word文档格式
- application/octet-stream : 二进制流数据(如常见的文件下载)
- application/x-www-form-urlencoded : 中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
另外一种常见的媒体格式是上传文件之时使用的:
- multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
那么回过头来说说请求发起方,当前场景下是curl发起的请求,而我对于curl的参数不是很熟悉,所以整理一下以便参考
排序参数描述用法
-
A
-A/–user-agent 指定客户端的用户代理,默认是 curl/versioncurl -A ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/76.0.3809.100 Safari/537.36’ 192.168.41.203 -v
curl -A ‘’ 192.168.41.203
-
B
-b/–cookie <name=data>向服务器发送 cookie(H)curl -b ‘foo1=bar;foo2=bar2’ 192.168.41.203
curl -b cookies.txt 192.168.41.203
-
C
-c/–cookie-jar 将服务器设置的 cookie 写入一个文件curl -c cookie.txt 192.168.41.203
-C/–continue-at 断点续传。是一个数值,表示从文件头开始计算的要跳过的字节数。如果被设定为 -,那么将自行检测待传输文件的断点位置curl -C - -O ‘http://10.2.19.250/400k.html’ --limit-rate 10k
-
D
-d/–data 以 POST 方式传送数据,请求头会自动加上 Content-Type:application/x-www-form-urlencoded,key=value数据应该是url转义的(H)curl -d ‘name=admin&password=123’ 192.168.41.203
curl -H ‘Content-Type: text/xml’ -d ‘hello’ 10.2.19.250:8181/upload -v #发送 XML 数据
curl -H ‘Content-Type: application/json’ -d ‘{“query”: “cats”}’ 10.2.19.250:8181/upload -v #发送 json 数据
–data-urlencode将发送的数据进行 URL 编码(H)curl --data-urlencode ‘name=admin password=123’ 192.168.41.203
–data-binary将发送的数据进行二进制编码(H)
–data-raw无法读取文件,只能发送字符串(H)
-D/–dump-header将响应头写入文件curl 192.168.41.203 -D test.txt
-
E
-e/–referer设置请求头 Referer,表示请求的来源(H)curl -e ‘a.com’ 192.168.41.203 -v
curl -e ‘’ 192.168.41.203 -v
-
F
-F/–form <name=content>模拟用户在浏览器上“submit”的操作,请求头会自动加上 Content-Type: multipart/form-data(H)curl -F ‘file=@1.html’ ‘http://10.2.19.250:8181/upload’ -v
curl -F ‘name=johnny’ ‘http://10.2.19.250:8181/upload’ -v #提交表单
curl -F ‘file=@1.html;filename=11.html’ ‘http://10.2.19.250:8181/upload’ -v #修改服务器接收到的文件名为 11.html
-
G
-g/–globoff关闭 URL 的通配符功能,允许 URL 中含有字符 {} 和[]curl -g “http://[5af0::2]” -v
-G/–get构造 URL 的查询字符串,以 GET 方式发送数据(H)curl -G -d ‘a=1’ -d ‘b=2’ 192.168.41.203 -v
curl -G --data-urlencode ‘id=1 or 1=2’ 192.168.41.148 -v
-
H
-h/–help显示简要帮助手册curl -h
H/–header
添加 HTTP 请求头curl -H ‘Host:a.com’ 192.168.41.203 -v -
I
-i/–include打印响应头和网页代码curl -i 192.168.41.203
–interface 使用指定网卡传输数据curl --interface en0 www.baidu.com -v
-I/–head向服务器发出 HEAD 请求,打印响应头curl -I 192.168.41.203
-
K
-k/–insecure允许连接到 SSL 站点,而不使用证书curl -k ‘https://192.168.41.205’
-K/–config 从指定的文件中读取选项curl -K curl.options 192.168.41.203
-
L
-L/–location跟随服务器的重定向(H)curl -L 192.168.41.148 -v
–limit-rate 限制请求和响应的带宽,模拟慢网速的环境curl --limit-rate 100k 192.168.41.203 #将带宽限制在每秒 200K 字节(单位还有 m/g)
curl --limit-rate 1 192.168.41.203 #不指定单位时,默认单位为字节
–local-port 为连接指定一个本地端口curl --local-port 5000 192.168.41.203 -v
-
M
-m/–max-time 最大传输时间curl -O ‘http://10.2.19.250/400k.html’ --limit-rate 10k -m 2 # 2s 后停止下载
-M/–manual显示完整帮助手册curl -M
-
N
–next在一个命令行中处理多个 URLcurl -vL 192.168.41.148:81 --next -v 192.168.41.203 --next -k “https://192.168.41.205”
-
O
-o/–output 将服务器的响应体保存成文件curl 192.168.41.203 -o test.txt
-O/–remote-name将服务器的响应体保存成文件,并用 URL 的最后一部分作为文件名curl -O ‘http://192.168.41.203/index.html’
-
Q
-q如果这个参数是 curl 命令的第一个参数,那么 curl 命令将不会去读取默认的 curlrc 配置文件
-
R
-r/–range 仅检索范围内的字节,分段下载curl ‘http://192.168.41.203/index.html’ -O -r 0-1
-
S
-s/–silent不打印错误和进度信息,如果返回正确会打印响应体curl -s ‘https://192.168.41.203’
curl -s -o /dev/null ‘https:192.168.41.203’ #不打印任何信息
-S/–show-error只输出错误信息,通常与 -s 一起使用curl -S -s -o /dev/null ‘https://192.168.41.203’
-
T
–trace 启用对所有数据包传输的追踪,并记录到指定文件中。当文件名是符号 - 时,追踪消息将输出到标准输出curl --trace - 192.168.41.203
curl --trace dump 192.168.41.203
curl --trace-ascii - 192.168.41.203
–trace-time添加时间戳,与 -v 和 --trace - 结合curl -v --trace-time 192.168.41.203
curl --trace - --trace-time 192.168.41.203
-T/–upload-file 将文件上传到指定位置curl -T 1.html “ftp://192.168.41.206/pub/”
-
U
-u/–user user:password设置 HTTP 认证的用户名和密码curl -u ‘admin:FE9F9x7q’ ‘https://192.168.41.148:9443/login’ -k -v
curl -u ‘admin’ ‘https://192.168.41.148:9443/login’ -k -v #询问密码
-
V
-v/–verbose打印关于请求和响应的详细信息curl -v 192.168.41.203
-V/–version显示版本号curl -V
W
-w/–write-out 格式化输出请求结果curl -v ‘http://192.168.41.203’ -w %{http_code} #可用变量见【六】
-
X
-x/–proxy <proxyhost[:port]>在指定的端口上使用代理,如果没有指定代理协议,默认为 HTTPcurl -v “www.google.com” -x “socks5://127.0.0.1:1081”
-X/–request 指定 HTTP 请求的方法curl -X POST 10.2.19.250:8181/upload -v #(方法还包括 GET/PUT/DELETE)
-
Z
-z/–time-cond
curl -z -15-Jun-20 192.168.41.203 -v # 2020-6-15之前有更新就返回响应体
-#/–progress-bar显示进度条curl -# -O ‘http://10.2.19.250/400k.html’
-0/–http1.0强制在 HTTP 传输时使用 1.0 协议,默认使用 HTTP 1.1(H)curl -0 192.168.41.203 -v
–http2强制在 HTTP 传输时使用 HTTP2 协议,默认使用 HTTP 1.1(H)curl -vk --http2 “https://192.168.41.148”
-1/–tlsv1强制在 SSL 传输时使用 TLS v1 协议(H)
-2/–sslv2强制在 SSL 传输时使用 SSL v2 协议(H)
-3/–sslv3强制在 SSL 传输时使用 SSL v3 协议(H)
-4/–ipv4将域名解析为 IPv4 地址curl -4 www.baidu.com -v
-6/–ipv6将域名解析为 IPv6 地址curl -6 www.baidu.com -v
注:(H) 表示仅适用 HTTP/HTTPS ,(F) 表示仅适用于 FTP
四、浏览器 → curl
Chrome:F12 → 选择 Network 选项卡 -> 选择一个请求 → 右键选择 Copy → 选择 Copy as cURL
了解了curl的请求内容,那么接下来就要用java(spring)去接收请求
@RequestParm使用
使用@RequestParm用于绑定controller上的参数,可以是多个参数,也可以是一个Map集合,GET,POST均可
@PostMapping(value = "requestParam")
@ResponseBody
public Boolean requestParam(@RequestParam(name = "string",required = false) String str,@RequestParam(name = "integer",defaultValue = "123456") int integer){
System.out.println("str:"+str);
System.out.println("integer:"+integer);
return true;
}
@RequestParm中name属性是指定参数名,required属性默认为ture,表示必传。若为false则为非必传。属性有defaultValue默认值选项,若该参数为null时,会将默认值填充到参数上。
@PostMapping(value = "paraMap")
@ResponseBody
public Map paraMap(@RequestParam Map<String, String> map){
System.out.println("map name:"+map);
return map;
}
@使用RequestParam的要求
- 均支持POST,GET请求
- 只支持Content-Type: 为 application/x-www-form-urlencoded编码的内容。Http协议中,如果不指定Content-Type,则默认传递的参数就是application/x-www-form-urlencoded类型)
@RequestParm使用
@RequestBody绑定一个对象实体
@PostMapping(value = "requestBody")
@ResponseBody
public User requestBody(@RequestBody User user){
System.out.println("user:"+user.getName());
return user;
}
@使用RequestBody的要求
- 不支持get请求,因为get请求没有HttpEntity
- 必须要在请求头中申明content-Type(如application/json).springMvc通过HandlerAdapter配置的HttpMessageConverters解析httpEntity的数据,并绑定到相应的bean上
- 只能一个@RequestBody。
- 可以与@RequestParam一起使用,但建议最好不要与@RequestParam一起使用,是因为@RequestBody会将InputStream吃掉,造成后面的@RequsetParam无法匹配到参数而报400
小结
区别 | @RequestParam | @RequestBody |
---|---|---|
content-type | 仅支持x-www-form-urlencoded | 支持json格式 |
请求类型 | ALL | 除了GET |
注解个数 | 可多个 | 只能一个 |
本来想整理一下@RequestParam和@RequestBody对应接收的contentType类型,但是网上的资料五花八门的,各有各的说辞我没有做更多测试,但是根据目前的需求内容,我可以做一个相对简化的处理,如若有错的地方,烦请各位大佬指出。
需求的流程中,第一步是接收一个-d参数的curl请求,目的是将该请求的参数条件转发到一个指定地址上去,所以我理解并不需要做解析的处理,因为解析后再发送请求的时候,还是需要重新再拼成一个字符串,所以没有必要做参数的中转。
于是我就用@RequestBody将请求中的body内容用String类型的变量完整的接收下来,然后在RestTemplate中,将接收到的字符串params原封不动的传给HTTPEntity当做参数
接收请求
@PostMapping("/api")
@ApiOperation("接口")
public String forwardApi(@RequestBody String params) {
return sendPostRequest(url, params);
}
发送请求
public static String sendPostRequest(String url, String params) {
RestTemplate client = new RestTemplate();
//新建Http头,add方法可以添加参数
HttpHeaders headers = new HttpHeaders();
//设置请求发送方式
HttpMethod method = HttpMethod.POST;
// 以表单的方式提交 public static final MediaType APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//将请求头部和参数合成一个请求
HttpEntity<String> requestEntity = new HttpEntity<>(params, headers);
//执行HTTP请求,将返回的结构使用String 类格式化(可设置为对应返回值格式的类)
ResponseEntity<String> response = client.exchange(url, method, requestEntity, String.class);
return response.getBody();
}
这是我理解的转发流程,上面的资料和代码中有些是粘贴于几个博主的文章,但是忘了添加说明了,我以后注意这个问题哈,还需要目标接口完成后进行测试,先记录到此吧,测试完验证一下我的想法是否正确。
后续:又需要对图片和文件进行转发,我一开始使用redirect的函数进行直接跳转,但是线上环境的服务器无法访问目标地址,所以只能先将数据取回到缓存,然后吐给调用方。
代码如下不废话:
public void getPdf(@PathVariable(“erp”) String erp, @PathVariable(“reportType”) String reportType, @PathVariable(“reportName”) String reportName, HttpServletResponse response) throws IOException {
URL url = new URL(reportUrl + “/pdf/” + erp + “/” + reportType + “/” + reportName);
URLConnection conn = url.openConnection();
InputStream inStream = conn.getInputStream();
BufferedInputStream br = new BufferedInputStream(inStream);
byte[] buf = new byte[1024];
int len = 0;
response.reset();
URL u = new URL(reportUrl + “/pdf/” + erp + “/” + reportType + “/” + reportName);
response.setContentType(u.openConnection().getContentType());
response.setHeader(“Content-Disposition”, “inline; filename=” + reportName);
OutputStream out = response.getOutputStream();
while ((len = br.read(buf)) > 0) {
out.write(buf, 0, len);
}
br.close();
out.close();
}
在这其中遇到了一个不太懂的问题,我能获取到InputStream流数据,但是很多文章上说需要FileInputStream流数据作为输出的数据格式,我查阅了一些资料没有收获(可能是没找对),其实主要是对流进行格式的处理和返回头的设置,所以经过各种测试,终于找到了一种可行的方案。ps:对于返回的文件类型处理,本来以为需要根据不同格式进行不同设置,指定具体的返回格式,但是发现getContentType()方法可以完美获取所有格式类型,与其他文章的内容有所不同的是,其他的文章都需要定义file类型的路径或者地址,但是我直接用流进行操作,无需转码base64也可以正确返回数据。其中的具体细节我还没来得及研究,先附上代码留个印象,等有时间再仔细比对各种流数据的区别。