作者:DCTANT
先介绍一下Web端使用的版本情况:
这里采用了Vue Cli+Webpack的形式搭建的项目,其中Vue版本为2.9.6,webpack版本为3.6.0,axios版本为0.19.0,在2019年9月19日应该算是比较新的版本了。
解决跨域请求问题不是单纯前端改改就好的,也不是后端单纯改改就好的,需要两个端配合修改才能解决问题,另外加上Android端也要相应进行配置,当然这三个端我一个人就能解决了,哈哈!
这里采用的是post json请求的方式,请求后端数据。
在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,避免乱码问题。
说白了前端改的东西非常少,后端才是最需要改的。
在Controller上加上@CrossOrigin注解
这里直接上代码:
@WebFilter(filterName = "total",urlPatterns = "/*")
public class TotalFilter implements Filter {
private ArrayList 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不会生效。
可以发现所有请求头都已经加上,能够正常访问数据了。
Android端这里使用Kotlin+OKhttp+Retrofit的方式访问服务端
private fun initOkhttp(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(logger)
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build()
}
其中的logger请参考我的个人博客:
https://blog.csdn.net/DCTANT/article/details/96971304
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()
}
interface RequestInterface {
@POST
@Headers("Content-Type:application/json;charset=utf-8")
fun response(
@Url url: String,
@Body any: Any
): Observable
}
这里采用的是观察ResponseBody的方式进行请求,可以让Body中传任何值进去。
retrofit.create(RequestInterface::class.java)
.response(requestUrl, form)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer {
override fun onSubscribe(d: Disposable) {
executing = true
}
override fun onNext(responseBody: ResponseBody) {
val string = responseBody.string()
}
override fun onComplete() {
}
override fun onError(e: Throwable) {
}
})
可以发现Android端根本不存在跨域请求问题。