项目学生:带有Jersey的Web服务客户端

这是Project Student的一部分。 其他职位包括带有Jersey的Webservice Client , 业务层和带有Spring Data的持久性 。

RESTful Web应用程序洋葱的第一层是Web服务客户端。 它可以用来模仿包含AJAX内容的网页,也可以被webapp的编程用户用来模仿。 注意,后者可能包括其他Web应用程序,例如,如果您的内部RESTful服务器被许多创建常规网页的演示服务器包围着。

设计决策

Jersey –我将Jersey库用于REST调用。 我考虑了几种选择,但决定选择Jersey,因为它是轻量级的,不会对开发人员施加太多限制。 相比之下,例如,使用Spring库除了引入其他库外,还可能在EJB3环境中引起问题。

UUID –数据库将使用整数主键,但是Web服务将使用UUID标识值。 这是出于安全考虑–如果攻击者知道帐户ID 1008存在,那么可以肯定地说帐户ID 1007存在。 更重要的是,用户ID 0可能存在,并且具有比普通用户更多的特权。

UUID并非如此-在大多数情况下,知道一个UUID并不能洞悉其他UUID。 这不是100%准确的-一些UUID是由IP地址或时间戳组成的,因此,知识渊博的攻击者可以极大地减少可能的值的范围-但是,随机UUID目前“足够好”。

局限性

我采用的是“实现所需的最少功能”方法,因此初始实现存在许多限制。

身份验证 –没有尝试提供身份验证信息。

加密 –没有尝试加密Web服务调用。

仅CRUD方法 –仅支持基本CRUD方法。

记住–限制是可以的, 但必须清楚地记录在案 。 在最好的情况下,它们将被添加到敏捷积压中。

客户端API

客户端API是基本的CRUD。 我们可以稍后添加功能。

public interface CourseRestClient {/*** Get list of all courses.*/Course[] getAllCourses();/*** Get details for specific course.* @param uuid*/Course getCourse(String uuid);/*** Create specific course.* @param name*/Course createCourse(String name);/*** Update specific course.* @param uuid* @param name*/Course updateCourse(String uuid, String name);/*** Delete course.* @param uuid*/void deleteCourse(String uuid);
}

例外情况

该API包括三个运行时异常。 第一个是RestClientException,它是抽象的运行时异常,它是所有其他异常的基类。

缺少期望值时将引发ObjectNotFoundException。 (实施说明:这是由404状态代码触发的。)此异常包含足够的信息以唯一地标识期望的对象。

public class ObjectNotFoundException extends RestClientException {private static final long serialVersionUID = 1L;private final String resource;private final Class<? extends PersistentObject> objectClass;private final String uuid;public ObjectNotFoundException(final String resource,final Class<? extends PersistentObject> objectClass,final String uuid) {super("object not found: " + resource + "[" + uuid + "]");this.resource = resource;this.objectClass = objectClass;this.uuid = uuid;}public String getResource() {return resource;}public Class<? extends PersistentObject> getObjectClass() {return objectClass;}public String getUuid() {return uuid;}
}

RestClientFailureException是用于意外或未处理状态代码的通用处理程序。

public class RestClientFailureException extends RestClientException {private static final long serialVersionUID = 1L;private final String resource;private final Class<? extends PersistentObject> objectClass;private final String uuid;private final int statusCode;/*** Constructor* * @param resource* @param objectClass* @param uuid* @param response*/public RestClientFailureException(final String resource,final Class<? extends PersistentObject> objectClass,final String uuid, final ClientResponse response) {super("rest client received error: " + resource + "[" + uuid + "]");this.resource = resource;this.objectClass = objectClass;this.uuid = uuid;this.statusCode = response.getStatus();}public String getResource() {return resource;}public Class<? extends PersistentObject> getObjectClass() {return objectClass;}/*** Get UUID, "<none>" (during listAllX()) or "(name)" (during createX())* * @return*/public String getUuid() {return uuid;}/*** Get standard HTTP status code.* * @return*/public int getStatusCode() {return statusCode;}
}

添加客户端身份验证后,我们将希望添加UnauthorizedOperationException。

客户实施

基本的CRUD实现通常是样板,因此我们可以使用抽象类来完成大部分繁重的工作。 更高级的功能可能需要此类来直接进行Jersey呼叫。

/*** This is the Course-specific implementation.*/
public class CourseRestClientImpl extends AbstractRestClientImpl<Course>implements CourseRestClient {private static final Course[] EMPTY_COURSE_ARRAY = new Course[0];/*** Constructor.* * @param courseResource*/public CourseRestClientImpl(final String resource) {super(resource, Course.class, Course[].class);}/*** Create JSON string.* * @param name* @return*/String createJson(final String name) {return String.format("{ \"name\": \"%s\" }", name);}/*** @see com.invariantproperties.sandbox.student.webservice.client.CourseRestClient#getAllCourses()*/public Course[] getAllCourses() {return super.getAllObjects(EMPTY_COURSE_ARRAY);}/*** @see com.invariantproperties.sandbox.student.webservice.client.CourseRestClient#getCourse(java.lang.String)*/public Course getCourse(final String uuid) {return super.getObject(uuid);}/*** @see com.invariantproperties.sandbox.student.webservice.client.CourseRestClient#createCourse(java.lang.String)*/public Course createCourse(final String name) {if (name == null || name.isEmpty()) {throw new IllegalArgumentException("'name' is required");}return createObject(createJson(name));}/*** @see com.invariantproperties.sandbox.student.webservice.client.CourseRestClient#updateCourse(java.lang.String,*      java.lang.String)*/public Course updateCourse(final String uuid, final String name) {if (name == null || name.isEmpty()) {throw new IllegalArgumentException("'name' is required");}return super.updateObject(createJson(name), uuid);}/*** @see com.invariantproperties.sandbox.student.webservice.client.CourseRestClient#deleteCourse(java.lang.String)*/public void deleteCourse(final String uuid) {super.deleteObject(uuid);}
}

抽象基类进行繁重的工作。

public class AbstractRestClientImpl<T extends PersistentObject> {private final String resource;private final Class<T> objectClass;private final Class<T[]> objectArrayClass;/*** Constructor.* * @param resource*/public AbstractRestClientImpl(final String resource,final Class<T> objectClass, final Class<T[]> objectArrayClass) {this.resource = resource;this.objectClass = objectClass;this.objectArrayClass = objectArrayClass;}/*** Helper method for testing.* * @return*/Client createClient() {return Client.create();}/*** List all objects. This is a risky method since there's no attempt at* pagination.*/public T[] getAllObjects(final T[] emptyListClass) {final Client client = createClient();try {final WebResource webResource = client.resource(resource);final ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);if (response.getStatus() == Response.Status.OK.getStatusCode()) {T[] entities = response.getEntity(objectArrayClass);return entities;} else {throw new RestClientFailureException(resource, objectClass,"<none>", response);}} finally {client.destroy();}}/*** Get a specific object.*/public T getObject(String uuid) {final Client client = createClient();try {final WebResource webResource = client.resource(resource + uuid);final ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);if (response.getStatus() == Response.Status.OK.getStatusCode()) {final T entity = response.getEntity(objectClass);return entity;} else if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {throw new ObjectNotFoundException(resource, objectClass, uuid);} else {throw new RestClientFailureException(resource, objectClass,uuid, response);}} finally {client.destroy();}}/*** Create an object with the specified values.*/public T createObject(final String json) {final Client client = createClient();try {final WebResource webResource = client.resource(resource);final ClientResponse response = webResource.type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).post(ClientResponse.class, json);if (response.getStatus() == Response.Status.CREATED.getStatusCode()) {final T entity = response.getEntity(objectClass);return entity;} else {throw new RestClientFailureException(resource, objectClass, "("+ json + ")", response);}} finally {client.destroy();}}/*** Update an object with the specified json.*/public T updateObject(final String json, final String uuid) {final Client client = createClient();try {final WebResource webResource = client.resource(resource + uuid);final ClientResponse response = webResource.type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).post(ClientResponse.class, json);if (response.getStatus() == Response.Status.OK.getStatusCode()) {final T entity = response.getEntity(objectClass);return entity;} else if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {throw new ObjectNotFoundException(resource, objectClass, uuid);} else {throw new RestClientFailureException(resource, objectClass,uuid, response);}} finally {client.destroy();}}/*** Delete specified object.*/public void deleteObject(String uuid) {final Client client = createClient();try {final WebResource webResource = client.resource(resource + uuid);final ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON).delete(ClientResponse.class);if (response.getStatus() == Response.Status.GONE.getStatusCode()) {// do nothing} else if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {// do nothing - delete is idempotent} else {throw new RestClientFailureException(resource, objectClass,uuid, response);}} finally {client.destroy();}}
}

单元测试

现在我们来看看我们的测试代码。 重要提示:我们要测试代码的行为,而不是代码的实现。

public class CourseRestClientImplTest {private static final String UUID = "uuid";private static final String NAME = "name";@Testpublic void testGetAllCoursesEmpty() {CourseRestClient client = new CourseRestClientMock(200, new Course[0]);Course[] results = client.getAllCourses();assertEquals(0, results.length);}@Testpublic void testGetAllCoursesNonEmpty() {Course course = new Course();course.setUuid(UUID);CourseRestClient client = new CourseRestClientMock(200,new Course[] { course });Course[] results = client.getAllCourses();assertEquals(1, results.length);}@Test(expected = RestClientFailureException.class)public void testGetAllCoursesError() {CourseRestClient client = new CourseRestClientMock(500, null);client.getAllCourses();}@Testpublic void testGetCourse() {Course course = new Course();course.setUuid(UUID);CourseRestClient client = new CourseRestClientMock(200, course);Course results = client.getCourse(course.getUuid());assertEquals(course.getUuid(), results.getUuid());}@Test(expected = ObjectNotFoundException.class)public void testGetCourseMissing() {CourseRestClient client = new CourseRestClientMock(404, null);client.getCourse(UUID);}@Test(expected = RestClientFailureException.class)public void testGetCourseError() {CourseRestClient client = new CourseRestClientMock(500, null);client.getCourse(UUID);}@Testpublic void testCreateCourse() {Course course = new Course();course.setName(NAME);CourseRestClient client = new CourseRestClientMock(Response.Status.CREATED.getStatusCode(), course);Course results = client.createCourse(course.getName());assertEquals(course.getName(), results.getName());}@Test(expected = RestClientFailureException.class)public void testCreateCourseError() {CourseRestClient client = new CourseRestClientMock(500, null);client.createCourse(UUID);}@Testpublic void testUpdateCourse() {Course course = new Course();course.setUuid(UUID);course.setName(NAME);CourseRestClient client = new CourseRestClientMock(200, course);Course results = client.updateCourse(course.getUuid(), course.getName());assertEquals(course.getUuid(), results.getUuid());assertEquals(course.getName(), results.getName());}@Test(expected = ObjectNotFoundException.class)public void testUpdateCourseMissing() {CourseRestClient client = new CourseRestClientMock(404, null);client.updateCourse(UUID, NAME);}@Test(expected = RestClientFailureException.class)public void testUpdateCourseError() {CourseRestClient client = new CourseRestClientMock(500, null);client.updateCourse(UUID, NAME);}@Testpublic void testDeleteCourse() {Course course = new Course();course.setUuid(UUID);CourseRestClient client = new CourseRestClientMock(Response.Status.GONE.getStatusCode(), null);client.deleteCourse(course.getUuid());}@Testpublic void testDeleteCourseMissing() {CourseRestClient client = new CourseRestClientMock(404, null);client.deleteCourse(UUID);}@Test(expected = RestClientFailureException.class)public void testDeleteCourseError() {CourseRestClient client = new CourseRestClientMock(500, null);client.deleteCourse(UUID);}
}

最后,我们需要使用模拟的REST客户端创建一个要测试的对象。 由于Client.createClient是静态方法,因此无法使用依赖项注入,但已将该调用包装在可以覆盖的package-private方法中。 该方法创建一个模拟的客户端,该客户端提供Jersey库中所需的其余值。

class CourseRestClientMock extends CourseRestClientImpl {static final String RESOURCE = "test://rest/course/";private Client client;private WebResource webResource;private WebResource.Builder webResourceBuilder;private ClientResponse response;private final int status;private final Object results;CourseRestClientMock(int status, Object results) {super(RESOURCE);this.status = status;this.results = results;}/*** Override createClient() so it returns mocked object. These expectations* will handle basic CRUD operations, more advanced functionality will* require inspecting JSON payload of POST call.*/Client createClient() {client = Mockito.mock(Client.class);webResource = Mockito.mock(WebResource.class);webResourceBuilder = Mockito.mock(WebResource.Builder.class);response = Mockito.mock(ClientResponse.class);when(client.resource(any(String.class))).thenReturn(webResource);when(webResource.accept(any(String.class))).thenReturn(webResourceBuilder);when(webResource.type(any(String.class))).thenReturn(webResourceBuilder);when(webResourceBuilder.accept(any(String.class))).thenReturn(webResourceBuilder);when(webResourceBuilder.type(any(String.class))).thenReturn(webResourceBuilder);when(webResourceBuilder.get(eq(ClientResponse.class))).thenReturn(response);when(webResourceBuilder.post(eq(ClientResponse.class),any(String.class))).thenReturn(response);when(webResourceBuilder.put(eq(ClientResponse.class),any(String.class))).thenReturn(response);when(webResourceBuilder.delete(eq(ClientResponse.class))).thenReturn(response);when(response.getStatus()).thenReturn(status);when(response.getEntity(any(Class.class))).thenReturn(results);return client;}
}

整合测试

这是洋葱的最外层,因此没有有意义的集成测试。

源代码

  • 可从http://code.google.com/p/invariant-properties-blog/source/browse/student/student-webservices/student-ws-client获取源代码。

参考: 项目学生:来自Invariant Properties博客的JCG合作伙伴 Bear Giles的泽西Web服务客户端 。

翻译自: https://www.javacodegeeks.com/2013/12/project-student-webservice-client-with-jersey.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/365471.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为摄像机搜索软件_别人的终点华为的起点!用普惠AI守护城市安全

看点&#xff1a;华为好望的求索启示录&#xff01;如何让老百姓用上实惠的AI&#xff1f;你也许不知道&#xff0c;高空抛物已是城市生活中的一大难以治理的安全隐患。一个小小的烟头、水果从高处扔下来&#xff0c;就可能引起严重火灾、人员伤亡、财物破坏&#xff0c;事发后…

在WebGL场景中进行棋盘操作的实验

这篇文章讨论如何在基于Babylon.js的WebGL场景中&#xff0c;建立棋盘状的地块和多个可选择的棋子对象&#xff0c;在点选棋子时显示棋子的移动范围&#xff0c;并且在点击移动范围内的空白地块时向目标地块移动棋子。在这一过程中要考虑不同棋子的移动力和影响范围不同&#x…

em算法python代码_EM算法的python实现的方法步骤

导读热词前言&#xff1a;前一篇文章大概说了EM算法的整个理解以及一些相关的公式神马的&#xff0c;那些数学公式啥的看完真的是忘完了&#xff0c;那就来用代码记忆记忆吧&#xff01;接下来将会对python版本的EM算法进行一些分析。EM的python实现和解析引入问题(双硬币问题)…

第一阶段·Linux运维基础-第2章·Linux系统目录结构介绍

01 变量与PS1 02 添加用户 03 关闭SELinux 04 关闭iptables 05 显示中文乱码排查过程 06 总结 07 目录结构课程内容 08 Linux目录结构特点 09 Linux核心目录简介 10 Linux目录文件之配置文件 11 Linux核心目录文件之DNS及屌丝逃离洗浴中心之路 12 Linux核心目录文件…

使用junit-drools进行JBoss Drools单元测试

最近&#xff0c;我一直在大量使用JBoss Drools进行项目。 我不是Drools专家-我也不太相信这个框架&#xff0c;或者可能不是只相信该项目中的特定用例-我发现很难为基于Drools的业务规则编写简单&#xff0c;可维护的单元测试 。 这就是junit-drools诞生的方式-简单的帮助程序…

scrapy 采集网页出现丢失url的问题

url_list ["http://www.icoat.cc/news/list_18_3.html", "http://www.icoat.cc/news/list_18.html", "http://www.icoat.cc/news/list_18_2.html", ] for ls in url_list:   yield scrapy.Request(urlls, headersheader, callbackself.parseL…

java中重新加载指定文件_java-更改后重新加载属性文件

我将属性文件加载到一个类中,然后在整个应用程序中使用该类来获取它们.public class PropertiesUtil extends PropertyPlaceholderConfigurer {private static Map properties new HashMap();Overrideprotected void loadProperties(final Properties props) throws IOExcepti…

plsql 为空显示 0 的函数_不加班只加薪!从0到1教你制作出入库进销存表格

出入库表应用十分广泛&#xff0c;是每个公司都用到的表格&#xff0c;下面我们来看看怎么从一张空白表一步一步实现《出入库表》的制作&#xff0c;目的是做到只需要记录出库入库流水&#xff0c;自动对库存及累计出入库数量进行计算、实时统计。出入库表构成做一个出入库表&a…

eShopOnContainers学习系列(一):Swagger的使用

最近在看eShopOnContainer项目&#xff0c;抽取一下其中的基础知识点&#xff0c;做个记录&#xff0c;有兴趣的可以看下。 新建一个.net core API项目&#xff0c;添加Nuget包 Swashbuckle.AspNetCore.SwaggerGen、Swashbuckle.AspNetCore.SwaggerUI&#xff1a; 然后在启动文…

结合WebSocket编写WebGL综合场景示例

在WebGL场景中导入多个Babylon骨骼模型&#xff0c;在局域网用WebSocket实现多用户交互控制。 首先是场景截图&#xff1a; 上图在场景中导入一个Babylon骨骼模型&#xff0c;使用asdw、空格、鼠标控制加速度移动&#xff0c;在移动时播放骨骼动画。 上图在场景中加入更多的骨…

awk----基本用法

awk具体的请看这个 https://www.cnblogs.com/bwbfight/p/9402738.html awk 竟然自诩一种语言&#xff0c;ok.... 牛 既然这样就学习一下吧 awk -F‘[指定多个分隔符]’ 比如 awk -F[ :]表示指定&#xff1a;空格为分隔符 涉及多个重复分割符可以这样指定 awk -F[ :] 表示多个空…

java按条件查询结果为空_mybatis中查询结果为空时不同返回类型对应返回值问题...

今天在别人的代码基础上实现新需求&#xff0c;看到对于mybatis查询结果的判断不是很正确&#xff0c;如果查询结果为空就会异常&#xff0c;不知道大家有没有这样的疑惑&#xff1a;mybatis中resultType有多种返回类型&#xff0c;对于每种不同类型&#xff0c;查询结果为空时…

object picker 微信小程序_微信小程序 demo分享

选择器示例demo&#xff1a;1.普通选择器 2.多列选择器 3.时间选择器 4.日期选择器 5.省市区选择器wxml普通选择器&#xff1a;(普通数组)当前选择&#xff1a;{{array[index]}}普通选择器2&#xff1a;(普通json格式数组)当前选择&#xff1a;{{objectArray[objectIndex].name…

项目学生:分片集成测试数据

这是Project Student的一部分。 其他职位包括带有Jersey的 Web服务 客户端&#xff0c;带有Jersey的 Web服务服务器 &#xff0c; 业务层和带有Spring Data的持久性 。 到目前为止&#xff0c;所有集成测试都使用了内存嵌入式数据库&#xff0c;该数据库无法一次又一次地保留信…

BZOJ1036 树的统计(树链剖分+线段树)

【题目描述】 一棵树上有n个节点&#xff0c;编号分别为1到n&#xff0c;每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作&#xff1a; I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问…

Unity插件Gaia使用介绍

零基础创建Unity精美场景地形&#xff08;使用插件Gaia&#xff09;一、先上最终效果图二、软件环境搭建1.Unity5.6.0下载链接https://unity3d.com/cn/get-unity/download/archive?_ga2.110664517.1175563345.1516068066-173539005.15020707552.Gaia Unity地形制作插件下载链…

java http 压缩_解压HTTP API的GZIP压缩数据

1.对Java后端的请求HttpURLConnection对象中的消息头设置压缩connection.setRequestProperty("Accept-Encoding", "gzip, deflate");2.发送请求后获取response中的content-encodingconnection.getContentEncoding(); // 获取content-encoding3.如果content…

20151208_使用windows2012配置weblogic节点管理器

经过实践&#xff0c;weblogic节点管理器的作用主要有两点&#xff1a; 1、可通过weblogic控制台远程控制被管server启停。 2、可以自动重启被管server的进程&#xff0c;并且对spring框架提供比直接启动更快的重启速度。 配置步骤&#xff1a; 在管理电脑上&#xff1a; …

python各种包安装顺序_史上最全的Python包管理工具:Anaconda教程

事实上Anaconda 和 Jupyter notebook已成为数据分析的标准环境。简单来说&#xff0c;Anaconda是包管理器和环境管理器&#xff0c;Jupyter notebook 可以将数据分析的代码、图像和文档全部组合到一个web文档中。接下来我详细介绍下Anaconda&#xff0c;并在最后给出Jupyter no…

在Spring MVC中使用多个属性文件

每个人都听说过将单个Web应用程序组合成一个大型应用程序的门户。 门户软件的工作原理类似于mashup &#xff0d;来自多个来源的内容在单个服务中被拾取&#xff0c;大部分显示在单个网页中。 门户软件还允许在嵌入到门户软件中的所有单个Web应用程序&#xff08;独立模块&…