作者: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註解
2.2Filer中增加允許跨域請求的請求頭
這裏直接上代碼:
@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不會生效。
2.3Controller接口配置
3.測試瀏覽器訪問
可以發現所有請求頭都已經加上,能夠正常訪問數據了。
4.Android端請求服務端數據
Android端這裏使用Kotlin+OKhttp+Retrofit的方式訪問服務端
4.1初始化OKhttp
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
4.2初始化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端訪問
可以發現Android端根本不存在跨域請求問題。