Http请求纪要

一、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> &#47;admin&#47;page&#47;wecom&#47;tag&#47;json&#47;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上一粘贴、就可以直接执行,如下图
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值