1.复现错误
今天写好导入hive表
的接口,如下代码所示:
/**
* hive表导入
*
* @author super先生
* @datetime 2023/3/20:16:32
* @return
*/
@ResponseBody
@PostMapping(value = "/xxx/importTables")
public ServiceStatusData localHiveImportTables(
@RequestBody ImportTablesBo importTablesBo,
@RequestHeader("x-userid") Long userId) {
logger.info("入参记录:importTablesBo={},userId={}", importTablesBo, userId);
if (isBlank(importTablesBo.getHiveTableName())) {
return new ServiceStatusData(ServiceStatusData.Status.Fail, "hive表名不能为空", null);
}
if (isBlank(importTablesBo.getTableImportType())) {
return new ServiceStatusData(ServiceStatusData.Status.Fail, "导表类型不能为空", null);
}
if (isBlank(importTablesBo.getCron())) {
return new ServiceStatusData(ServiceStatusData.Status.Fail, "执行周期表达式不能为空", null);
}
if (null == importTablesBo.getDatasetId()) {
return new ServiceStatusData(ServiceStatusData.Status.Fail, "工作表id不能为空", null);
}
// TODO 调用service层的方法
......
return new ServiceStatusData(ServiceStatusData.Status.Success, "", importTablesBo);
}
启动项目后,使用postman
测试,却报出如下错误:
即Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported
的错误。
2. 分析错误
首先,将错误信息Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported
翻译成中文,便是不支持内容类型'application/x-www-form-urlencoded;charset=UTF-8'
。
换句话说,我的后端接口导入hive表
的接口,不支持application/x-www-form-urlencoded
内容类型。
那么application/x-www-form-urlencoded
是在哪里定义的呢?
当我在postman
选中x-www-form-urlencoded
,其便设置Content-Type
的属性值为x-www-form-urlencoded
,如下图所示:
x-www-form-urlencoded
为url
格式的编码,故而,传值为url
格式,比如username=tom&pwd=123
,一般使用qs
库的qs.stringify()
方法就能把json
对象转换成url
格式编码x-www-form-urlencoded
。
x-www-form-urlencoded
是post
的默认Content-Type
。
而我的后端使用@RequestBody
来接参,而@RequestBody
主要用来接收前端传递给后端的json
字符串中的数据的(请求体中的数据的)。
因而,我的错误是:后端接参和前端传参类型不一致,导致后端无法接收到前端的参数,因而,可进行如下修改。
3. 解决错误
因为后端接参和前端传参类型不一致,导致后端无法接收到前端的参数,因而,可以有如下两种解决方法。
3.1 方法1:修改后端接参方式
\
前端的Content-Type
的属性值不变,依旧为x-www-form-urlencoded
,后端使用如下两种方式来接参:
- 后端使用
HttpServletRequest
类的request.getParameter()
方法来接参,如下代码所示:
/**
* hive表导入
*
* @author super先生
* @datetime 2023/3/20:16:32
* @return
*/
@ResponseBody
@PostMapping(value = "/xxx/importTables")
public ServiceStatusData localHiveImportTables(
HttpServletRequest request,
@RequestHeader("x-userid") Long userId) {
ImportTablesBo importTablesBo = new ImportTablesBo();
importTablesBo.setHiveTableName(request.getParameter("hiveTableName"));
importTablesBo.setTableImportType(request.getParameter("tableImportType"));
importTablesBo.setPkColumn(request.getParameter("pkColumn"));
importTablesBo.setIncrementColumn(request.getParameter("incrementColumn"));
importTablesBo.setCron(request.getParameter("cron"));
importTablesBo.setDatasetId(Long.valueOf(request.getParameter("datasetId")));
logger.info("入参记录:importTablesBo={},userId={}", importTablesBo, userId);
.....
// TODO 调用service层的方法
.....
return new ServiceStatusData(ServiceStatusData.Status.Success, "", importTablesBo);
}
运行结果如下图所示:
- 后端也可以使用
@RequestParam
注解来接参,如下代码所示:
/**
* hive表导入
*
* @author super先生
* @datetime 2023/3/20:16:32
* @return
*/
@ResponseBody
@PostMapping(value = "/xxx/importTables")
public ServiceStatusData localHiveImportTables(
@RequestParam("hiveTableName") String hiveTableName,
@RequestParam("tableImportType") String tableImportType,
@RequestParam("pkColumn") String pkColumn,
@RequestParam("incrementColumn") String incrementColumn,
@RequestParam("cron") String cron,
@RequestParam("datasetId") Long datasetId,
@RequestHeader("x-userid") Long userId) {
ImportTablesBo importTablesBo = new ImportTablesBo();
importTablesBo.setHiveTableName(hiveTableName);
importTablesBo.setTableImportType(tableImportType);
importTablesBo.setPkColumn(pkColumn);
importTablesBo.setIncrementColumn(incrementColumn);
importTablesBo.setCron(cron);
importTablesBo.setDatasetId(Long.valueOf(datasetId));
logger.info("入参记录:importTablesBo={},userId={}", importTablesBo, userId);
......
// TODO 调用service层的方法
......
return new ServiceStatusData(ServiceStatusData.Status.Success, "", importTablesBo);
}
运行结果同上图一样,如下所示:
3.2 方法2:修改前端传参方式
不修改后端的代码,代码为复现错误中的代码。
既然@RequestBody
用来接收前端传递给后端的json
字符串,那么,只修改前端的传参方式。
将postman
的Content-Type
的属性值修改为application/json
,如下图所示:
请求参数如下代码所示:
{
"hiveTableName": "project",
"tableImportType": "1",
"pkColumn": "id",
"incrementColumn": "projectname",
"cron": "0 0 11 * * ?",
"datasetId": 2
}
运行结果如下图所示:
如果你是前端代码报出的这个错误,可以在前端代码中加上Content-Type: application/json; charset=UTF-8
,如下代码所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>content-type为application/json</title>
<script src="js/jquery-3.4.1.js"></script>
<script>
function importTables() {
const importTablesReq = {
"hiveTableName": "project",
"tableImportType": "1",
"pkColumn": "id",
"incrementColumn": "projectname",
"cron": "0 0 11 * * ?",
"datasetId": 2
};
$.ajax({
url: 'http://localhost:8080/.../importTables.do',
type: 'post',
headers: {
"x-userid": 1
},
data: JSON.stringify(importTablesReq),
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log("data=", data)
}
});
}
</script>
</head>
<body>
<button type="button" onclick="importTables()">点击按钮获取值</button>
</body>
</html>
输出结果如下图所示:
4. 补充说明content-type
一般是指网页中存在的Content-Type(内容类型)
,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件,这就是经常看到一些PHP
网页点击的结果却是下载一个文件或一张图片的原因。
Content-Type
标头告诉客户端实际返回的内容的内容类型。
4.1 语法格式
Content-Type: text/html; charset=utf-8
Content-Type: multipart/form-data; boundary=something
实例如下图:
4.2 常见的类型值
-
常见的媒体格式类型:
-
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
:<form encType=””>
中默认的encType
,form
表单数据被编码为key/value
格式发送到服务器(表单默认的提交数据的格式)
-
-
另外一种常见的媒体格式是上传文件之时使用的:
multipart/form-data
: 需要在表单中进行文件上传时,就需要使用该格式
5. 文末总结
正赶上最近ChatGPT
比较火,可以借助其分析上述错误,其给出的回答如下:
读者可以作为简单参考。