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

jersey客户端

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

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

设计决策

泽西岛 -我使用泽西岛库进行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

jersey客户端

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

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

相关文章

蓝桥杯基础模块3_2:数码管动态显示

一、模块题目 二、原理简述 动态显示的基本原理与实现思路(转载) 动态显示实质上就是轮流点亮单个数码管实现多位数码管整体显示的效果。在轮流显示过程中,每位数码管点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但…

【渝粤教育】电大中专中医基础知识 (2)作业 题库

1.下列表述中属于证的是 A.头痛 B.麻疹 C.风寒犯肺 D.恶寒 E.水痘 正确 正确答案&#xff1a;左边查询 学生答案&#xff1a;C 2.下列表述中属于症的是 A.水肿 B.消渴 C.咳嗽 D.肺痈 E.恶寒 错误 正确答案&#xff1a;左边查询 学生答案&#xff1a;未作答 3.“阳损及阴,阴损及…

Java:使用Toxiproxy模拟各种连接问题

用Toxiproxy和Java的HttpURLConnection模拟各种连接问题&#xff0c;以查看产生了什么样的错误&#xff1a;连接超时vs.读取超时vs.连接被拒绝…。 结果&#xff1a; 系统&#xff1a;openjdk 11.0.1 2018-10-16 (.setConnectTimeout 1) > java.net.SocketTimeoutExceptio…

蓝桥杯基础模块4_1:独立按键

一、模块题目 二、原理简述 1、独立按键的处理思路(转载) 一般情况下,独立按键有两个引脚,其中一个通过上拉电阻接到单片机的I/O端口,另外一端接地。也就是说,平时按键没有动作的时候,输出的是高电平,如果有按下动作发生,则输出的是低电平。那么,我们在程序设计的时…

【渝粤教育】广东开放大学 行政管理 形成性考核 (35)

选择题 题目&#xff1a; ( )是从行政过程的角度来看待行政职能的 选择一项&#xff1a; 答案&#xff1a;看左侧 题目&#xff1a; 经过1993年的机构改革&#xff0c;国务院机构减少到59个&#xff0c;其中部委40个&#xff0c;直属机构13个&#xff0c;办事机构5个和1个办公…

【渝粤教育】广东开放大学 Photoshop 图像处理 形成性考核 (24)

选择题 题目&#xff1a; 使用变换命令中的缩放命令时&#xff0c;按住哪个键可以保证等比例缩放&#xff1f;&#xff08; &#xff09; 答案&#xff1a;看左侧 题目&#xff1a; 将前景色和背景色恢复为默认颜色的快捷键是&#xff08;&#xff09;&#xff1f; 答案&#…

【渝粤教育】广东开放大学 个人与团队管理 形成性考核 (57)

选择题 题目&#xff1a;按照KOLB学习周期&#xff0c;一个完整的学习过程包含四个阶段&#xff0c;不属于这四个阶段的是&#xff08;&#xff09;。 答案&#xff1a;看左侧 题目&#xff1a; 小米的新工作需要录入大量文件&#xff0c;可他对打字不是很在行。他给自己制定了…

junit动态忽略测试_有条件忽略测试的JUnit规则

junit动态忽略测试我一直认为使用Ignore停用测试是一个坏主意。 例外&#xff0c;这可能是一种将间歇性失败的测试放入隔离区以供以后处理的方法&#xff08;如Martin Fowler 在此处所述 &#xff09;。 随着越来越多的测试不断被忽略和遗忘&#xff0c;这带来了测试套件衰减的…

【渝粤教育】广东开放大学 企业标准化 形成性考核 (49)

选择题 题目&#xff1a; 对接受评价部门/单位标准化子体系的建立和实施不能采用随机抽查样本的方式进行评价。 选择一项&#xff1a; 答案&#xff1a;看左侧 题目&#xff1a; 现场确认一般是采用随机抽查&#xff0c;填写记录表和评分表&#xff0c;并通过确认人员的______…

蓝桥杯基础模块4_3:矩阵按键

一、模块题目 二、原理简述 1、矩阵键盘的扫描思想(转载+删减) 与独立按键不同的是,按键的两个引脚都分别连接的单片机的I/O端口,一个作为行信号,另外一个作为列信号。下以4X4的矩阵键盘为例,探讨其工作方式和扫描思路。 要识别出黄色按键的按下状态,逐行扫描,然后读…

【渝粤教育】广东开放大学 刑法 形成性考核 (42)

选择题 题目&#xff1a;以下哪个不是我国刑法的法定原则&#xff1f; 题目&#xff1a;以下哪个管辖权是我国管辖权的基础&#xff1f; 题目&#xff1a;以下哪种情况不适用属地管辖&#xff1f; 题目&#xff1a;犯罪的最本质特征在于它是&#xff08; &#xff09;。 题目&a…

休息一下,或者:如何使用Java 12制作出色的拼图游戏

Java 12以实验形式提供了switch表达式以及switch和break语句的新形式。 对于可能很少用到的构造&#xff0c;有大量新语法和语义&#xff0c;当然&#xff0c;对于那些困惑者和认证考试问题的作者来说&#xff0c;这是一件了不起的礼物。 如果您喜欢Java难题&#xff0c;并且可…

蓝桥杯基础模块5:外部中断

一、模块题目 二、原理简述 (转载,删改) 1、什么是中断 你正在追电视剧《神雕侠侣》,正看得入迷的时候,电话响了,你暂停电视剧,去接电话,在接电话的过程中,门铃又响了,你暂时放下电话,去把门打开。如果追电视剧是在执行主程序,那么电话就是中断源,电话铃响了就是中…

【渝粤教育】广东开放大学 商务谈判 形成性考核 (34)

选择题 题目&#xff1a; 对明智的谈判者而言&#xff0c;应当在保持&#xff08;&#xff09;的基础上&#xff0c;追求自己利益的最大化&#xff0c;在使对方通过谈判获得利益时&#xff0c;自己也获得相应的利益。 选择一项&#xff1a; 答案&#xff1a;看左侧 题目&#…

【渝粤教育】广东开放大学 数据结构 形成性考核 (30)

选择题 题目&#xff1a;链接存储的存储结构所占存储空间&#xff08; &#xff09; 题目&#xff1a;线性表若采用链式存储结构时&#xff0c;要求内存中可用存储单元的地址&#xff08; &#xff09;。 题目&#xff1a;顺序表中第一个元素的存储地址是100&#xff0c;每个元…

蓝桥杯基础模块06_1:定时器计数器

一、模块题目 二、原理简述 &#xff08;转载&#xff0c;删改&#xff09; 1、定时器、计数器定义 在没有钟表的时候&#xff0c;定时的方式通过有一注香的时间&#xff0c;或者一桶水的时间。前者烧香不断减少是减法&#xff0c;后者滴水不断增加是加法。 定时/计数器&#…

structure101_使用structure101分析软件包的依赖关系

structure101稳定应用程序的一个关键是结构良好的代码库。 我们知道我们应该建立尽可能多的黑匣子&#xff0c;因为一旦完成一个黑匣子&#xff0c;我们就不必再考虑其内部了。 您只需要使用您或其他团队成员通过明确定义的界面编写的代码即可。 这使您可以专注于要添加的下一个…

【渝粤教育】广东开放大学 机械制造基础 形成性考核 (55)

选择题 题目&#xff1a;车削加工中大部分切削热传给了&#xff08; &#xff09; 题目&#xff1a;下列诸因素中,哪个是造成45钢淬火硬度偏低的主要原因&#xff08; &#xff09; 题目&#xff1a;切削用量中对切削力影响最大的因素是背吃刀量。 题目&#xff1a;焊接接头中的…

【渝粤教育】广东开放大学 电子商务(本) 形成性考核 (21)

选择题 题目&#xff1a;&#xff08; &#xff09;的域名含义是政府组织。 答案&#xff1a;看左侧 题目&#xff1a;&#xff08; &#xff09;是指包含巨量资料的信息资产&#xff1f; 答案&#xff1a;看左侧 题目&#xff1a;www.sina.com.cn的二级域名是&#xff08; &…