这个时候前端发的请求后端就接收不到参数了。

我们可以打开chrome开发者工具,看看axios的请求的请求头详情,发现Request-Headers的**Content-Type是application/json;charset=UTF-8**,Request Payload为

{username: "admin", password: "admin"}

我们同样的用jquery的ajax把我们这个请求同样的发送一遍

发现Request-Headers的**Content-Type是application/x-www-form-urlencoded;charset=UTF-8**,URL encode为

username=admin&password=admin

到这里,由于是前端换了一个发送ajax请求的工具,导致以前的接口不能用了,后端朋友们首先想到的就是我们前端人员写错了,然后我们就要开始苦逼的研究了。

可以看出,两个请求唯一的不同就是**Content-Type**的问题,朋友们,是Request Headers中的Content-Type哈,不是Response中的哈,不要搞错了。

那不同点找到了,那我们就可以开始搞了,我们大胆的猜想,如果把axios的post请求的Content-Type也变成**application/x-www-form-urlencoded**,那么问题想必就迎刃而解了。

我们看看axios的源码

axios.create = function create(instanceConfig) { return createInstance(utils.merge(defaults, instanceConfig)); };

create方法就是把我们传入的参数和默认参数合并,然后创建一个axios实例,我们再看看defaults这个配置对象

var utils = require('./utils'); var normalizeHeaderName = require('./helpers/normalizeHeaderName'); /* 这个表明默认的Content-Type就是我们想要的 */ var DEFAULT_CONTENT_TYPE = { 'Content-Type': 'application/x-www-form-urlencoded' }; /* 看方法名就知道,这个是设置ContentType用的(Content-Type没有设置的时候) */ function setContentTypeIfUnset(headers, value) { if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { headers['Content-Type'] = value; } } /* 这个是用来区别对待浏览器和nodejs请求发起工具的区别的 */ function getDefaultAdapter() { var adapter; if (typeof XMLHttpRequest !== 'undefined') { // For browsers use XHR adapter adapter = require('./adapters/xhr'); } else if (typeof process !== 'undefined') { // For node use HTTP adapter adapter = require('./adapters/http'); } return adapter; } /* 这里终于看到了万众期待的默认配置 */ var defaults = { adapter: getDefaultAdapter(), /* 这个transformRequest配置就厉害了 * 官方描述`transformRequest` allows changes to the request data before it is sent to the server * 这个函数是接受我们传递的参数,并且在发送到服务器前,可以对其进行更改 * */ transformRequest: [function transformRequest(data, headers) { normalizeHeaderName(headers, 'Content-Type'); if (utils.isFormData(data) || utils.isArrayBuffer(data) || utils.isStream(data) || utils.isFile(data) || utils.isBlob(data) ) { return data; } if (utils.isArrayBufferView(data)) { return data.buffer; } /* 关键点1、如果用URLSearchParams对象传递参数,就可以用我们想要的Content-Type传递 */ if (utils.isURLSearchParams(data)) { setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); return data.toString(); } /* 关键点2、这里我们看到,如果参数Object的话,就是通过json传递 */ if (utils.isObject(data)) { setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); return JSON.stringify(data); } return data; }], transformResponse: [function transformResponse(data) { /*eslint no-param-reassign:0*/ if (typeof data === 'string') { try { data = JSON.parse(data); } catch (e) { /* Ignore */ } } return data; }], timeout: 0, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, validateStatus: function validateStatus(status) { return status >= 200 && status < 300; } }; defaults.headers = { common: { 'Accept': 'application/json, text/plain, */*' } }; utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { defaults.headers[method] = {}; }); utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); }); module.exports = defaults;

通过上面的源码注解,我们找到了着手点:

用URLSearchParams传递参数 改写transformRequest

很显然,如果我们不是通过axios.create方法创建实例,再拿来调用,我们就只能采用第一种解决办法

第一种方法解决方案

改写userService

import axios = import('axios'); let param = new URLSearchParams(); param.append("username", "admin"); param.append("password", "admin"); export async function () { axios.post('/api/doLogin', param) }

果不其然,这就成功了。

如果不想用URLSearchParams,还是觉得Json方便,那么我们可以重新配置transformRequest

第二种方法解决方案

改写axios的create的配置

import axios from 'axios'; // 这里我自己重写了一下类型判断的所有方法,当然也可以用util库 import { isFormData, isArrayBuffer, isStream, isFile, isBlob, isURLSearchParams, isObject, isUndefined } from './Type'; function setContentTypeIfUnset(headers, value) { if (!isUndefined(headers) && isUndefined(headers['Content-Type'])) { headers['Content-Type'] = value; } } const instance = axios.create({ baseURL: '/ghcws', timeout: 10000, transformRequest: [function transformRequest(data, headers) { /* 把类似content-type这种改成Content-Type */ let keys = Object.keys(headers); let normalizedName = 'Content-Type'; keys.forEach(name => { if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) { headers[normalizedName] = headers[name]; delete headers[name]; } }); if (isFormData(data) || isArrayBuffer(data) || isStream(data) || isFile(data) || isBlob(data) ) { return data; } if (isURLSearchParams(data)) { setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); return data.toString(); } /* 这里是重点,其他的其实可以照着axios的源码抄 */ /* 这里就是用来解决POST提交json数据的时候是直接把整个json放在request payload中提交过去的情况 * 这里简单处理下,把 {name: 'admin', pwd: 123}这种转换成name=admin&pwd=123就可以通过 * x-www-form-urlencoded这种方式提交 * */ if (isObject(data)) { setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); let keys2 = Object.keys(data); /* 这里就是把json变成url形式,并进行encode */ return encodeURI(keys2.map(name => `${name}=${data[name]}`).join('&')); } return data; }] }); export default instance;

当然不用create方法也是可以通过修改axios.defaults.transformRequest实现相同效果。

那么现在问题虽然解决了,但是为什么之前后端就是接收不到json类型的参数呢???

其实原因很简单,因为axios post一个对象到后端的时候,是直接把json放到**请求体**中的,提交到后端的,而后端是怎么取参数的,是用的

@RequestParam

这个是什么意思,这个是只能从请求的地址中取出参数,也就是只能从**username=admin&password=admin**这种字符串中解析出参数,这样是不能提取出请求体中的参数的。

那么现在我们又可以大胆的猜想了,如果我们不这么去取参数,而是直接去请求体中取参数不就行了么。

我们可以不改前端,只需要改改后端代码就可以了。

解决方案

@RequestMappting("/api/doLogin") @ResponseBody public Object doLogin(@RequestBody Map map) throws Exception { System.out.println("username: "+map.get("username")); System.out.println("password: "+map.get("password")); JSONObject json = new JSONObject(); json.put("success", true); return json; }

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

最后总结我的面试经验

2021年的金三银四一眨眼就到了,对于很多人来说是跳槽的好机会,大厂面试远没有我们想的那么困难,摆好心态,做好准备,你也可以的。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。

BAT面试经验

实战系列:Spring全家桶+Redis等

其他相关的电子书:源码+调优

面试真题:

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取! **

[外链图片转存中…(img-ixtPWrBb-1713065319124)]

[外链图片转存中…(img-aTeqJ7kK-1713065319125)] 《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

文章链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。