首页 > 技术文章 > 前后端分离开发的一些问题

wqh17 2017-04-18 14:01 原文

1.前言  

  前后台分离式开发,后端提供访问接口,前端使用ajax拉取数据,使用vue绑定数据,各司其职开发更高效,架构高内聚低耦合,扩展维护逻辑更简单,团队协作程度更高.

  相比于以前后端的兄弟处理完后台业务,写完后台接口,还需要拿到前端写的页面,使用一些页面渲染技术,比如el,jstl,freemark等来将后台数据生成html结构再响应给客户端展示,这种开发模式要苦逼很多,比如html结构太复杂,js操作dom结构太多,后端开发是不可能搞清楚的,这时你就要问前台,我应该怎么改你的结构呀?这里啥意思呀?这里的js是特效还是操作数据了呀?还有就是你可能在修改结构的同时可能会错乱或者特效丢失的一些情况,然后这样的话你自己一搬改不了,还要叫前端兄弟,前端兄弟肯定会不耐烦的说:"我给你的时候是好好的,谁知道你怎么改的......" 等等一些页面问题,这是不是很苦逼,我们可是后端开发,干嘛每天要看着繁杂的html,css,js,熟悉那么多无穷尽的结构,这些页面问题本来就应该前端兄弟处理,后端开发就不应该考虑. so,前后端分离开应运而生.

  当然了,使用任何一种新的技术或者架构,都肯定会多多少少出点问题,然后下面总结了几点常见问题以及解决方案.

1. 跨域问题

1.1 何为跨域

  域.指的是一个范围,一个可见或可控的范围.

  web开发中,在同一协议,同一主机,同一端口下的项目便是在同一域下.反之有任何一点不同,则都属于跨域.

  我们由于是前后端分离开发,自然要在两台或两台以上的电脑开发,端口呢也一般是不相同的,后端如果是java的话大多使用tomcat,端口默认为8080,前端的话就不好说了,可能使用webstorm,也可能使用hbuilder端口都是IDE给生成的.所以肯定有跨域问题.

2.1 场景重现

  当服务端提供了接口,我们按正常方式请求时,发生了问题. 

  现有结构如下:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<script src="js/jquery-1.11.1.js"></script>
</head>
<body>
<button onclick="get()">getSid</button>
<script type="text/javascript">
var url = "http://10.2.1.108:8080";

function get(){
$.ajax({
url:url+'/nutzdemo/userAction/login',
dataType:'json',
success:function(data){
console.log(data);
}
});
}
</script>
</body>
</html>

  我们点击按钮,发送http请求拉取数据,抛出如下异常

  

  这个错误遍是跨域问题

1.3 解决方案

1.3.1 前端解决

  见此博文: 点我 点我

1.3.2 后端解决

  由于后端使用的nutz,国产开源的一个非常优雅的框架,而且社区非常有Ji情,nutz为跨域提供了统一解决方案,一个Filter~

  

  将此过滤器添加到mvc主模块上,则应用于所有子模块的入口函数. so easy~

 

2. Cookie跨域失效

1.1 场景重现

  跨域后Cookie也挂了 = =||,虽然跨域问题解决了,但是跨域遗留问题便是Cookie失效,Cookie失效不当紧,或许你会说失效就失效呗,大不了不用Cookie了呗,不就是本地存储嘛,我页面上不用不就得了...然而这也是我们以为的,但殊不知,session是依赖Cookie的,Cookie挂了session也没了....session没了,我们服务器拿什么确定你是谁? 没有session,管你是天王老子,在我这里都是第一次来访问,统一处理...

  session是服务端技术,是解决http协议无状态无会话一大缺点的技术,session的工作方式为当用户第一次请求时,tomcat会为你创建一个Session(Session其实就是一个类,即HttpSession),然后这个session就代表你,既然是代表唯一的你,自然是有一个唯一的标识,so,那就是sessionId,为你生成session后,服务端会将对应你session的sessionid随着响应流来到你的客户端,然后默认是存储到Cookie中,当你再次访问时,浏览器默认将sessionid从Cookie里取出,随着请求到达你的服务器,服务器再从请求中获取sessionid,然后找出上次那个Session,然后它就知道:'哦,是你这个家伙啊',然后再做相关业务处理.

  我后端用的java,如果在我后端服务器的站点上访问 ,浏览器上的 cookie 能存储到了 jsessionid,session 功能无误; 但是如果在前端服务器上的站点,每次在站点部署的前端页面里发送 ajax 请求,后端则会识别不了,每次都认为是一个新的客户端访问

  so,大概意思就是你输入用户名密码,然后登陆成功了,进入主页,购买商品,然后还是会要求你重新登陆,为啥嘞? 因为服务器不认识你呀,尼玛是谁?我跟你熟悉么?滚去登陆去....

1.2 解决方案

  问题原因大概是跨域了,会丢了很多响应头,这些响应头本应该浏览器默认加上的,但不知为何就没加上,然后刚好tomcat是依赖这些响应头才能向客户端传递sessionid并存储到cookie中的,so,既然浏览器不能帮我们,那我们可以手动在服务端搞. 拦截请求有多种方式,比如:Servlet Filter,spring的filter,struts2的拦截器等,在此使用我们nutz提供的过滤器,非常之方便.

package demo.hello.filter;

import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.nutz.mvc.ActionContext;
import org.nutz.mvc.ActionFilter;
import org.nutz.mvc.View;

public class PassHttpFilter implements ActionFilter  {

    @Override
    public View match(ActionContext actionContext) {
            HttpServletResponse res = actionContext.getResponse();
            HttpServletRequest request = actionContext.getRequest();
            res.setContentType("textml;charset=UTF-8");
            res.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
            res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
            res.setHeader("Access-Control-Max-Age", "0");
            res.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");
            res.setHeader("Access-Control-Allow-Credentials", "true");
            res.setHeader("XDomainRequestAllowed","1");
            return null;
    }
    
}

 

   在主模块注解声明此Filter

  

  只在服务端处理还不成,客户端也需要做些事情. 自古便是服务端/浏览器默契协调的搭配,才造就了这样完善的web开发.

    这就要求我们每次请求都需要加上这三个头

       xhrFields: {
                withCredentials: true
            },
            crossDomain: true,

  完整demo:

function send(){
        $.ajax({
            url:url+'/nutzdemo/userAction/index',
            xhrFields: {
                withCredentials: true
            },
            crossDomain: true,
            success:function(data){
                alert("发送完成!");
            },
            error:function(data){
                console.log('error:'+data);
            }
        });
    }

 

 

[完] 转载请联系我,并注明来源.

推荐阅读