作者:DCTANT

先介绍一下Web端使用的版本情况:

这里采用了Vue Cli+Webpack的形式搭建的项目,其中Vue版本为2.9.6,webpack版本为3.6.0,axios版本为0.19.0,在2019年9月19日应该算是比较新的版本了。

解决跨域请求问题不是单纯前端改改就好的,也不是后端单纯改改就好的,需要两个端配合修改才能解决问题,另外加上Android端也要相应进行配置,当然这三个端我一个人就能解决了,哈哈!

这里采用的是post json请求的方式,请求后端数据。

1.前端axios配置

  1.1避免session、cookie失效

在main.js中添加上:

import axios from 'axios'
……

中间省略无用代码

……
axios.defaults.withCredentials = true

如果不加上面这行代码,则session中无法保存任何信息,包括登录信息等等,以至于登录功能无法实现,因此这代码必须加上!

  1.2设置post方法

我这里将post请求封装到一个单独的js文件中,所以直接上整个function:

function post (interfaceName, form, callback) {
  axios({
    url: constant.url + interfaceName,
    method: 'post',
    data: JSON.stringify(form),
    headers: {
      'Content-Type': 'application/json;charset=utf-8'
    },
    // withCredentials: true
  }).then(object => {
    callback(object.data)
  })
}

由于我是请求整个form(object),因此采用JSON.stringify的方式把整个object转为json字符串传到后端请求。Content-Type使用application/json;charset=utf-8,避免乱码问题。

说白了前端改的东西非常少,后端才是最需要改的。

2.后端SpringBoot设置

  2.1Controller增加跨域请求注解

在Controller上加上@CrossOrigin注解

vue axios post 外部地址 跨越_Kotlin

Filer中增加允许跨域请求的请求头

这里直接上代码:

@WebFilter(filterName = "total",urlPatterns = "/*")
public class TotalFilter implements Filter {
    private ArrayList<String> allowOrigin = new ArrayList<>();

    public TotalFilter() {
        allowOrigin.add("http://127.0.0.1:8080");
        allowOrigin.add("http://localhost:8080");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        String origin = ((HttpServletRequest)servletRequest).getHeader("Origin");
        // INFO: DCTANT: 2019/9/19 设置允许的跨域请求源
        if(allowOrigin.contains(origin)){
            httpServletResponse.setHeader("Access-Control-Allow-Origin",origin);
        }else{
            httpServletResponse.setHeader("Access-Control-Allow-Origin","http://localhost:8080");
        }
        // INFO: DCTANT: 2019/9/19 设置允许的跨域请求头
        httpServletResponse.setHeader("Access-Control-Allow-Headers","Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, userId, token, x-requested-with, XMLHttpRequest, Accept");
        // INFO: DCTANT: 2019/9/19 设置允许的跨域请求方法
        httpServletResponse.setHeader("Access-Control-Allow-Methods","POST, GET, OPTIONS, DELETE");
        // INFO: DCTANT: 2019/9/19 设置允许跨域请求的最长时间,这里设置了30天,就为了尽量延长允许时间,
        //  时间过短会导致经常在请求前先发送一个Option请求,用于获取服务端允许哪些跨域访问类型,导致资源浪费。
        httpServletResponse.setHeader("Access-Control-Max-Age","2592000");
        // INFO: DCTANT: 2019/9/19 这个非常重要!设置允许携带证书信息,包括Session和Cookie等等
        httpServletResponse.setHeader("Access-Control-Allow-Credentials","true");
        // INFO: DCTANT: 2019/9/19 设置请求类型为json请求 
        httpServletResponse.setContentType("application/json;charset=utf-8");

        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

注意:Filter这个类创好后,要Application类中增加@ServletComponentScan(basePackages = {"***.***.***.filter"}),否则filter不会生效。

vue axios post 外部地址 跨越_Kotlin_02

  2.3Controller接口配置

vue axios post 外部地址 跨越_跨域访问_03

3.测试浏览器访问

vue axios post 外部地址 跨越_axios_04

vue axios post 外部地址 跨越_跨域访问_05

可以发现所有请求头都已经加上,能够正常访问数据了。

4.Android端请求服务端数据

Android端这里使用Kotlin+OKhttp+Retrofit的方式访问服务端

初始化OKhttp

private fun initOkhttp(): OkHttpClient {
        return OkHttpClient.Builder()
                .addInterceptor(logger)
                .connectTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .build()
    }

其中的logger请参考我的个人博客:


初始化Retrofit

private fun initRetrofit(): Retrofit {
        return Retrofit.Builder()
                .client(okHttpClient)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(if (BuildConfig.DEBUG) WebConfig.DEBUG_URL else WebConfig.RELEASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
    }

 

  4.3Retrofit请求用的Interface

interface RequestInterface {
        @POST
        @Headers("Content-Type:application/json;charset=utf-8")
        fun response(
                @Url url: String,
                @Body any: Any
        ): Observable<ResponseBody>
    }

这里采用的是观察ResponseBody的方式进行请求,可以让Body中传任何值进去。

  4.4调用接口执行观察者模式

retrofit.create(RequestInterface::class.java)
.response(requestUrl, form)
.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(object : Observer<ResponseBody> {
                    override fun onSubscribe(d: Disposable) {
                        executing = true
                    }

                    override fun onNext(responseBody: ResponseBody) {
                        val string = responseBody.string()
                    }

                    override fun onComplete() {

                    }

                    override fun onError(e: Throwable) {

                    }
                })

 

  4.5测试Android端访问

vue axios post 外部地址 跨越_axios_06

可以发现Android端根本不存在跨域请求问题。