一、Java接收请求参数的常见方式
1、前端请求参数放在路径上的情况
1)使用路径参数注解@PathVariable,注解属性value(默认),指明对应参数名
@GetMapping(value = "/getInfo/{gId}/{gName}")
public AjaxResult getInfo(@PathVariable("gId") Long gId, @PathVariable("gName") Long gName)
{
return AjaxResult.success(ylJgService.selectYlJgByGId(gId));
}
前端请求时只需要将参数放在路径上即可,如下
http://localhost:8080/wms/getInfo/1001/海口市
2)通过方法形参接收请求参数:就是直接把请求参数写在Controller方法的形参中,要求形参名称与请求参数名称完全相同
@RequestMapping("/register")
public String register(String uname, String upass, String reupass){
if("wangguodong".equals(uname) && "123".equals(upass) && "123".equals(reupass)){
return "login" ;
}else{
model.addAttribute("uname", uname) ;
return "register" ;
}
}
3)通过实体类接受请求参数,要求实体类字段名与请求参数名相同;不相同不会报错,但是参数接收不到
这种方法与“前端将参数放在请求体”、后端通过@RequestBody接收有类似的地方,但是不用混淆。
3中说的还是“前端将参数放在请求路径”这种情况,后端接收也不用@RequestBody;只写一个实体类,只要字段名与参数名对应,也可以接收参数。
4)通过@RequestParam接收请求参数,请求参数名可以与接收参数名称不一致,如果不一致通过注解的value值(默认)来确定对应关系
@RequestMapping("/login")
public String login(@RequestParam String uname, @RequestParam String upass){
if("wangguodong".equals(uname) && "123".equals(upass)){
return "main";
}else{
model.addAttribute("messageError", "用户名或密码错误") ;
return "login";
}
}
5)通过HttpServletRequest接收请求参数,适用于get和post请求方式。
@RequestMapping("/login")
public String login(HttpServletRequest request){
String uname = request.getParameter("uname") ;
String upass = request.getParameter("upass") ;
if("wangguodong".equals(uname) && "123".equals(upass)){
return "main";
}else{
model.addAttribute("messageError", "用户名或密码错误") ;
return "login";
}
}
2、前端请求参数放在请求体的情况,补充:Get、Post、Put等请求,都可以将参数放在body中(但是get有大小限制)
1)使用@RequestBody注解接收请求参数,如下图
2)文件参数,即MultipartFile类型的参数,也要写参数名;再postman中可以这样测试
注意,传输文件的请求必须用post请求。
因为get请求有大小限制,
get请求有大小限制这种说法也不准确,其实http协议对get请求没有大小、长度限制,限制产生在浏览器和服务器。
服务器一般限制get请求大小在8kb以内;
而浏览器又随型号不通、限制也各不相同,MSIE和Safari的长度限制是2kb,Opear是4kb,Firefox是8KB…
3、前端在一个接口中即传普通参数、又传文件参数,后端的接收方法,以及用Postman的测试方式
1)postman测试
传参方式:参数放在请求体中,用form-data格式;普通格式参数选text,文件参数选择file
2)后端可以用实体类直接接收
后端接收方式有点类似参数放在请求路径时那种,但只是类似、并不一样,参数中有文件,入参是放在请求体中的。
4、前端以Base64串的格式传输图片,后端接收、以及postman测试方法
1)场景描述
前端传的图片,不是以文件流格式,无法用MultipartFile对接接收,传的是base64字符串,如图所示
2)后端接收及处理方式
我的方式是,后端先用字符串接收,然后处理成MultipartFile。
提供两个工具类
public class ImageUtils {
public static MultipartFile base64ToMultipartFile(String base64) {
//base64编码后的图片有头信息所以要分离出来 [0]data:image/png;base64, 图片内容为索引[1]
String[] baseStrs = base64.split(",");
//取索引为1的元素进行处理
byte[] b = Base64.decode(baseStrs[1]);
for (int i = 0; i < b.length; ++i) {
if (b[i] < 0) {
b[i] += 256;
}
}
//处理过后的数据通过Base64DecodeMultipartFile转换为MultipartFile对象
return new Base64DecodeMultipartFile(b, baseStrs[0]);
}
}
public class Base64DecodeMultipartFile implements MultipartFile {
private final byte[] imgContent;
private final String header;
public Base64DecodeMultipartFile(byte[] imgContent, String header) {
this.imgContent = imgContent;
this.header = header.split(";")[0];
}
@Override
public String getName() {
return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];
}
@Override
public String getOriginalFilename() {
return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1];
}
@Override
public String getContentType() {
return header.split(":")[1];
}
@Override
public boolean isEmpty() {
return imgContent == null || imgContent.length == 0;
}
@Override
public long getSize() {
return imgContent.length;
}
@Override
public byte[] getBytes() throws IOException {
return imgContent;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(imgContent);
}
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
new FileOutputStream(dest).write(imgContent);
}
}
使用方式如下:
//base64串用Record对象中的picFile字符串接收,然后通过上面的工具类型进行转换
String picFile = record.getPicFile();
if(StringUtil.isEmpty(picFile)){
return Result.failure("请上传推荐截图");
}else {
//用上面提供的工具类进行转换
MultipartFile multipartFile = ImageUtils.base64ToMultipartFile(picFile);
。。。
}
3)postman测试
就和传普通字符串一样
二、Http请求-Java后台问题汇总
1、SpringMVC项目,Controller接口没加@ResponseBody注解,导值返回404
1)问题描述
写了一个普通接口,期望返回一个对象(对象的json格式),但是实际却返回的404。
错误示例:
@PostMapping("/json/import")
public Result importByJson(@RequestBody WecomTagQuery wecomTagQuery){
Result result = Result.failure();
//推送
try{
String str = wecomTagService.saveOrUpdateByJson(wecomTagQuery.getTag_list());
result = Result.success();
result.setInfo(str);
}catch (Exception e){
e.printStackTrace();
result.setInfo("执行失败!");
}
return result;
}
错误结果:
<!doctype html>
<html lang="en">
<head><title>HTTP Status 404 – Not Found</title><style type="text/css">h1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} h2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} h3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} body {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} b {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} p {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;} a {color:black;} a.name {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head>
<body><h1>HTTP Status 404 – Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Message</b> /admin/page/wecom/tag/json/import.jsp</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr class="line" /><h3>Apache Tomcat/8.5.39</h3></body>
</html>
2)原因分析
不加**@ResponseBody**注解springmvc框架会认为方法返回的是一个 ModelAndViewer对象,即视图对象,那么它就会去找这么一个对象,找不到则报404错。
3)解决
a、
SpringMVC项目,需要直接返回结果、而不是视图的Controller接口,要加**@ResponseBody**注解
正确示例:
@PostMapping("/json/import")
@ResponseBody
public Result importByJson(@RequestBody WecomTagQuery wecomTagQuery){
Result result = Result.failure();
//推送
try{
String str = wecomTagService.saveOrUpdateByJson(wecomTagQuery.getTag_list());
result = Result.success();
result.setInfo(str);
}catch (Exception e){
e.printStackTrace();
result.setInfo("执行失败!");
}
return result;
}
b、
现在越来越多系统是前后端分离的项目(前端用vue),这时后端controller接口返回的基本都不是视图对象,这样的话可以直接在Controller接口上、将@Controller注解替换成@RestController;
@RestController就等于@Controller + @ResponseBody,这样该Controller接口下的所有接口(直接返回json,不返回视图的接口),就都不用加@ResponseBody注解了。
2、请求体和请求路径上都有参数时,报400错误
1)问题描述
请求方式不限(post、get等都可),要求请求体中可传参数(如果有),路径上也有参数(5种方式都可);
但是我刚开始这样请求,报错“400 bad request”。
2)原因分析
因为后端接口,要求请求体传参,没有参数时可以不传参,但是请求体不能是none、请求头的需要有Context-Type。
我一开始,因为请求体没有入参,就没有设置请求体
这会导致请求头没有Context-Type,不符合后端接口的要求。
3)解决
即使这次请求没有body参数,也要设置请求体,里面不传参数就可以了
如图设置好以后,请求头就有Context-Type了
注意,body不仅不能选none,选择raw-json后,还要加{},否则也不行。
三、Http请求-生产常用方法补充
1、获取用户登录ip
1)提供一个实测有效的获取ip的工具类
/**
* 获取IP方法
*/
public class IpUtils
{
/**
* 获取客户端IP
*
* @param request 请求对象
* @return IP地址
*/
public static String getIpAddr(HttpServletRequest request)
{
if (request == null)
{
return "unknown";
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
}
/**
* 检查是否为内部IP地址
*
* @param ip IP地址
* @return 结果
*/
public static boolean internalIp(String ip)
{
byte[] addr = textToNumericFormatV4(ip);
return internalIp(addr) || "127.0.0.1".equals(ip);
}
/**
* 检查是否为内部IP地址
*
* @param addr byte地址
* @return 结果
*/
private static boolean internalIp(byte[] addr)
{
if (StringUtils.isNull(addr) || addr.length < 2)
{
return true;
}
final byte b0 = addr[0];
final byte b1 = addr[1];
// 10.x.x.x/8
final byte SECTION_1 = 0x0A;
// 172.16.x.x/12
final byte SECTION_2 = (byte) 0xAC;
final byte SECTION_3 = (byte) 0x10;
final byte SECTION_4 = (byte) 0x1F;
// 192.168.x.x/16
final byte SECTION_5 = (byte) 0xC0;
final byte SECTION_6 = (byte) 0xA8;
switch (b0)
{
case SECTION_1:
return true;
case SECTION_2:
if (b1 >= SECTION_3 && b1 <= SECTION_4)
{
return true;
}
case SECTION_5:
switch (b1)
{
case SECTION_6:
return true;
}
default:
return false;
}
}
/**
* 将IPv4地址转换成字节
*
* @param text IPv4地址
* @return byte 字节
*/
public static byte[] textToNumericFormatV4(String text)
{
if (text.length() == 0)
{
return null;
}
byte[] bytes = new byte[4];
String[] elements = text.split("\\.", -1);
try
{
long l;
int i;
switch (elements.length)
{
case 1:
l = Long.parseLong(elements[0]);
if ((l < 0L) || (l > 4294967295L))
{
return null;
}
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 2:
l = Integer.parseInt(elements[0]);
if ((l < 0L) || (l > 255L))
{
return null;
}
bytes[0] = (byte) (int) (l & 0xFF);
l = Integer.parseInt(elements[1]);
if ((l < 0L) || (l > 16777215L))
{
return null;
}
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 3:
for (i = 0; i < 2; ++i)
{
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L))
{
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
l = Integer.parseInt(elements[2]);
if ((l < 0L) || (l > 65535L))
{
return null;
}
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 4:
for (i = 0; i < 4; ++i)
{
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L))
{
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
break;
default:
return null;
}
}
catch (NumberFormatException e)
{
return null;
}
return bytes;
}
/**
* 获取IP地址
*
* @return 本地IP地址
*/
public static String getHostIp()
{
try
{
return InetAddress.getLocalHost().getHostAddress();
}
catch (UnknownHostException e)
{
}
return "127.0.0.1";
}
/**
* 获取主机名
*
* @return 本地主机名
*/
public static String getHostName()
{
try
{
return InetAddress.getLocalHost().getHostName();
}
catch (UnknownHostException e)
{
}
return "未知";
}
/**
* 从多级反向代理中获得第一个非unknown IP地址
*
* @param ip 获得的IP地址
* @return 第一个非unknown IP地址
*/
public static String getMultistageReverseProxyIp(String ip)
{
// 多级反向代理检测
if (ip != null && ip.indexOf(",") > 0)
{
final String[] ips = ip.trim().split(",");
for (String subIp : ips)
{
if (false == isUnknown(subIp))
{
ip = subIp;
break;
}
}
}
return ip;
}
/**
* 检测给定字符串是否为未知,多用于检测HTTP请求相关
*
* @param checkString 被检测的字符串
* @return 是否未知
*/
public static boolean isUnknown(String checkString)
{
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
}
}
其中第一个方法IpUtils.getIpAddr(HttpServletRequest request)
就可以实现获取ip的需求;该方法需要HttpServletRequest对象为入参,你可以在即在Controller接收HttpServletRequest对象,也可以通过客户端工具类ServletUtils获取(ServletUtils工具类网上有很多,都有getRequest()方法)。
2)应用示例
获取ip一般在用户登录接口,时机是用户登录成功以后、一般会开一个异步任务去执行获取登录信息的操作,这样设计比较优雅。
其实在异步任务里,关于用户的登录信息有很多,ip只是其中一项,这里演示一下1中说的两个工具类怎么使用
异步任务{
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
}
3)通过ip查看ip的归属地区
介绍一个不用魔法上网就能访问的网站
IP-API
通过该网站,可以查看ip属于哪个地区,有时候需要校验ip、可能会用到。
查询也非常简单,进入网站后不用登录啥的,直接在下图输入ip、点查询,然后通过返回值就可以知道ip所属地区了。
2、在服务器发请求(用cURL发请求)
1)应用场景描述
有时候需要手动发请求,而且需要通过系统服务器发;
比如,商城生产购买报错,日志显示是调用别人接口、解析返回结果失败导致的。
这时候,你无论是出于问题排查的目的、看看返回结果是啥,还是也不管为啥失败了、要重调接口获取数据,你是不是都得手动发请求?
但是很可能,人家服务器设置了访问IP限制,只有你们家生产服务器在白名单,那你是不是得在服务器发请求才行。
2)方法示例
之前我只会在本机用postman之类的工具发http请求,Linux咋发请求呢?
其实很简单,linux用cURL就可以。
cURL咋写?
其实也不用管,postman提供了转换的功能,你在postman写好请求,然后点击右侧的code,再选择转换成cURL,就可以了,如下图:
右侧这个cURL一复制,在Linux上一粘贴、就可以直接执行,如下图