Web-请求数据+号丢失问题

1.背景

先来复习下URL请求的基本知识

HTTP的早期设计主要考虑了基本的文档检索需求以及表单提交功能,这直接影响了后来对POST请求和内容类型的发展。

1.1 请求方法

HTTP(超文本传输协议)最初设计的目的是为了支持万维网上的文档检索,这涉及到请求HTML页面、图像、视频等静态资源。

  • GET

    设计用于请求数据,它将请求信息编码在URL中,适用于检索操作。由于URL长度有限制,并且数据公开可见,这种方法适合非敏感数据的简单检索。

  • POST

    • 随着Web的发展,需要一种方式能在保护用户隐私的前提下发送大量数据。POST应运而生,用于发送数据到服务器以创建或更新资源。
    • 最初,POST主要用来提交表单数据,这些数据不适合通过URL传输(例如,由于安全、大小或隐私考虑)。

1.2 请求内容类型

随着Internet的应用日益广泛,对表单处理能力的需求增加,引入了几种内容类型来支持更复杂的交互:

  • application/x-www-form-urlencoded

    • 这是最早用于支持HTML表单数据提交的内容类型。由于HTML表单是早期Web交互的核心,因此这种内容类型设计来处理简单的文本数据。
    • 它简单地将表单数据编码为名/值对,与URL查询字符串使用相同的格式,不过是放在了请求体中。
  • multipart/form-data

    • 随着文件上传需求的出现,application/x-www-form-urlencoded方式由于其编码方式(特别是空间效率问题)并不适合处理文件或大量数据。
    • 因此,multipart/form-data被引入,支持表单中的文件上传以及包含二进制数据的需求。
  • application/json

    • 随着Web服务和API的普及,尤其是RESTful架构的推广,需要一种更灵活、能支持复杂数据结构(如对象和数组)的数据交换格式。
    • JSON因其易于人类读写和机器解析生成的特性,成为数据交互的首选格式,尤其在Web应用与服务器之间。

2.URL保留字符

在URL中,某些字符有特殊的意义,被称为“保留字符”。这些字符通常用于分隔URL的不同部分,如路径、查询参数和片段标识符。

字符 用途 URL编码
: 分隔协议和地址、端口号 %3A
/ 分隔URL路径的各个部分 %2F
? 标记URL的查询部分的开始 %3F
# 标记URL的片段标识符的开始 %23
[ 在URL中用于特定的语法结构 %5B
] 在URL中用于特定的语法结构 %5D
@ 在URL中指定用户名和密码 %40
! 子分隔符,有时用于注入特殊意义 %21
$ 子分隔符,用于处理特定数据 %24
& 分隔URL中的查询参数 %26
' 子分隔符,用于封装数据 %27
( 子分隔符,用于更复杂的数据结构 %28
) 子分隔符,用于更复杂的数据结构 %29
* 子分隔符,有时用于URL中的通配符 %2A
+ 子分隔符,用于空格的替代符,尤其是在表单数据中 %2B
, 子分隔符,用于分隔数据 %2C
; 分隔URL参数 %3B
= 分隔URL中的参数名称和值 %3D

好,现在问题就来了,在不进行URL编码的时候,+号默认译为空格。

3.问题场景

根据上面的分析,我们可以看到,常见的问题点在2个场景。

3.1 URL路径

跟方法get/post无关,关键是路径中直接使用+号,会被默认解释为空格。

@Slf4j @RestController @RequestMapping("/index") public class IndexController { @RequestMapping("/a") public String index1(@RequestParam String data) { log.info("data: {}", data); return "{\"ret\":\"ok\"}"; } }

image-20240527123257744

image-20240527123312969

3.2 内容类型

application/x-www-form-urlencoded使用的也是简单的字符串拼接方式,问题主要发生在这里

其余两个类型内容转换的方法不一样,不会有这个问题。

<script setup></script> <template> <div id="app"> <form @submit.prevent="handleSubmit"> <input v-model="inputData" type="text" placeholder="++++++++++++++++" /> <button type="submit">Submit</button> </form> </div> </template> <script> export default { data() { return { inputData: '' } }, methods: { handleSubmit() { console.log('Before encoding: ', this.inputData) const sourceData = this.inputData const encodedData = encodeURIComponent(this.inputData) // 源字符串 console.log('Source data: ', sourceData) // URL编码 console.log('URL Encoded data: ', encodedData) // 直接发送的是源字符串 this.sendData(sourceData) }, sendData(sendData) { fetch('http://127.0.0.1:8090/index/b', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `data=${sendData}` }) .then((response) => response.json()) .then((data) => console.log('Server response: ', data)) .catch((error) => console.error('Error:', error)) } } } </script>
package cn.yang37.za.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; /** * @description: * @class: IndexController * @author: yang37z@qq.com * @date: 2024/5/27 11:40 * @version: 1.0 */ @Slf4j @RestController @RequestMapping("/index") public class IndexController { @RequestMapping("/b") @CrossOrigin(origins = "http://localhost:5173") public String index2(@RequestParam String data) { log.info("data: {}", data); return "{\"ret\":\"ok\"}"; } }

这里,我们直接使用未编码的sourceData进行发送,即this.sendData(sourceData)

image-20240527123529125

image-20240527123607585

4.解决方案

上面可以看到,问题的根源都在于没有进行URL编码。

4.1 URL路径

// 源参数 const userInput = "这 是 一 堆 特 殊 字 符 / = &"; // url编码 const encodedInput = encodeURIComponent(userInput); // 路径里使用编码后的 const url = `https://example.com/data/${encodedInput}`;

同理啊,有没有想过,平时看到的是?key=value&key2=value2的格式。

那假设发送的key或者value有特殊符号呢,例如key是name?n&666,value是value&123

https://example.com/api?name?n&666=value&123

你看,你自己都分不清,换成这样你才理解。

https://example.com/api?name%3Fn%26666=value%26123
const baseUrl = "https://example.com/api"; const key = "name?n&666"; const value = "value&123"; const encodedKey = encodeURIComponent(key); const encodedValue = encodeURIComponent(value); const url = `${baseUrl}?${encodedKey}=${encodedValue}`; console.log(url);

当然一般不会这样,但又不是不允许。

所以,碰到特殊符号,记得想起来先编码。

4.2 内容类型

思路一致,application/x-www-form-urlencoded把要发送的内容,进行URL编码。

methods: { handleSubmit() { console.log('Before encoding: ', this.inputData) const sourceData = this.inputData const encodedData = encodeURIComponent(this.inputData) // URL编码 console.log('URL Encoded data: ', encodedData) // 用编码后的数据 this.sendData(encodedData) }, sendData(sendData) { fetch('http://127.0.0.1:8090/index/b', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `data=${sendData}` }) // ...

__EOF__

本文作者羊37
本文链接https://www.cnblogs.com/yang37/p/18215272.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   羊37  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示