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\"}";
}
}
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: [email protected]
* @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)
。
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}`
})
// ...
标签:Web,sendData,const,请求,编码,URL,丢失,data,log
From: https://www.cnblogs.com/yang37/p/18215272