Spring Test, JUnit, Mockito, Hamcrest 集成 Web 测试
关于Spring 3.2
1. Spring 3.2 及以上版本自动开启检测URL后缀,设置Response content-type功能, 如果不手动关闭这个功能,当url后缀与accept头不一致时, Response的content-type将会和request的accept不一致,导致报406
关闭URL后缀检测的方法如下
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
<property name="favorParameter" value="false" />
</bean>
2. Spring-Test框架无法应用关闭Spring自动URL后缀检测的设置, 且StandaloneMockMvcBuilder将设置favorPathExtendsion属性的方法设置为protected
即 关闭自动匹配URL后缀, 忽略Accept头, 自动设置Reponse Content-Type为 URL后缀类型 的配置, 所以如果要使用Spring-Test测试返回类型为JSON的@ResponseBody API, 必须将请求URL后缀改为.json和accept头(application/json)相匹配
一个可行的方案是继承StandaloneMockMvcBuilder, 将其favorPathExtendsion改为false, 这样既可禁用自动匹配URL后缀功能
前言
实际上需要测试一个Spring的MVC controller,主要要做的就是模拟一个真实的Spring的上下文环境, 同时mock出访问这个MVC方法的request, 并通过断言去判断响应及方法内部个方法的调用情况的正确性
需要准备的Maven依赖
<dependencies>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.9</version>
<scope>test</scope>
</dependency> <!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.2.4.RELEASE</version>
</dependency> <!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>3.0.1</version>
</dependency> <dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency> <!-- logger -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency> <!-- test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>hamcrest-core</artifactId>
<groupId>org.hamcrest</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>hamcrest-core</artifactId>
<groupId>org.hamcrest</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency> <!-- validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.0.1.Final</version>
</dependency>
</dependencies>
对转发到页面的Controller方法进行测试
Controller
@Controller
@RequestMapping("/category")
public class CategoryController extends AbstractController { @Resource
CategoryService categoryService; /**
* 课程类目管理页面
*
* @return
*/
@RequestMapping("/manage.htm")
public ModelAndView categoryManage() {
List<Category> categoryList = categoryService.fetchAllCategories();
return new ModelAndView("category/categoryList").addObject(categoryList);
}
}
测试类
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/access-control.xml", "classpath:spring/dao.xml",
"classpath:spring/property.xml", "classpath:spring/service.xml" })
// "file:src/main/webapp/WEB-INF/spring-servlet.xml" })
public class CategoryControllerTest { private MockMvc mockMvc; @Mock
private CategoryService mockCategoryService; @InjectMocks
private CategoryController categoryController; // @Resource
// private WebApplicationContext webApplicationContext; @Before
public void before() throws Exception {
MockitoAnnotations.initMocks(this); // 初始化mock对象
Mockito.reset(mockCategoryService); // 重置mock对象
/*
* 如果要使用完全默认Spring Web Context, 例如不需要对Controller注入,则使用 WebApplicationContext mockMvc =
* MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
*/
// mockMvc = MockMvcBuilders.standaloneSetup(categoryController).build();
mockMvc = QMockMvcBuilders.standaloneSetup(categoryController).build();
} /**
* 课程分类管理测试
*
* @throws Exception
*/
@Test
public void testCategoryManage() throws Exception {
// 构建测试数据
Category c1 = new CategoryBuilder().id(1).name("cat1").build();
Category c2 = new CategoryBuilder().id(2).name("cat2").build(); // 定义方法行为
when(mockCategoryService.fetchAllCategories()).thenReturn(ImmutableList.of(c1, c2)); // 构造http请求及期待响应行为
mockMvc.perform(get("/category/manage.htm"))
.andDo(print()) // 输出请求和响应信息
.andExpect(status().isOk())
.andExpect(view().name("category/categoryList"))
// .andExpect(forwardedUrl("/WEB-INF/jsp/category/categoryList.jsp"))
.andExpect(model().attribute("categoryList", hasSize(2)))
.andExpect(
model().attribute("categoryList",
hasItem(allOf(hasProperty("id", is(1)), hasProperty("name", is("cat1"))))))
.andExpect(
model().attribute("categoryList",
hasItem(allOf(hasProperty("id", is(2)), hasProperty("name", is("cat2")))))); verify(mockCategoryService, times(1)).fetchAllCategories();
verifyNoMoreInteractions(mockCategoryService);
}
}
下面对各变量进行解释
@WebAppConfiguration: 表明该类会使用web应用程序的默认根目录来载入ApplicationContext, 默认的更目录是"src/main/webapp", 如果需要更改这个更目录可以修改该注释的value值
@RunWith: 使用 Spring-Test 框架
@ContextConfiguration(location = ): 指定需要加载的spring配置文件的地址
@Mock: 需要被Mock的对象
@InjectMocks: 需要将Mock对象注入的对象, 此处就是Controller
@Before: 在每次Test方法之前运行的方法
特别需要注意的是, MockMvc就是用来模拟我们的MVC环境的对象, 他负责模拟Spring的MVC设置, 例如对Controller方法的RequestMapping等的扫描, 使用什么ViewResolver等等, 一般我们使用默认配置即可
由于此处我们需要将Controller mock掉, 所以我们不能使用真实的Spring MVC环境, 要使用与原web程序一样的真实的Spring MVC环境, 请使用
MockMvcBuilders.webAppContextSetup(webApplicationContext).build()
此处我们使用自定义的web MVC环境, controller也是自己注入的
// mockMvc = MockMvcBuilders.standaloneSetup(categoryController).build();
mockMvc = QMockMvcBuilders.standaloneSetup(categoryController).build();
注意这里使用的是QMockMvcBuilders, 而不是mockito提供的MockMvcBuilders, 原因就是Spring3.2 默认开启的忽略accept, url后缀匹配自动设置response content-type,这样容易导致406
所以我想把自动关闭后缀匹配, 又由于MockMvcBuilders无法读取spring-mvc的配置文件, 无法关闭该特性, 且MockMvcBuilders提供的关闭该特性(关闭favorPathExtension属性)内部方法居然是protected的,所以我只好继承该类去关闭该特性了
package com.qunar.fresh.exam.web.mockmvc; /**
* @author zhenwei.liu created on 2013 13-10-15 上午1:19
* @version 1.0.0
*/
public class QMockMvcBuilders {
public static StandaloneMockMvcBuilderWithNoPathExtension standaloneSetup(Object... controllers) {
return new StandaloneMockMvcBuilderWithNoPathExtension(controllers);
}
}
package com.qunar.fresh.exam.web.mockmvc; import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder;
import org.springframework.web.accept.ContentNegotiationManagerFactoryBean; /**
* 一个favorPathExtension=false的StandaloneMockMvcBuilder
*
* @author zhenwei.liu created on 2013 13-10-15 上午12:30
* @version 1.0.0
*/
public class StandaloneMockMvcBuilderWithNoPathExtension extends StandaloneMockMvcBuilder { /**
* 重设 ContentNegotiationManager, 关闭自动URL后缀检测
*
* @param controllers 控制器
*/
protected StandaloneMockMvcBuilderWithNoPathExtension(Object... controllers) {
super(controllers);
ContentNegotiationManagerFactoryBean factory = new ContentNegotiationManagerFactoryBean();
factory.setFavorPathExtension(false); // 关闭URL后缀检测
factory.afterPropertiesSet();
setContentNegotiationManager(factory.getObject());
}
}
另外还有个工具类, 和一个用来创建测试数据的builder
package com.qunar.fresh.exam.web.mockmvc; import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder;
import org.springframework.web.accept.ContentNegotiationManagerFactoryBean; /**
* 一个favorPathExtension=false的StandaloneMockMvcBuilder
*
* @author zhenwei.liu created on 2013 13-10-15 上午12:30
* @version 1.0.0
*/
public class StandaloneMockMvcBuilderWithNoPathExtension extends StandaloneMockMvcBuilder { /**
* 重设 ContentNegotiationManager, 关闭自动URL后缀检测
*
* @param controllers 控制器
*/
protected StandaloneMockMvcBuilderWithNoPathExtension(Object... controllers) {
super(controllers);
ContentNegotiationManagerFactoryBean factory = new ContentNegotiationManagerFactoryBean();
factory.setFavorPathExtension(false); // 关闭URL后缀检测
factory.afterPropertiesSet();
setContentNegotiationManager(factory.getObject());
}
}
package com.qunar.fresh.exam.controller.category; import com.qunar.fresh.exam.bean.Category; /**
* 用于创建的Category测试数据
*
* @author zhenwei.liu created on 2013 13-10-14 下午12:00
* @version 1.0.0
*/
public class CategoryBuilder {
private int id;
private String name; public CategoryBuilder id(int id) {
this.id = id;
return this;
} public CategoryBuilder name(String name) {
this.name = name;
return this;
} public Category build() {
return new Category(id, name);
}
}
最后看看返回结果
MockHttpServletRequest:
HTTP Method = GET
Request URI = /category/manage.htm
Parameters = {}
Headers = {} Handler:
Type = com.qunar.fresh.exam.controller.CategoryController
Method = public org.springframework.web.servlet.ModelAndView com.qunar.fresh.exam.controller.CategoryController.categoryManage() Resolved Exception:
Type = null ModelAndView:
View name = category/categoryList
View = null
Attribute = categoryList
value = [com.qunar.fresh.exam.bean.Category@60e390, com.qunar.fresh.exam.bean.Category@fc40ae] FlashMap: MockHttpServletResponse:
Status = 200
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = category/categoryList
Redirected URL = null
Cookies = []
对表单提交方法进行测试
待提交的bean结构和验证内容
/**
* @author zhenwei.liu created on 2013 13-10-15 下午4:19
* @version 1.0.0
*/
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/service.xml")
public class PostControllerTest {
private MockMvc mockMvc; @Mock
private PostService mockPostService; @InjectMocks
private PostController postController; @Before
public void before() {
MockitoAnnotations.initMocks(this);
Mockito.reset(mockPostService);
mockMvc = QMockMvcBuilders.standaloneSetup(postController).build();
} @Test
public void testPostAddWhenTitleExceeds20() throws Exception {
mockMvc.perform(
post("/post/add").contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("title", TestUtil.createStringWithLength(21))
.param("content", "NaN")).andDo(print())
.andExpect(status().isMovedTemporarily())
.andExpect(redirectedUrl("/post/addPage"))
.andExpect(flash().attributeCount(1))
.andExpect(flash().attribute("errMap", hasKey("title")))
.andExpect(flash().attribute("errMap", hasValue("标题长度必须在2至20个字符之间")));
}
}
Controller方法
import java.util.HashMap;
import java.util.Map; import javax.annotation.Resource;
import javax.validation.Valid; import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView; import com.qunar.mvcdemo.bean.Post;
import com.qunar.mvcdemo.service.PostService; /**
* @author zhenwei.liu created on 2013 13-10-12 下午11:51
* @version 1.0.0
*/
@Controller
@RequestMapping("/post")
public class PostController { @Resource
PostService postService; @RequestMapping("/list")
public ModelAndView list() {
ModelAndView mav = new ModelAndView("post/list");
mav.addObject(postService.fetchPosts());
return mav;
} @RequestMapping("/addPage")
public ModelAndView addPage(@ModelAttribute HashMap<String, String> errMap) {
return new ModelAndView("post/add");
} @RequestMapping(value = "/add", method = RequestMethod.POST)
public ModelAndView add(@Valid Post post, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
// 个人认为Spring的错误信息局限性太大,不如自己取出来手动处理
if (bindingResult.hasErrors()) {
Map<String, String> errMap = new HashMap<String, String>();
for (FieldError fe : bindingResult.getFieldErrors()) {
errMap.put(fe.getField(), fe.getDefaultMessage());
}
redirectAttributes.addFlashAttribute("errMap", errMap);
return new ModelAndView(new RedirectView("/post/addPage"));
}
postService.addPost(post);
return new ModelAndView(new RedirectView("/post/list"));
}
}
测试方法
/**
* @author zhenwei.liu created on 2013 13-10-15 下午4:19
* @version 1.0.0
*/
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/service.xml")
public class PostControllerTest {
private MockMvc mockMvc; @Mock
private PostService mockPostService; @InjectMocks
private PostController postController; @Before
public void before() {
MockitoAnnotations.initMocks(this);
Mockito.reset(mockPostService);
mockMvc = QMockMvcBuilders.standaloneSetup(postController).build();
} @Test
public void testPostAddWhenTitleExceeds20() throws Exception {
mockMvc.perform(
post("/post/add").contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("title", TestUtil.createStringWithLength(21))
.param("content", "NaN")).andDo(print())
.andExpect(status().isMovedTemporarily())
.andExpect(redirectedUrl("/post/addPage"))
.andExpect(flash().attributeCount(1))
.andExpect(flash().attribute("errMap", hasKey("title")))
.andExpect(flash().attribute("errMap", hasValue("标题长度必须在2至20个字符之间")));
}
}
注意的点
1. 这个请求链使用了 RedirectAttribute的flashAttribute, flashAttribute的是一个基于Session的临时数据, 他使用session暂时存储, 接收方使用@ModelAttribte 来接受参数使用.
2. 使用了flash().attribute()来判断错误信息是否是期待值
查看输出
MockHttpServletRequest:
HTTP Method = POST
Request URI = /post/add
Parameters = {title=[274864264523756946214], content=[NaN]}
Headers = {Content-Type=[application/x-www-form-urlencoded]} Handler:
Type = com.qunar.mvcdemo.controller.PostController
Method = public org.springframework.web.servlet.ModelAndView com.qunar.mvcdemo.controller.PostController.add(com.qunar.mvcdemo.bean.Post,org.springframework.validation.BindingResult,org.springframework.web.servlet.mvc.support.RedirectAttributes) Async:
Was async started = false
Async result = null Resolved Exception:
Type = null ModelAndView:
View name = null
View = org.springframework.web.servlet.view.RedirectView: unnamed; URL [/post/addPage]
Model = null FlashMap:
Attribute = errMap
value = {title=标题长度必须在2至20个字符之间} MockHttpServletResponse:
Status = 302
Error message = null
Headers = {Location=[/post/addPage]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = /post/addPage
Cookies = []
对REST API测试
Controller接口
/**
* 添加分类
*
* @param category
* @return
*/
@ResponseBody
@RequestMapping(value = "/add.json", method = RequestMethod.POST)
public Object categoryAdd(@RequestBody @Valid Category category) {
if (!loginCheck()) {
return getRedirectView("/loginPage.htm");
} // 检查类目名是否重复
Map<String, Object> params = Maps.newHashMap();
params.put("name", category.getName());
List<Category> test = categoryService.fetchCategories(params);
if (test != null && test.size() != 0) { // 重复类目
return JsonUtils.errorJson("分类名已存在");
} categoryService.addCategory(category);
logService.addLog(session.getAttribute(USERNAME).toString(), LogType.ADD, "新增课程类目: " + category.getName());
return JsonUtils.dataJson("");
}
测试方法
/**
* 添加已存在课程分类测试 期待返回错误信息JSON数据
*
* @throws Exception
*/
@Test
@SuppressWarnings("unchecked")
public void testCategoryAddWhenNameDuplicated() throws Exception {
Category duplicatedCategory = new CategoryBuilder().id(1).name(TestUtil.createStringWithLength(5)).build();
String jsonData = new ObjectMapper().writeValueAsString(duplicatedCategory); when(mockSession.getAttribute(SessionUtil.USERNAME)).thenReturn(TestUtil.createStringWithLength(5));
when(mockCategoryService.fetchCategories(anyMap())).thenReturn(ImmutableList.of(duplicatedCategory)); mockMvc.perform(
post("/category/add.json").contentType(TestUtil.APPLICATION_JSON_UTF8)
.accept(TestUtil.APPLICATION_JSON_UTF8).content(jsonData)).andDo(print())
.andExpect(status().isOk()).andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.ret", is(false))).andExpect(jsonPath("$.errcode", is(1)))
.andExpect(jsonPath("$.errmsg", is("分类名已存在"))); verify(mockSession, times(1)).getAttribute(SessionUtil.USERNAME);
verifyNoMoreInteractions(mockSession);
verify(mockCategoryService, times(1)).fetchCategories(anyMap());
verifyNoMoreInteractions(mockCategoryService);
}
需要注意的是这里需要将请求数据序列化为JSON格式post过去,我们需要设置Accept头和request content-type以及response content-type
最后是验证返回的JSON数据是否符合预期要求,这里使用jsonpath来获取json的特定属性
输出如下
MockHttpServletRequest:
HTTP Method = POST
Request URI = /category/add.json
Parameters = {}
Headers = {Content-Type=[application/json;charset=UTF-8], Accept=[application/json;charset=UTF-8]} Handler:
Type = com.qunar.fresh.exam.controller.CategoryController
Method = public java.lang.Object com.qunar.fresh.exam.controller.CategoryController.categoryAdd(com.qunar.fresh.exam.bean.Category) Resolved Exception:
Type = null ModelAndView:
View name = null
View = null
Model = null FlashMap: MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[application/json;charset=UTF-8]}
Content type = application/json;charset=UTF-8
Body = {"ret":false,"errcode":1,"errmsg":"分类名已存在"}
Forwarded URL = null
Redirected URL = null
Cookies = []
The End
Spring Test, JUnit, Mockito, Hamcrest 集成 Web 测试的更多相关文章
- JUnit+Mockito结合测试Spring MVC Controller
		[本文出自天外归云的博客园] 概要简述 利用JUnit结合Mockito,再加上spingframework自带的一些方法,就可以组合起来对Spring MVC中的Controller层进行测试. 在 ... 
- Spring之junit测试集成
		简介 Spring提供spring-test-5.2.1.RELEASE.jar 可以整合junit. 优势:可以简化测试代码(不需要手动创建上下文,即手动创建spring容器) 使用spring和j ... 
- Spring Boot中采用Mockito来mock所测试的类的依赖(避免加载spring bean,避免启动服务器)
		最近试用了一下Mockito,感觉真的挺方便的.举几个应用实例: 1,需要测试的service中注入的有一个dao,而我并不需要去测试这个dao的逻辑,只需要对service进行测试.这个时候怎么办呢 ... 
- SpringBoot_07_Springboot test 使用mockito进行web测试
		一.前言 使用mockito测试框架可以方便的进行web测试 二.用法实例 package com.ray.weixin.qy.controller; import com.ray.weixin.qy ... 
- Struts2+Spring+Mybatis+Junit 测试
		Struts2+Spring+Mybatis+Junit 测试 博客分类: HtmlUnit Junit Spring 测试 Mybatis package com.action.kioskmoni ... 
- 原创:Spring整合junit测试框架(简易教程 基于myeclipse,不需要麻烦的导包)
		我用的是myeclipse 10,之前一直想要用junit来测试含有spring注解或动态注入的类方法,可是由于在网上找的相关的jar文件进行测试,老是报这样那样的错误,今天无意中发现myeclips ... 
- Spring整合junit测试
		本节内容: Spring整合junit测试的意义 Spring整合junit测试 一.Spring与整合junit测试的意义 在没整合junit之前,我们在写测试方法时,需要在每个方法中手动创建容器, ... 
- Maven聚合、Maven仓库jar包以及Spring+MyBatis+JUnit+Maven整合测试的搭建过程
		一.Maven将父项目创建到父项目的内部 在父项目的pom.xml上 点右键,选择maven-->new-->maven module project 二.Maven聚合 在某个项目的p ... 
- Spring与Junit测试整合
		一.引入spring测试包:text包 二.@RunWith:指定spring对junit提供的一个运行器 @ContextConfiguration: locations指定spring配置文件位 ... 
随机推荐
- javascript模拟flash头像裁切上传
			是的,jq已经有类似的插件了,或者干脆用flash算了,为什么我还要自己写?因为造(wo)轮(bu)子(hui)也(flash)是一个学习的过程,轮子不会造,将来怎么造飞机?先来一张最终效果图: 一. ... 
- SpringMVC中使用Ajax POST请求以json格式传递参数服务端通过request.getParameter("name")无法获取参数值问题分析
			SpringMVC中使用Ajax POST请求以json格式传递参数服务端通过request.getParameter("name")无法获取参数值问题分析 一:问题demo展示 ... 
- MySQL DROP DB或TABLE场景下借助SQL Thread快速应用binlog恢复方案
			[问题] 假设有这种场景,误操作DROP DB或TABLE,常规的恢复操作是还原全备份,并用mysqlbinlog追加到drop操作前的位置. 如果需要恢复的binlog的日志量比较大而我们只希望恢复 ... 
- 使用ApiPost测试接口时需要先登录的接口怎么办(基于Cookie)?
			在后台在开发.调试接口时,常常会遇到需要登陆才能请求的接口. 比如:获取登陆用户的收藏列表,此时,我们就需要模拟登陆状态进行接口调试了.如图: 今天,我们讲解利用ApiPost的环境变量,解决这种需要 ... 
- iOS开发安全 架构
			网络通讯.本地文件和数据.源代码三方面 网络通讯 安全的传输用户密码 客户端在登录时,使用公钥将用户的密码加密后,将密文传输到服务器.服务器使用私钥将密码解密,然后加盐 (Salt:在密码学中,是指通 ... 
- [ 转载 ] Python Web 框架:Django、Flask 与 Tornado 的性能对比
			本文的数据涉及到我面试时遇到过的问题,大概一次 http 请求到收到响应需要多少时间.这个问题在实际工作中与框架有比较大的关系,因此特别就框架的性能做了一次分析. 这里使用 2016 年 6 月 9 ... 
- android的AsyncTask.get()方法会阻塞UI线程
			AsyncTask.get()方法, 是有阻塞UI的能力的. 
- c# RSA 加密解密 java.net公钥私钥转换 要解密的模块大于128字节
			有一个和接口对接的任务,对方使用的是java,我方使用的是c#,接口加密类型为RSA,公钥加密私钥解密. 然后就是解决各种问题. 1.转换对方的密钥字符串 由于c#里面需要使用的是xml各式的密钥字符 ... 
- ant design table column 设置width不生效解决方案
			当td里的内容超出了width的范围时,会出现width不固定,也就是width不生效 解决方案: 设置scroll的width等于所有列宽之和(scroll={{x: 100%}}) 
- spring cloud 学习(4) - hystrix 服务熔断处理
			hystrix 是一个专用于服务熔断处理的开源项目,当依赖的服务方出现故障不可用时,hystrix有一个所谓的断路器,一但打开,就会直接拦截掉对故障服务的调用,从而防止故障进一步扩大(类似中电路中的跳 ... 
