来自 前端知识 2019-12-07 15:32 的文章
当前位置: 金沙澳门官网网址 > 前端知识 > 正文

前后端分离了

后端开荒

自己在这里个示例中,后端选取了spring-boot作为示范,你应当能够非常轻巧将相通的思绪应用到Ruby可能此外语言上。

首先是伏乞的输入,FeedsController会担任分析倡议路线,查数据库,最后回来JSON格式的多寡。

JavaScript

@Controller @RequestMapping("/api") public class FeedsController { @Autowired private FeedsService feedsService; @Autowired private UserService userService; public void setFeedsService(FeedsService feedsService) { this.feedsService = feedsService; } public void setUserService(UserService userService) { this.userService = userService; } @RequestMapping(value="/feeds", method = RequestMethod.GET) @ResponseBody public Iterable<Feed> allFeeds() { return feedsService.allFeeds(); } @RequestMapping(value="/fav-feeds/{userId}", method = RequestMethod.GET) @ResponseBody public Iterable<Feed> favFeeds(@PathVariable("userId") Long userId) { return userService.favoriteFeeds(userId); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Controller
@RequestMapping("/api")
public class FeedsController {
 
    @Autowired
    private FeedsService feedsService;
 
    @Autowired
    private UserService userService;
 
    public void setFeedsService(FeedsService feedsService) {
        this.feedsService = feedsService;
    }
 
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
 
    @RequestMapping(value="/feeds", method = RequestMethod.GET)
    @ResponseBody
    public Iterable<Feed> allFeeds() {
        return feedsService.allFeeds();
    }
 
    @RequestMapping(value="/fav-feeds/{userId}", method = RequestMethod.GET)
    @ResponseBody
    public Iterable<Feed> favFeeds(@PathVariable("userId") Long userId) {
        return userService.favoriteFeeds(userId);
    }
}

切切实实查询的内部景况我们就不做斟酌了,感兴趣的可以在文章结尾处找到代码库的链接。那么有了这些Controller之后,我们什么样测量试验它吗?大概说,怎样让公约变得实在可用呢?

spring-test提供了极度特出的DSL来编排测验,大家仅供给一些代码就可以将左券用起来,并实际上的监督接口的改良:

Java

private MockMvc mockMvc; private FeedsService feedsService; private UserService userService; @Before public void setup() { feedsService = mock(FeedsService.class); userService = mock(UserService.class); FeedsController feedsController = new FeedsController(); feedsController.setFeedsService(feedsService); feedsController.setUserService(userService); mockMvc = standaloneSetup(feedsController).build(); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private MockMvc mockMvc;
private FeedsService feedsService;
private UserService userService;
 
@Before
public void setup() {
    feedsService = mock(FeedsService.class);
    userService = mock(UserService.class);
 
    FeedsController feedsController = new FeedsController();
    feedsController.setFeedsService(feedsService);
    feedsController.setUserService(userService);
 
    mockMvc = standaloneSetup(feedsController).build();
}

创建了mockmvc之后,大家就能够编写Controller的单元测量试验了:

JavaScript

<a href='; public void shouldResponseWithAllFeeds() throws Exception { when(feedsService.allFeeds()).thenReturn(Arrays.asList(prepareFeeds())); mockMvc.perform(get("/api/feeds")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json;charset=UTF-8")) .andExpect(jsonPath("$", hasSize(3))) .andExpect(jsonPath("$[0].publishDate", is(notNullValue()))); }

1
2
3
4
5
6
7
8
9
10
<a href='http://www.jobbole.com/members/madao'>@Test</a>
public void shouldResponseWithAllFeeds() throws Exception {
    when(feedsService.allFeeds()).thenReturn(Arrays.asList(prepareFeeds()));
 
    mockMvc.perform(get("/api/feeds"))
            .andExpect(status().isOk())
            .andExpect(content().contentType("application/json;charset=UTF-8"))
            .andExpect(jsonPath("$", hasSize(3)))
            .andExpect(jsonPath("$[0].publishDate", is(notNullValue())));
}

当发送GET须求到/api/feeds上今后,大家目的在于再次回到状态是200,然后内容是application/json。然后我们预料重临的结果是三个尺寸为3的数组,然后数组中的第叁个成分的publishDate字段不为空。

专一此处的prepareFeeds方法,事实上它会去加载mocks/feeds.json文件 — 也正是前面叁个用来测量检验的mock文件:

JavaScript

private Feed[] prepareFeeds() throws IOException { URL resource = getClass().getResource("/mocks/feeds.json"); ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(resource, Feed[].class); }

1
2
3
4
5
private Feed[] prepareFeeds() throws IOException {
    URL resource = getClass().getResource("/mocks/feeds.json");
    ObjectMapper mapper = new ObjectMapper();
    return mapper.readValue(resource, Feed[].class);
}

那样,当后端修改Feed定义(增加/删除/纠正字段),大概改善了mock数据等,都会促成测验失利;而前面二个纠正mock之后,也会招致测验失利— 不要惊惧失利 — 那样的战败会推进叁次合计,并驱动出最终的service的左券。

对应的,测量检验/api/fav-feeds/{userId}的法门接近:

JavaScript

<a href='; public void shouldResponseWithUsersFavoriteFeeds() throws Exception { when(userService.favoriteFeeds(any(Long.class))) .thenReturn(Arrays.asList(prepareFavoriteFeeds())); mockMvc.perform(get("/api/fav-feeds/1")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json;charset=UTF-8")) .andExpect(jsonPath("$", hasSize(1))) .andExpect(jsonPath("$[0].title", is("使用underscore.js塑造前端选用"卡塔尔国卡塔尔(قطر‎卡塔尔国.andExpect(json帕特h("$[0].publishDate", is(notNullValue()))); }

1
2
3
4
5
6
7
8
9
10
11
12
<a href='http://www.jobbole.com/members/madao'>@Test</a>
public void shouldResponseWithUsersFavoriteFeeds() throws Exception {
    when(userService.favoriteFeeds(any(Long.class)))
        .thenReturn(Arrays.asList(prepareFavoriteFeeds()));
 
    mockMvc.perform(get("/api/fav-feeds/1"))
            .andExpect(status().isOk())
            .andExpect(content().contentType("application/json;charset=UTF-8"))
            .andExpect(jsonPath("$", hasSize(1)))
            .andExpect(jsonPath("$[0].title", is("使用underscore.js构建前端应用")))
            .andExpect(jsonPath("$[0].publishDate", is(notNullValue())));
}

  总结

  前后端分离是大器晚成件轻便的思想政治工作,而且协会也许在长时间能够观察大多功利,可是只要不认真管理集成的题目,抽离反而可能会推动更加长的并轨时间。通过面向左券的艺术来协会各自的测量试验,能够带动众多的平价:更便捷的End2End测量试验,更平整的集成,更安全的辞行开荒等等。

总结

前后端抽离是大器晚成件轻便的业务,並且组织大概在长时间能够见到众多益处,不过即使不认真管理集成的难题,抽离反而或者会带给更加长的并轨时间。通过面向协议的办法来协会各自的测量试验,能够拉动众多的益处:更加高效的End2End测验,更平整的合龙,更安全的分别开荒等等。

  叁个例证

  大家以那一个利用为示范,来注解什么在上下端抽离之后,保险代码的品质,并裁减集成的本钱。那一个应用处景很简短:全部人都得以观看叁个条约列表,各样登录客商都足以采取本人钟爱的条目款项,并为之加星。加星之后的条目款项会保留到客户本人的个人中心中。客户分界面看起来是如此的:

澳门金莎娱乐手机版 1

  然则为了专心在我们的着力上,作者去掉了诸如登录,个人民代表大会旨之类的页面,假使你是三个已登入顾客,然后大家来走访怎么样编写测验。

前端开采

听他们说日常的做法,前后端分离之后,大家比较轻易mock一些多少来协调测验:

XHTML

[ { "id": 1, "url": "", "title": "Python中的 list comprehension 以及 generator", "publicDate": "2015年3月20日" }, { "id": 2, "url": "", "title": "使用inotify/fswatch营造自动监控脚本", "publicDate": "二〇一五年七月1日" }, { "id": 3, "url": "", "title": "使用underscore.js构建前端采纳", "publicDate": "二零一四年四月13日" } ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[
    {
        "id": 1,
        "url": "http://abruzzi.github.com/2015/03/list-comprehension-in-python/",
        "title": "Python中的 list comprehension 以及 generator",
        "publicDate": "2015年3月20日"
    },
    {
        "id": 2,
        "url": "http://abruzzi.github.com/2015/03/build-monitor-script-based-on-inotify/",
        "title": "使用inotify/fswatch构建自动监控脚本",
        "publicDate": "2015年2月1日"
    },
    {
        "id": 3,
        "url": "http://abruzzi.github.com/2015/02/build-sample-application-by-using-underscore-and-jquery/",
        "title": "使用underscore.js构建前端应用",
        "publicDate": "2015年1月20日"
    }
]

接下来,一个大概的格局是通过供给那几个json来测量检验前台:

JavaScript

$(function() { $.get('/mocks/feeds.json').then(function(feeds) { var feedList = new Backbone.Collection(extended); var feedListView = new FeedListView(feedList); $('.container').append(feedListView.render()); }); });

1
2
3
4
5
6
7
8
$(function() {
  $.get('/mocks/feeds.json').then(function(feeds) {
      var feedList = new Backbone.Collection(extended);
      var feedListView = new FeedListView(feedList);
 
      $('.container').append(feedListView.render());
  });
});

那样自然是足以干活的,可是此地发送乞求的url并非终极的,当集成的时候大家又须求改革为真正的url。两个精短的做法是接收Sinatra来做一遍url的调换:

Shell

get '/api/feeds' do content_type 'application/json' File.open('mocks/feeds.json').read end

1
2
3
4
get '/api/feeds' do
  content_type 'application/json'
  File.open('mocks/feeds.json').read
end

如此,当大家和实际的劳动实行归总时,只必要连接到非常服务器就足以了。

留心,大家现在的着力是mocks/feeds.json这一个文件。这么些文件今后的角色就是叁个合同,起码对于前带给讲是如此的。紧接着,大家的采纳须求渲染加星的服从,那就须求其余壹个合同:找寻最近客商加星过的有着不成方圆,因而大家步入了二个新的协议:

XHTML

[ { "id": 3, "url": "", "title": "使用underscore.js创设前端采纳", "publicDate": "2014年八月二十二十一日" } ]

1
2
3
4
5
6
7
8
[
    {
        "id": 3,
        "url": "http://abruzzi.github.com/2015/02/build-sample-application-by-using-underscore-and-jquery/",
        "title": "使用underscore.js构建前端应用",
        "publicDate": "2015年1月20日"
    }
]

接下来在sinatra中投入一个新的映射:

XHTML

get '/api/fav-feeds/:id' do content_type 'application/json' File.open('mocks/fav-feeds.json').read end

1
2
3
4
get '/api/fav-feeds/:id' do
  content_type 'application/json'
  File.open('mocks/fav-feeds.json').read
end

由此那三个央求,咱们会拿走五个列表,然后依照那五个列表的混合来绘制出具有的星号的情况(有的是空心,有的是实心):

JavaScript

$.when(feeds, favorite).then(function(feeds, favorite) { var ids = _.pluck(favorite[0], 'id'); var extended = _.map(feeds[0], function(feed) { return _.extend(feed, {status: _.includes(ids, feed.id)}); }); var feedList = new Backbone.Collection(extended); var feedListView = new FeedListView(feedList); $('.container').append(feedListView.render()); });

1
2
3
4
5
6
7
8
9
10
11
$.when(feeds, favorite).then(function(feeds, favorite) {
    var ids = _.pluck(favorite[0], 'id');
    var extended = _.map(feeds[0], function(feed) {
        return _.extend(feed, {status: _.includes(ids, feed.id)});
    });
 
    var feedList = new Backbone.Collection(extended);
    var feedListView = new FeedListView(feedList);
 
    $('.container').append(feedListView.render());
});

剩余的多少个难点是当点击红心时,我们须要发央求给后端,然后更新红心的景况:

JavaScript

toggleFavorite: function(event) { event.preventDefault(); var that = this; $.post('/api/feeds/'+this.model.get('id')).done(function(){ var status = that.model.get('status'); that.model.set('status', !status); }); }

1
2
3
4
5
6
7
8
toggleFavorite: function(event) {
    event.preventDefault();
    var that = this;
    $.post('/api/feeds/'+this.model.get('id')).done(function(){
        var status = that.model.get('status');
        that.model.set('status', !status);
    });
}

这里又多出来三个倡议,不过使用Sinatra我们照旧得以十分轻便的扶植它:

JavaScript

post '/api/feeds/:id' do end

1
2
post '/api/feeds/:id' do
end

能够看出,在未曾后端的意况下,我们整个都开展顺遂 — 后端以致还不曾初始做,可能正在由一个进度比大家慢的公司在付出,但是不在乎,他们不会影响大家的。

不唯有如此,当我们写完前端的代码之后,能够做多个End2End的测验。由于使用了mock数据,免去了数据库和互联网的耗时,那个End2End的测量试验会运作的超快,而且它的确起到了端到端的功用。这么些测验在终极的合一时,仍为能够用来当UI测量试验来运作。所谓一举多得。

JavaScript

#encoding: utf-8 require 'spec_helper' describe 'Feeds List Page' do let(:list_page) {FeedListPage.new} before do list_page.load end it 'user can see a banner and some feeds' do expect(list_page).to have_banner expect(list_page).to have_feeds end it 'user can see 3 feeds in the list' do expect(list_page.all_feeds).to have_feed_items count: 3 end it 'feed has some detail information' do first = list_page.all_feeds.feed_items.first expect(first.title).to eql("Python中的 list comprehension 以及 generator") end end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#encoding: utf-8
require 'spec_helper'
 
describe 'Feeds List Page' do
  let(:list_page) {FeedListPage.new}
 
  before do
      list_page.load
  end
 
  it 'user can see a banner and some feeds' do
      expect(list_page).to have_banner
      expect(list_page).to have_feeds
  end
 
  it 'user can see 3 feeds in the list' do
      expect(list_page.all_feeds).to have_feed_items count: 3
  end
 
  it 'feed has some detail information' do
      first = list_page.all_feeds.feed_items.first
      expect(first.title).to eql("Python中的 list comprehension 以及 generator")
  end
end

澳门金莎娱乐手机版 2

关于怎么样编写那样的测验,能够参照早前写的那篇随笔。

 前言

前言

左右端分离已然是产业界所共鸣的黄金时代种开垦/安顿格局了。所谓的光景端抽离,并不是历史观行个中的按部门分割,豆蔻梢头部分人纯做前端(HTML/CSS/JavaScript/Flex),另黄金年代局地人纯做后端,因为这种方式是不工作的:比方比比较多团组织接受了后端的沙盘本领(JSP, Free马克尔, ERB等等),前端的付出和调和必要叁个后台Web容器的支撑,进而不恐怕产生真正的分手(更不要提在铺排的时候,由于动态内容和静态内容混在协同,当设计动态静态分流的时候,管理起来非常辛劳)。关于前后端支付的另叁个座谈能够参照这里。

不畏通过API来解耦前端和后端开荒进程,前后端通过RESTFul的接口来通信,前端的静态内容和后端的动态总括分别支付,分别布置,集成反之亦然是一个绕不开的问题— 前端/后端的运用都足以独自的运作,然而集成起来却不干活。大家需求花销多量的精力来调解,直到上线前照例未有人有信念有所的接口都是做事的。

  代码

  前后端的代码小编都放到了Gitbub上,感兴趣的能够clone下来自行钻研:

  1. bookmarks-frontend
  2. bookmarks-server

 

 

上下端剥离了,然后呢?

2015/07/09 · CSS, HTML5, JavaScript · 2 评论 · 前后端

原稿出处: 邱俊涛的博客(@正反反长)   

  一点背景

  一个天下第一的Web应用的构造看起来是如此的:

澳门金莎娱乐手机版 3

澳门金莎娱乐手机版 ,  前后端都分别有和煦的开采流程,营造筑工程具,测量试验会集等等。前后端仅仅通过接口来编制程序,这么些接口或者是JSON格式的RESTFul的接口,也说不准是XML的,重视是后台只担当数据的提供和计量,而浑然不处理表现。而前面二个则承受获得多少,协会数量并显现的办事。那样结构清晰,关怀点抽离,前后端会变得相对独立并松耦合。

  上述的景象依旧比较优良,我们实际在骨子里条件中会有特别复杂的气象,比方异构的网络,异构的操作系统等等:

澳门金莎娱乐手机版 4

  在其实的景色中,后端可能还也许会更目不暇接,比如用C语言做多少搜聚,然后通过Java整合到三个数据货仓,然后该数据旅社又有风姿洒脱层Web Service,最终若干个那样的Web Service又被一个Ruby的聚合Service整合在协同回到给前端。在如此二个繁杂的类别中,后台自便端点的倒闭都或者梗塞前端的支出流程,由此大家会动用mock的艺术来减轻这几个难题:

澳门金莎娱乐手机版 5

  这个mock服务器能够运行多个轻易的HTTP服务器,然后将有个别静态的内容serve出来,以供前端代码应用。那样的收益多多:

  1. 上下端支出相对独立
  2. 后端的速度不会耳濡目染前端开垦
  3. 启船舶的速度度更加快
  4. 内外端都足以应用本人深谙的工夫栈(让前者的学maven,让后端的用gulp都会非常不顺手)

  但是当集成如故是三个令人胸闷的难点。我们反复在合龙的时候才察觉,本来协商的数据布局变了:deliveryAddress字段本来是叁个字符串,以往改为数组了(业务发生了改变,系统往后得以援救多少个快递地址);price字段形成字符串,协商的时候是number;客商邮箱地址多了三个层级等等。这个改变难以避免,何况发生,那会成本多量的调治将养时间和集成时间,更别提修正之后的回归测量检验了。

  所以仅仅使用贰个静态服务器,然后提供mock数码是远远不足的。我们须求的mock应该还可以够到位:

  1. 前端信任钦点格式的mock数据来开展UI开荒
  2. 前面叁个的开销和测量检验都依照那一个mock数据
  3. 后端发生钦定格式的mock数据
  4. 后端要求测量检验来作保生成的mock数据正是前端须求的

  总的来讲,大家要求签订一些合同,并将那几个协议作为能够被测验的中间格式。然后前后端都亟待有测量检验来采用那一个左券。生机勃勃旦契约爆发变化,则另外一方的测量试验会战败,那样就能使得双方协商,并缩短集成时的浪费。

  三个其实的境况是:前端发掘已部分有个别公约中,贫乏了叁个address的字段,于是就在协议中加多了该字段。然后在UI中将那个字段正确的展现了(当然还设置了字体,字号,颜色等等)。可是后台湾学子成该合同的劳动并未感知到那平生成,当运维生成契约部分测量试验(后台)时,测量检验会战败了 — 因为它并未变化这么些字段。于是后端程序猿就找前带给研讨,精晓职业逻辑之后,他会改正代码,并确定保障测量检验通过。这样,当集成的时候,就不会产出UI上少了多少个字段,可是哪个人也不晓得是前面一个难题,后端难点,依然数据库难点等。

  并且事实上的类型中,往往都以八个页面,多少个API,多少个版本,多少个共青团和少先队还要拓张开采,那样的契约会收缩超多的调养时间,使得集成相对平缓。

  在实施中,合同能够定义为三个JSON文件,只怕三个XML的payload。只须要保证前后端分享同二个公约集合来做测量试验,那么集成工作就能从当中收益。叁个最简易的款式是:提供一些静态的mock文件,而前面一个有着发今后台的伏乞都被某种机制拦截,并调换来对该静态财富的倡议。

  1. moco,基于Java
  2. wiremock,基于Java
  3. sinatra,基于Ruby

  看到sinatra被列在此边,或者熟知Ruby的人会反对:它可是一个后端全职能的的程序库啊。之所以列它在这里地,是因为sinatra提供了黄金时代套简洁杰出的DSL,这个DSL卓殊符合Web言语,作者找不到更杰出的法门来驱动这几个mock server一发易读,所以就应用了它。

本文由金沙澳门官网网址发布于前端知识,转载请注明出处:前后端分离了

关键词: