南宁网站推广哪家好,网站后续建设,国家建设标准网站,做网站外包的公司好干嘛jersey put 服务这是Project Student的一部分。 其他职位包括带有Jersey的Webservice Client #xff0c; 业务层和带有Spring Data的持久性 。 RESTful Web应用程序洋葱的第二层是Web服务服务器。 它应该是一个薄层#xff0c;用于包装对业务层的调用#xff0c;但不对其自… jersey put 服务 这是Project Student的一部分。 其他职位包括带有Jersey的Webservice Client 业务层和带有Spring Data的持久性 。 RESTful Web应用程序洋葱的第二层是Web服务服务器。 它应该是一个薄层用于包装对业务层的调用但不对其自身进行大量处理。 这篇文章有很多代码但主要是测试类。 设计决策 泽西岛 -我将泽西岛用于REST服务器。 我考虑了替代方案-Spring MVC Netty等但出于与客户相同的原因决定选择Jersey。 它轻巧不会限制开发人员。 依赖注入 –我需要依赖注入这意味着我需要确定一个框架SpringEJB3Guice等。我已经知道我将在持久层中使用Spring Data 因此使用它很容易春天的框架。 我仍然会谨慎地最小化该框架上的任何依赖关系ha以实现最大的灵活性。 局限性 球衣 –我不知道球衣将如何处理高负荷。 这是REST服务器必须是业务层的薄包装的关键原因-如果有必要更改库将相对容易。 用户权限 –没有尝试将对某些方法的访问限制为特定用户或主机。 这应该由业务层来处理而安全性异常将由REST服务器转换为FORBIDDEN状态代码。 泽西REST服务器 REST API是我们早期的设计文档之一。 对于服务器这意味着我们从REST服务器开始而不是从业务层API开始实现该层。 实际上REST服务器在业务层API中定义了必要的方法。 与标准的REST CRUD API有一个小的偏差对象是使用POST而不是PUT创建的因为后者的语义是完全按照提供的方式创建了对象。 我们无法做到这一点–出于安全原因我们从不公开内部ID也不得接受用户定义的UUID。 这意味着我们将违反REST API合同因此我们改用POST。 还有一个小作弊CRUD合同仅需要具有创建或更新对象的能力。 这意味着我们只需给出路径就可以找出所需的操作–我们不需要添加特定的“操作”字段。 随着我们将实现扩展到不仅仅包括CRUD动作这可能会改变。 继续执行代码... Service
Path(/course)
public class CourseResource extends AbstractResource {private static final Logger log Logger.getLogger(CourseResource.class);private static final Course[] EMPTY_COURSE_ARRAY new Course[0];ContextUriInfo uriInfo;ContextRequest request;Resourceprivate CourseService service;/*** Default constructor.*/public CourseResource() {}/*** Unit test constructor.* * param service*/CourseResource(CourseService service) {this.service service;}/*** Get all Courses.* * return*/GETProduces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response findAllCourses() {log.debug(CourseResource: findAllCourses());Response response null;try {ListCourse courses service.findAllCourses();ListCourse results new ArrayListCourse(courses.size());for (Course course : courses) {results.add(scrubCourse(course));}response Response.ok(results.toArray(EMPTY_COURSE_ARRAY)).build();} catch (Exception e) {if (!(e instanceof UnitTestException)) {log.info(unhandled exception, e);}response Response.status(Status.INTERNAL_SERVER_ERROR).build();}return response;}/*** Create a Course.* * param req* return*/POSTConsumes({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response createCourse(Name req) {log.debug(CourseResource: createCourse());final String name req.getName();if ((name null) || name.isEmpty()) {return Response.status(Status.BAD_REQUEST).entity(name is required).build();}Response response null;try {Course course service.createCourse(name);if (course null) {response Response.status(Status.INTERNAL_SERVER_ERROR).build();} else {response Response.created(URI.create(course.getUuid())).entity(scrubCourse(course)).build();}} catch (Exception e) {if (!(e instanceof UnitTestException)) {log.info(unhandled exception, e);}response Response.status(Status.INTERNAL_SERVER_ERROR).build();}return response;}/*** Get a specific Course.* * param uuid* return*/Path(/{courseId})GETProduces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response getCourse(PathParam(courseId) String id) {log.debug(CourseResource: getCourse());Response response null;try {Course course service.findCourseByUuid(id);response Response.ok(scrubCourse(course)).build();} catch (ObjectNotFoundException e) {response Response.status(Status.NOT_FOUND).build();} catch (Exception e) {if (!e instanceof UnitTestException)) {log.info(unhandled exception, e);}response Response.status(Status.INTERNAL_SERVER_ERROR).build();}return response;}/*** Update a Course.* * FIXME: what about uniqueness violations?* * param id* param req* return*/Path(/{courseId})POSTConsumes({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response updateCourse(PathParam(courseId) String id, Name req) {log.debug(CourseResource: updateCourse());final String name req.getName();if ((name null) || name.isEmpty()) {return Response.status(Status.BAD_REQUEST).entity(name is required).build();}Response response null;try {final Course course service.findCourseByUuid(id);final Course updatedCourse service.updateCourse(course, name);response Response.ok(scrubCourse(updatedCourse)).build();} catch (ObjectNotFoundException exception) {response Response.status(Status.NOT_FOUND).build();} catch (Exception e) {if (!(e instanceof UnitTestException)) {log.info(unhandled exception, e);}response Response.status(Status.INTERNAL_SERVER_ERROR).build();}return response;}/*** Delete a Course.* * param id* return*/Path(/{courseId})DELETEpublic Response deleteCourse(PathParam(courseId) String id) {log.debug(CourseResource: deleteCourse());Response response null;try {service.deleteCourse(id);response Response.noContent().build();} catch (ObjectNotFoundException exception) {response Response.noContent().build();} catch (Exception e) {if (!(e instanceof UnitTestException)) {log.info(unhandled exception, e);}response Response.status(Status.INTERNAL_SERVER_ERROR).build();}return response;}
} 该实现告诉我们我们需要三件事 服务APICourseService 请求参数类名称 洗涤器scrubCourse 我没有显示完整的日志记录。 必须清除请求参数以避免日志污染。 。 作为一个简单的示例请考虑使用写入SQL数据库的记录器以简化分析。 这个记录器的一个简单的实现-不使用位置参数-将允许通过精心设计的请求参数进行SQL注入 OWASP ESAPI包含可用于日志清理的方法。 我没有包含在这里因为设置起来有些麻烦。 应该很快就会在签入代码中。 为什么要登录到数据库 一种好的做法是记录到达服务器层的所有未处理的异常-您永远都不想依靠用户来报告问题而且写入日志文件的错误很容易被忽略。 相反使用简单工具可以轻松检查写入数据库的报告。 发生未处理的异常时高级开发人员甚至可以创建新的错误报告。 在这种情况下至关重要的是维护一个单独的异常数据库以避免提交重复的条目和使开发人员不知所措。 数据库可以包含每个异常的详细信息但错误报告系统每个异常类堆栈跟踪仅应有一个错误报告。 服务API CRUD操作的服务API很简单。 public interface CourseService {ListCourse findAllCourses();Course findCourseById(Integer id);Course findCourseByUuid(String uuid);Course createCourse(String name);Course updateCourse(Course course, String name);void deleteCourse(String uuid);
} 该API还包含一个ObjectNotFoundException。 这应该扩展为包括找不到的对象的类型。 public class ObjectNotFoundException extends RuntimeException {private static final long serialVersionUID 1L;private final String uuid;public ObjectNotFoundException(String uuid) {super(object not found: [ uuid ]);this.uuid uuid;}public String getUuid() {return uuid;}
} 如上所述我们最终还需要一个UnauthorizedOperationException。 请求参数 请求参数是封装了POST负载的简单POJO。 XmlRootElement
public class Name {private String name;public String getName() {return name;}public void setName(String name) {this.name name;}
} 学生和教师也需要电子邮件地址。 XmlRootElement
public class NameAndEmailAddress {private String name;private String emailAddress;public String getName() {return name;}public void setName(String name) {this.name name;}public String getEmailAddress() {return emailAddress;}public void setEmailAddress(String emailAddress) {this.emailAddress emailAddress;}
} 最终的应用程序将具有大量的请求参数类。 洗涤塔 洗涤塔具有三个目的。 首先它删除了不应提供给客户端的敏感信息例如内部数据库标识符。 其次它可以防止由于引入集合而导致大量的数据库转储。 例如一个学生应包括当前部分的列表但每个部分都有已注册的学生和教师的列表。 这些学生和教师中的每一个都有自己的当前部分列表。 进行泡沫冲洗重复最终将整个数据库转储以响应单个查询。 解决方案是仅包含有关每个可以独立查询的对象的浅层信息。 例如一个学生将拥有当前部分的列表但是这些部分将仅包含UUID和名称。 一条很好的经验法则是清理后的集合应完全包含将在下拉列表和表示表中使用的信息仅此而已。 演示列表可以包含链接或AJAX操作以根据需要提取其他信息。 最后这是执行HTML编码和清理的好地方。 应该清除返回的值以防止跨站点脚本CSS攻击 。 public abstract class AbstractResource {/*** Scrub course object.** FIXME add HTML scrubbing and encoding for string values!*/ public Course scrubCourse(final Course dirty) {final Course clean new Course();clean.setUuid(dirty.getUuid());clean.setName(dirty.getName());// clean.setSelf(resource/ dirty.getUuid());return clean;}
}配置类 我们有两个配置类。 第一个始终由服务器使用第二个仅在集成测试期间由服务器使用。 后者的配置和引用的类位于集成测试源树中。 我更喜欢使用配置类在Spring 3.0中引入因为它们提供了最大的灵活性-例如我可以根据运行应用程序或环境变量的用户有条件地定义bean-并允许我仍然包括标准配置文件。 Configuration
ComponentScan(basePackages { com.invariantproperties.sandbox.student.webservice.server.rest })
ImportResource({ classpath:applicationContext-rest.xml })
// PropertySource(classpath:application.properties)
public class RestApplicationContext {Resourceprivate Environment environment;
} Spring 3.1引入了配置文件。 它们可以工作-但是我正在使用的具有弹簧意识的jersey servlet似乎无法正确设置活动概要文件。 Configuration
//Profile(test)
public class RestApplicationContextTest {BeanStudentService studentService() {return new DummyStudentService();}
}web.xml 现在我们有足够的资源来实现我们的Web服务器。 使用的servlet是启用了spring的Jersey servlet它使用contextClass参数中给出的配置类。 也可以使用配置文件但不能使用配置类和文件的组合。 该Servlet还包含spring.profiles.active的定义。 目的是通过spring 3.1 Profile注释有条件地在RestApplicationContextTest中包含定义但我无法使其正常工作。 我把它留给以后参考。 ?xml version1.0 encodingUTF-8?
web-app version2.5 xmlnshttp://java.sun.com/xml/ns/javaeexmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsddisplay-nameProject Student Webservice/display-namecontext-paramparam-namecontextClass/param-nameparam-valueorg.springframework.web.context.support.AnnotationConfigWebApplicationContext/param-value/context-paramcontext-paramparam-namecontextConfigLocation/param-nameparam-valuecom.invariantproperties.sandbox.student.webservice.server.config.RestApplicationContextcom.invariantproperties.sandbox.student.webservice.server.config.RestApplicationContextTest/param-value/context-paramlistenerlistener-classorg.springframework.web.context.ContextLoaderListener/listener-class/listenerservletservlet-nameREST dispatcher/servlet-nameservlet-classcom.sun.jersey.spi.spring.container.servlet.SpringServlet/servlet-classinit-paramparam-namespring.profiles.active/param-nameparam-valuetest/param-value/init-param/servletservlet-mappingservlet-nameREST dispatcher/servlet-nameurl-pattern/rest/*/url-pattern/servlet-mapping
/web-app单元测试 单元测试很简单。 public class CourseResourceTest {private Course physics new Course();private Course mechanics new Course();Beforepublic void init() {physics.setId(1);physics.setName(physics);physics.setUuid(UUID.randomUUID().toString());mechanics.setId(1);mechanics.setName(mechanics);mechanics.setUuid(UUID.randomUUID().toString());}Testpublic void testFindAllCourses() {final ListCourse expected Arrays.asList(physics);final CourseService service Mockito.mock(CourseService.class);when(service.findAllCourses()).thenReturn(expected);final CourseResource resource new CourseResource(service);final Response response resource.findAllCourses();assertEquals(200, response.getStatus());final Course[] actual (Course[]) response.getEntity();assertEquals(expected.size(), actual.length);assertNull(actual[0].getId());assertEquals(expected.get(0).getName(), actual[0].getName());assertEquals(expected.get(0).getUuid(), actual[0].getUuid());}Testpublic void testFindAllCoursesEmpty() {final ListCourse expected new ArrayList();final CourseService service Mockito.mock(CourseService.class);when(service.findAllCourses()).thenReturn(expected);final CourseResource resource new CourseResource(service);final Response response resource.findAllCourses();assertEquals(200, response.getStatus());final Course[] actual (Course[]) response.getEntity();assertEquals(0, actual.length);}Testpublic void testFindAllCoursesFailure() {final CourseService service Mockito.mock(CourseService.class);when(service.findAllCourses()).thenThrow(new UnitTestException();final CourseResource resource new CourseResource(service);final Response response resource.findAllCourses();assertEquals(500, response.getStatus());}Testpublic void testGetCourse() {final Course expected physics;final CourseService service Mockito.mock(CourseService.class);when(service.findCourseByUuid(expected.getUuid())).thenReturn(expected);final CourseResource resource new CourseResource(service);final Response response resource.getCourse(expected.getUuid());assertEquals(200, response.getStatus());final Course actual (Course) response.getEntity();assertNull(actual.getId());assertEquals(expected.getName(), actual.getName());assertEquals(expected.getUuid(), actual.getUuid());}Testpublic void testGetCourseMissing() {final CourseService service Mockito.mock(CourseService.class);when(service.findCourseByUuid(physics.getUuid())).thenThrow(new ObjectNotFoundException(physics.getUuid()));final CourseResource resource new CourseResource(service);final Response response resource.getCourse(physics.getUuid());assertEquals(404, response.getStatus());}Testpublic void testGetCourseFailure() {final CourseService service Mockito.mock(CourseService.class);when(service.findCourseByUuid(physics.getUuid())).thenThrow(new UnitTestException();final CourseResource resource new CourseResource(service);final Response response resource.getCourse(physics.getUuid());assertEquals(500, response.getStatus());}Testpublic void testCreateCourse() {final Course expected physics;final Name name new Name();name.setName(expected.getName());final CourseService service Mockito.mock(CourseService.class);when(service.createCourse(name.getName())).thenReturn(expected);final CourseResource resource new CourseResource(service);final Response response resource.createCourse(name);assertEquals(201, response.getStatus());final Course actual (Course) response.getEntity();assertNull(actual.getId());assertEquals(expected.getName(), actual.getName());}Testpublic void testCreateCourseBlankName() {final Course expected physics;final Name name new Name();final CourseService service Mockito.mock(CourseService.class);final CourseResource resource new CourseResource(service);final Response response resource.createCourse(name);assertEquals(400, response.getStatus());}/*** Test handling when the course cant be created for some reason. For now* the service layer just returns a null value - it should throw an* appropriate exception.*/Testpublic void testCreateCourseProblem() {final Course expected physics;final Name name new Name();name.setName(expected.getName());final CourseService service Mockito.mock(CourseService.class);when(service.createCourse(name.getName())).thenReturn(null);final CourseResource resource new CourseResource(service);final Response response resource.createCourse(name);assertEquals(500, response.getStatus());}Testpublic void testCreateCourseFailure() {final Course expected physics;final Name name new Name();name.setName(expected.getName());final CourseService service Mockito.mock(CourseService.class);when(service.createCourse(name.getName())).thenThrow(new UnitTestException();final CourseResource resource new CourseResource(service);final Response response resource.createCourse(name);assertEquals(500, response.getStatus());}Testpublic void testUpdateCourse() {final Course expected physics;final Name name new Name();name.setName(mechanics.getName());final Course updated new Course();updated.setId(expected.getId());updated.setName(mechanics.getName());updated.setUuid(expected.getUuid());final CourseService service Mockito.mock(CourseService.class);when(service.findCourseByUuid(expected.getUuid())).thenReturn(expected);when(service.updateCourse(expected, name.getName())).thenReturn(updated);final CourseResource resource new CourseResource(service);final Response response resource.updateCourse(expected.getUuid(),name);assertEquals(200, response.getStatus());final Course actual (Course) response.getEntity();assertNull(actual.getId());assertEquals(mechanics.getName(), actual.getName());assertEquals(expected.getUuid(), actual.getUuid());}/*** Test handling when the course cant be updated for some reason. For now* the service layer just returns a null value - it should throw an* appropriate exception.*/Testpublic void testUpdateCourseProblem() {final Course expected physics;final Name name new Name();name.setName(expected.getName());final CourseService service Mockito.mock(CourseService.class);when(service.updateCourse(expected, name.getName())).thenReturn(null);final CourseResource resource new CourseResource(service);final Response response resource.createCourse(name);assertEquals(500, response.getStatus());}Testpublic void testUpdateCourseFailure() {final Course expected physics;final Name name new Name();name.setName(expected.getName());final CourseService service Mockito.mock(CourseService.class);when(service.updateCourse(expected, name.getName())).thenThrow(new UnitTestException();final CourseResource resource new CourseResource(service);final Response response resource.createCourse(name);assertEquals(500, response.getStatus());}Testpublic void testDeleteCourse() {final Course expected physics;final CourseService service Mockito.mock(CourseService.class);doNothing().when(service).deleteCourse(expected.getUuid());final CourseResource resource new CourseResource(service);final Response response resource.deleteCourse(expected.getUuid());assertEquals(204, response.getStatus());}Testpublic void testDeleteCourseMissing() {final Course expected physics;final Name name new Name();name.setName(expected.getName());final CourseService service Mockito.mock(CourseService.class);doThrow(new ObjectNotFoundException(expected.getUuid())).when(service).deleteCourse(expected.getUuid());final CourseResource resource new CourseResource(service);final Response response resource.deleteCourse(expected.getUuid());assertEquals(204, response.getStatus());}Testpublic void testDeleteCourseFailure() {final Course expected physics;final CourseService service Mockito.mock(CourseService.class);doThrow(new UnitTestException()).when(service).deleteCourse(expected.getUuid());final CourseResource resource new CourseResource(service);final Response response resource.deleteCourse(expected.getUuid());assertEquals(500, response.getStatus());}
}整合测试 问题 REST服务器集成测试是否应该使用实时数据库 答这是一个技巧问题。 我们都需要。 总体架构包含三个Maven模块。 我们前面介绍了student-ws-client今天介绍了Student-ws-server。 每个都创建一个.jar文件。 第三个模块-student-ws-webapp-创建实际的.war文件。 学生-ws-服务器模块的集成测试应使用虚拟服务层而学生-ws-webapp模块的集成测试应使用完整堆栈。 我们从集成测试开始该集成测试反映了客户端模块中的单元测试。 public class CourseRestServerIntegrationTest {CourseRestClient client new CourseRestClientImpl(http://localhost:8080/rest/course/);Testpublic void testGetAll() throws IOException {Course[] courses client.getAllCourses();assertNotNull(courses);}Test(expected ObjectNotFoundException.class)public void testUnknownCourse() throws IOException {client.getCourse(missing);}Testpublic void testLifecycle() throws IOException {final String physicsName Physics 201;final Course expected client.createCourse(physicsName);assertEquals(physicsName, expected.getName());final Course actual1 client.getCourse(expected.getUuid());assertEquals(physicsName, actual1.getName());final Course[] courses client.getAllCourses();assertTrue(courses.length 0);final String mechanicsName Newtonian Mechanics 201;final Course actual2 client.updateCourse(actual1.getUuid(),mechanicsName);assertEquals(mechanicsName, actual2.getName());client.deleteCourse(actual1.getUuid());try {client.getCourse(expected.getUuid());fail(should have thrown exception);} catch (ObjectNotFoundException e) {// do nothing}}
} 我们还需要一个虚拟服务类该类可以实现足以支持我们的集成测试的功能。 public class DummyCourseService implements CourseService {private Map cache Collections.synchronizedMap(new HashMapString, Course());public ListCourse findAllCourses() {return new ArrayList(cache.values());}public Course findCourseById(Integer id) {throw new ObjectNotFoundException(null); }public Course findCourseByUuid(String uuid) {if (!cache.containsKey(uuid)) {throw new ObjectNotFoundException(uuid); }return cache.get(uuid);}public Course createCourse(String name) {Course course new Course();course.setUuid(UUID.randomUUID().toString());course.setName(name);cache.put(course.getUuid(), course);return course;}public Course updateCourse(Course oldCourse, String name) {if (!cache.containsKey(oldCourse.getUuid())) {throw new ObjectNotFoundException(oldCourse.getUuid()); }Course course cache.get(oldCourse.getUuid());course.setUuid(UUID.randomUUID().toString());course.setName(name);return course; }public void deleteCourse(String uuid) {if (cache.containsKey(uuid)) {cache.remove(uuid);}}
}pom.xml pom.xml文件应包含一个用于运行嵌入式码头或tomcat服务器的插件。 作为集成测试的一部分高级用户可以旋转和拆除嵌入式服务器-请参阅更新。 buildplugins!-- Run the application using mvn jetty:run --plugingroupIdorg.mortbay.jetty/groupIdartifactIdmaven-jetty-plugin/artifactIdversion6.1.16/version !-- ancient! --configuration!-- Log to the console. --requestLog implementationorg.mortbay.jetty.NCSARequestLog!-- This doesnt do anything for Jetty, but is a workaround for a Maven bug that prevents the requestLog from being set. --appendtrue/append/requestLogwebAppConfigcontextPath//contextPathextraClasspath${basedir}/target/test-classes//extraClasspath/webAppConfig/configuration/plugin/plugins
/build更新资料 经过更多研究之后我进行了配置以在集成测试期间设置和拆除码头服务器。 此配置使用非标准端口因此我们无需关闭同时运行的另一个码头或tomcat实例即可运行它。 project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdbuildplugins!-- Run the application using mvn jetty:run --plugingroupIdorg.eclipse.jetty/groupIdartifactIdjetty-maven-plugin/artifactIdversion9.1.0.v20131115/versionconfigurationwebAppextraClasspath${basedir}/target/test-classes//extraClasspath/webAppscanIntervalSeconds10/scanIntervalSecondsstopPort18005/stopPortstopKeySTOP/stopKeysystemPropertiessystemPropertynamejetty.port/namevalue18080/value/systemProperty/systemProperties/configurationexecutionsexecutionidstart-jetty/idphasepre-integration-test/phasegoalsgoalrun/goal/goalsconfigurationscanIntervalSeconds0/scanIntervalSecondsdaemontrue/daemon/configuration/executionexecutionidstop-jetty/idphasepost-integration-test/phasegoalsgoalstop/goal/goals/execution/executions/plugin/plugins/build
/project源代码 可从http://code.google.com/p/invariant-properties-blog/source/browse/student/student-webservices/student-ws-server获取源代码。 参考 项目学生来自Invariant Properties博客的JCG合作伙伴 Bear Giles提供的带有Jersey的Webservice Server 。 翻译自: https://www.javacodegeeks.com/2014/01/project-student-webservice-server-with-jersey.htmljersey put 服务