vue axios跨域访问相关问题 | axios默认发送‘applicationx-www-form-urlencoded‘格式数据 | Content-Type is not allowed b

    技术2022-07-12  76

    文章目录

    概述报错1 Content-Type is not allowed by Access-Control-Allow-Headers in preflight respon报错2 返回状态码500

    好久没更博客了, 最近一直搞框架搞项目, 好多问题也都没有记录下来…

    好吧, 那从今天起来, 继续开始保持记录的好习惯, 先写一下在 axios 上踩下了这么多坑.

    概述

    通过以下两个报错, 来介绍解决使用 axios 来进行网络请求中的遇到的跨域问题

    报错一 对应 服务器端需要的处理方式

    报错二 对应 客户端需要的处理方式

    这两种是都要添加进去的 !!!

    报错1 Content-Type is not allowed by Access-Control-Allow-Headers in preflight respon

    这是因为 Access-Control-Allow-Headers 这个请求头没有在后端设置好, 解决方案就是在后端的过滤器上设置好五行 response 加上判断预处理

    这里的的 OPTIONS 请求方式, 是 axios 的预处理

    比如发一次 POST, 他会先发一次 OPTIONS 再发一次 POST

    如果没有对 request.getParameter() 取值进行处理, 那么第一次将是null, 导致报错中止请求, 所以需要主动给返回成功!

    package com.excellence.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; @WebFilter ( "/*" ) public class CORSFilter implements Filter { public void destroy ( ) { } public void doFilter ( ServletRequest req, ServletResponse resp, FilterChain chain ) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; response.setHeader( "Access-Control-Allow-Origin", request.getHeader( "Origin" ) ); response.setHeader( "Access-Control-Allow-Methods", "*" ); response.setHeader( "Access-Control-Allow-Headers", "*" ); response.setHeader( "Access-Control-Allow-Credentials", "true" ); response.setHeader( "Access-Control-Max-Age", "3600" ); if ( request.getMethod( ).equalsIgnoreCase( "OPTIONS" ) ) { resp.getOutputStream( ).write( "Success".getBytes( StandardCharsets.UTF_8 ) ); } else { chain.doFilter( (ServletRequest) request, (ServletResponse) response ); } } public void init ( FilterConfig config ) throws ServletException { } }

    报错2 返回状态码500

    返回 500 是服务器内部错误, 也就是 tomcat 里抛了异常直接停了

    这里一般就是request.getParameters() 这里取不到值了

    一般 ajax 提交的数据有三种

    application/x-www-form-urlencodedmultipart/form-dataapplication/json

    区别可以点击传送看一下 https://zhuanlan.zhihu.com/p/129057481

    像表单提交, jquery的ajax 都是默认第一种, 但是 axios 默认是第三种, 是json

    因为request.getParamer 只能获取第一种表单类型的值, 所以, 通过 axios 发送的 json类型的请求是没办法通request.getParameter 来获取的

    所以 request.getParameter 直接获取了 null, 如果没有处理, 那么就会直接报错抛出异常, 那么这次请求就终止了, 返回了500 凉凉

    因此, 这里有两种解决方案, 分别对应 服务器端 和 客户端 怎么处理请求数据类型

    一种是, 既然你发送 json, 那么我就处理 json 呗

    也就是说, 在后端进行流读取字符串

    然后借助第三方 json 处理工具 在后台处理, 处理完后放到 session 里, 其他地方调用即可

    这里用的是 jackson 来反序列化json, 处理完放 session 里就好, 就略了, 这里只展示反序列化了

    // 读取请求内容 BufferedReader br = new BufferedReader( new InputStreamReader( request.getInputStream( ), StandardCharsets.UTF_8 ) ); String line = null; StringBuilder sb = new StringBuilder( ); while ( (line = br.readLine( )) != null ) { sb.append( line ); } // 将字符串转换为json对象 ObjectMapper objectMapper = new ObjectMapper( ); JsonNode jsonNode = objectMapper.readTree( sb.toString( ) ); String str_method = jsonNode.get( "params" ).get( "method" ).asText( ); // String str_method = jsonNode.at( "/params/method" ).asText( ); try { Method method = FindGoodsService.class.getMethod( str_method, HttpServletRequest.class, HttpServletResponse.class ); method.invoke( new FindGoodsService( ), request, response ); } catch ( NoSuchMethodException | IllegalAccessException | InvocationTargetException e ) { e.printStackTrace( ); }

    另一种方法, 则是在客户端处理, 将发送数据类型设置为可以处理的 x-www-www-form-urlencoded

    听起来是简单, 但是对于 axios 来说可就费事儿了

    经过数小时的翻阅, 在一堆复读机声中穿插而过 …

    总结了一下, 大概有三种方法实现

    <1> 自己配置 axios, 改成自己的默认配置

    这是因为官方文档中的设置 header{} 里设置ContenType无效, 据说是一个bug, 在 github 好长时间了

    <2> 用 vue 默认配置代理的方式实现跨域

    此坑待 vue 全家桶全部搞通再来填, 或者补充到 vue的文章里

    <3> 字符串格式库 qs

    这也是我的解决方案

    这个 qs 是第三方库, 网上很多都是本地 npm install 这个 然后在 js 中导入

    其实 bootstrap 官网有 qs 的 cdn 的, 对于一些小 demo 级别的项目, 直接引入就行

    注意看 axios中 对应 params: 的位置 是 Qs.stringfy

    这种方法亲测可行! 在浏览器的控制台里 Content-Type 也是 x-www-form…类型, 发送的数据也是 request.getParameter() 可以接收到的

    时间紧迫, 这里就不求甚解, 只记录这个可行的方案了, 如果有大神路过, 还请交流下这个精妙的方法的理解!

    上代码

    getJoke_post: function () { axios.post( "http://localhost:8080/ExcellenceShoppingSystem/FindGoodsServlet", Qs.stringify({ method: 'findAllGoods', currentPage: '1', pageSize: '100', })).then( response => { console.log(response) this.info = response.data; }, error => { console.log(error); } ) } }

    附全代码, 直接 cv 跑就行

    里面的五个 cdn 分别是 jquery( 此处未用到 ), bootstrap插件( 此处未用到 ), qs, axios, vue

    <!DOCTYPE html> <!-- * @Author : acmaker * @Date : 2020-07-01 15:16:32 * @LastEditTime: 2020-07-01 19:27:14 * @FilePath : \vue-study\cors.html * @Website : http://csdn.acmaker.vip * @Description : --> <html> <head> <title>Title</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link href="./img/favicon.ico" type="image/x-icon" rel="shortcut icon" /> <link href="https://cdn.bootcss.com/font-awesome/5.11.2/css/all.min.css" rel="stylesheet" /> <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet" /> <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" rel="stylesheet" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous" /> <!-- <link href="./css/index.css" type="text/css" rel="stylesheet" /> --> </head> <body> <!-- --> <div id="vm" class="container"> <button @click="getJoke_get">get joke by get</button> <button @click="getJoke_post">get joke by post</button> <ul> <li v-for="(v,i) in arr">{{v}}</li> </ul> <p> {{info}} </p> </div> <!-- TODO: --> <!-- --> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> <script src="https://cdn.bootcdn.net/ajax/libs/qs/6.9.4/qs.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: "#vm", data: { arr: [], info: '', }, methods: { getJoke_get: function () { axios.get("https://autumnfish.cn/api/joke/list", { params: { num: 10 } }).then( response => { console.log(Object.values(response.data.jokes)); this.arr = Object.values(response.data.jokes); }, error => { console.log(error); } ) }, getJoke_post: function () { axios.post( "http://localhost:8080/ExcellenceShoppingSystem/FindGoodsServlet", Qs.stringify({ method: 'findAllGoods', currentPage: '1', pageSize: '100', })).then( response => { console.log(response) this.info = response.data; }, error => { console.log(error); } ) } } }) </script> </body> </html>
    Processed: 0.011, SQL: 9