最近做项目的时候,前端异步请求用到了尤大推荐的axios,发现一个问题,就是POST请求的时候,后台人员说他们的接口里面取不到我传过去的数据。
案例重现
let axios = import('axios');
instance = ate({baseURL: '/ghcws',timeout: 10000,
});
export default instance;
import axios = import('./axios');
export async function () {axios.post('/api/doLogin', {usesrname: 'admin',password: 'admin'})
}
@RequestMappting("/api/doLogin")
public Object doLogin(@RequestParam String username, @RequestParam String password) throws Exception {System.out.println("username: "+username);System.out.println("password: "+password);JSONObject json = new JSONObject();json.put("success", true);return json;
}
这个时候前端发的请求后端就接收不到参数了。
我们可以打开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的源码
ate = function create(instanceConfig) {return (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 adapteradapter = require('./adapters/xhr');} else if (typeof process !== 'undefined') {// For node use HTTP adapteradapter = 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');String();}/* 关键点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] = (DEFAULT_CONTENT_TYPE);
});
ports = defaults;
通过上面的源码注解,我们找到了着手点:
改写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 = ate({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 && UpperCase() === UpperCase()) {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');String();}/* 这里是重点,其他的其实可以照着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方法也是可以通过修改ansformRequest实现相同效果。
那么现在问题虽然解决了,但是为什么之前后端就是接收不到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: "("username"));System.out.println("password: "("password"));JSONObject json = new JSONObject();json.put("success", true);return json;
}
通过@RequestBody 注解,springmvc可以把json中的数据绑定到Map中, 我们就可以取出了.
或者也可以
@RequestBody Pojo pojo
这样也可以把json绑定到实体类中,也能取到参数。
总共三种解决方案:
本文发布于:2024-01-28 19:02:13,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17064397389580.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |