您的位置:首页 > 移动开发 > IOS开发

angularjs跨域请求,html5封装进android与ios

2016-11-12 23:22 405 查看

前言

第一次正儿八经的写博客实在有点不知道怎么开头好,所学的东西也不够系统,我相信我写的东西瞄准了一个点去写,无论从哪里开始,都会让人觉得有点突然,但是,我也没办法从所谈主题的起源开始谈,所以不纠结这个次序关系了,有关主题的前后我就稍微介绍一些,主要围绕我所遇到的问题和如何去解决来谈吧。

我使用的框架是springboot+angularjs ,内容主要包括:

- 为什么需要跨域

- js跨域请求

- 使用localStorage替代本地Cookies

- 使用token替代跨域发送Cookies

下面主要针对以上几点来分别说说

为什么需要跨域

我们这个俄项目是使用springboot+angularjs开发的,web端顺利开发结束,接下来是手机端,手机端直接使用html在电脑端运行也开发的比较顺利,因为是直接使用的PC端浏览器,所以和web端开发是没有区别的,当然,手机端的浏览器也同样不会有什么问题。但是我们公司项目能打包成android和ios的app,android和ios都支持webview,按理直接把url告诉webview然后去请求服务器访问也没什么问题,毕竟通过webview的形式,理论上就相当于使用了android和ios提供的一个浏览器而已,只是它隐藏了url。

考虑到手机上的切换效果,app开发的同事会使用多个webview来切换渲染数据,像详情之类的页面,需要开启新的webview,这样问题就来了,体验就非常的差了,非常的卡!非常的慢!为什么?

我简单的做了个比较.

Web端打开新页面过程



App端打开新页面过程



过程比较:

过程webapp
第一次加载
切换页面
由此可见,使用android或ios嵌套webview来达到一般原生的切换效果和访问服务器的速度,是很难的!

此时我们想到三个方案:

app端只开启一个webview。

优点:可以减少切换页面时重新开启(特定的导航页面在切换时没有被关闭掉,但不刷新)webview的时间。

缺点:b)页面效果可能较差,并且ios打包后有可能影响appstore审核。

服务器端后台文件和前端文件分开部署。

优点:稍微加快静态页面访问速度。

缺点:提升速度不明显。

html文件本地化,打包进app文件中。

优点:不需要从服务器端请求html再返回到webview中加载,节省了加载html的时间,速度将有较大提升。

缺点:需要对当前项目做很多修改(文件访问路径方面,后续webview对接也给我们带来了许多困难)

虽然是想到了三个可行方案,但第一种方案直接被pass,第二种方案速度提升非常不明显,那只能考虑采用第三种了,将原先html文件与java文件共同打包成一个war包,然后现在需要将html文件直接打包进app文件,必然就需要js进行跨域请求了。

JS跨域请求

s跨域请求需要在js和服务器端都有申明跨域,具体如下:

原生ajax 请求可以这样写:

$.ajax({
type: 'POST',
url: "/user",
data: {
'phone': 'xxxxxxxx',
'password':'xxxxxxx'
},
withCredentials: true,   // 跨域
dataType: 'json'
}).success(function(){

});


angularjs

$http.get(url, {
withCredentials : true    //跨域
});


java过滤器代码

@Component
public class CorsFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));   //允许跨域
response.setHeader("Access-Control-Allow-Credentials", "true");
if ("OPTIONS".equals(request.getMethod())) {
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT,OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
}
filterChain.doFilter(request, response);
}
}


response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));


这行代码是有点意思的,我这里没有将值直接设为“*”号,将值设为星号无法跨域发送cookies,需要发送cookies的可以设置成

request.getHeader("origin");


这样跨域发送请求就没有问题了。

使用localStorage替代本地Cookies

跨域后有个问题真的让我头疼了很久,虽然成功跨域了,数据也都已经成功返回并渲染页面了。但依然有几个问题。

登录问题

比如,登录成功后session在后台保存了用户会话信息,再次发送请求获取数据时,再次读取session验证用户身份时,始终无法取到登录时存储的会话信息,因为没有获取到cookies,而服务器与浏览器之间的会话sessionID是保存在cookies中的,为什么没有获取到cookies,是因为服务器端允许跨域是这样设置的:

response.setHeader("Access-Control-Allow-Origin", "*");   //允许跨域


origin设置成“*”,是不支持文件形式的访问所发送cookies的,需要怎么改呢?改成与本地文件一样的路径形式就成,比如,在我的机器上如果html路径是 file:///D:test.html,我在java端允许跨域就必须设置成

response.setHeader("Access-Control-Allow-Origin", "file://");   //允许跨域


才能获取到cookies,考虑到html打包进ios或android app中的路径可能不同,则将路径设为:

response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));


这点可让我找了很久,“*”居然不行,我到现在也没想通。

本地cookies保存

登录的问题解决了,发送cookies也没有问题了,但是本地文件的cookies保存又出了问题,无法保存!也许有办法只是我没找到。

考虑到不同浏览器之间的差别和ios对cookies的可能支持不太好的问题,决定打算使用用localStorage在本地文件保存一些信息,因为localStorage是html5自带的,不存在不同浏览器之间的差别。

localStorage的使用也比较简单,我在这个项目中也只使用了几个方法。

首先申明一个全局的storage,因为本身用他的目的就是替代cookies保存用户状态,当然是全局的啦

var storage = window.localStorage;


然后保存值

storage.setItem('userid','深蓝浅蓝的天')


取值

storage.getItem('userid');


退出清空storage

storage.clear();


so easy,我很快就搜索全部代码将cookies的使用全部替换掉了(注意搜索路径哦),但是问题到了这里还是没有结束,在电脑上测试完了本地文件的访问都没有了问题,但是用android或ios打包html文件又出现问题了。

post方法可正常跨域,因为它的headers中的origin是有指的,而get方法origin却是是null,而服务器端对于null是不接收跨域发送cookies,所以cookies还是要忍痛替换掉。++

使用token替代跨域发送Cookies

cookies既然有那么多的问题,而且可能在某些地方支持不太好,之后若是需要对app进行功能扩展,第三方授权之类的,也还是要走token,所以索性直接换掉cookies,当然,我在js中是有区分web/ios/android的,所以手机浏览器端仍然延续使用cookies,ios和android打包文件形式则用token。

虽然在服务器代码中session几乎无处不在,但要替换成token也不会特别麻烦,将原先保存在session中的数据,使用token作为一个键保存在redis中,然后在用户请求服务器时,拦截请求并取出token,再从redis中取出数据手动赋值到session中就可以了,只是我把原先服务器接收请求并取出cookies中sessionID,从而取出对应session的动作替换了下而已。

简单图示就是:

- 服务器处理cookies

第一次请求服务器



第二次请求服务器



大致过程如上图,用markdown不会画图,下次画个好点的~~

token替换cookie

第一次请求服务器



第二次请求服务器



过程几乎是一样的,只是我在拦截中做了一步处理,以便我不需要大幅度改变原有代码,如下:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
if (session.getAttribute("userId") == null) {
String token = request.getHeader("Authorization");
if (token != null && !token.equals("null")) {
String jsonToken = (String) redisTemplate.opsForValue().get(token);
UserToken userToken = JSON.parseObject(jsonToken, UserToken.class);
if (userToken != null) {
session.setAttribute("userId", userToken.getUserId());
}
}
}
return true;
}


过程大致如此。

第一次写这么长的博文,欢迎指正。

欢迎关注我的个人公众号:逍遥的心。主推程序员写的生活类文章,有兴趣的朋友可以共同探讨下:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息