营销网站建设都是专业技术人员,中山短视频seo教程,做哪些网站流量最大,网站seo优化推广外包jersey spring想要在Java中使用REST#xff1f; 然后您来对地方了#xff0c;因为在博客文章中#xff0c;我将向您介绍如何“美丽”地设计REST API#xff0c;以及如何使用Jersey框架在Java中实现它。 本教程中开发的RESTful API将演示针对存储在MySql数据库中的播客资源的… jersey spring 想要在Java中使用REST 然后您来对地方了因为在博客文章中我将向您介绍如何“美丽”地设计REST API以及如何使用Jersey框架在Java中实现它。 本教程中开发的RESTful API将演示针对存储在MySql数据库中的播客资源的完整的Create__read_update_and_deleteCRUD功能。 1.例子 为什么 在开始之前让我告诉你为什么写这篇文章–好吧我的意图是将来为Podcastpedia.org提供REST API。 当然我可以像现在对AJAX调用那样使用Spring自己的REST实现 但是我还想看看“正式”实现的样子。 因此了解该技术的最佳方法是使用该技术构建原型。 这就是我所做的也是我在这里介绍的我可以说我对泽西岛非常满意。 继续阅读以了解原因 注意您可以使用jQuery和Spring MVC访问我的帖子自动完成搜索框以了解Spring如何处理REST请求。 它有什么作用 本教程中管理的资源是播客。 REST API将允许创建检索更新和删除此类资源。 建筑与技术 该演示应用程序基于“德米特法则LoD或最少知识原理” [16]使用了多层体系结构 第一层是通过Jersey实施的REST支持具有立面的作用并将逻辑委托给业务层 业务层是逻辑发生的地方 数据访问层是与持久性存储在本例中为MySql数据库进行通信的地方 关于所用技术/框架的几句话 1.3.1。 泽西岛门面 Jersey RESTful Web服务框架是开源生产质量的用于在Java中开发RESTful Web服务的框架该框架提供对JAX-RS API的支持并充当JAX-RS JSR 311和JSR 339参考实现。 1.3.2。 Spring业务层 我喜欢将东西与Spring粘在一起这个示例也不例外。 我认为没有更好的方法来制作具有不同功能的POJO。 您将在本教程中找到将Jersey 2与Spring集成所需的内容。 1.3.3。 JPA 2 / Hibernate持久层 对于持久层我仍然使用DAO模式尽管要实现它我使用的是JPA 2正如某些人所说它应该使DAO变得多余我不喜欢EntityManager / JPA专用代码。 JPA 2的AS支持框架我正在使用Hibernate。 有关Java中的持久性主题的有趣讨论请参见我的SpringJPA2和Hibernate的Java持久性示例 。 1.3.4。 网络容器 一切都与Maven打包为.war文件并且可以部署在任何Web容器上–我使用Tomcat和Jetty 但也可以是GlassfihWeblogicJBoss或WebSphere。 1.3.5。 MySQL 示例数据存储在MySQL表中 1.3.6。 技术版本 泽西2.9 Spring4.0.3 Hibernate4 Maven的3 Tomcat7 码头9 MySQL的5.6 注意帖子中的主要焦点将是REST api设计及其通过Jersey JAX-RS实现的实现所有其他技术/层均被视为实现因素。 源代码 此处提供的项目的源代码可在GitHub上获得其中包含有关如何安装和运行项目的完整说明 Codingpedia / Demo-rest-jersey-spring 2.配置 在开始介绍REST API的设计和实现之前我们需要做一些配置以便所有这些奇妙的技术可以一起发挥作用 项目依赖 Jersey Spring扩展名必须出现在项目的类路径中。 如果使用的是Maven请将其添加到项目的pom.xml文件中 pom.xml中的Jersey-spring依赖项 dependencygroupIdorg.glassfish.jersey.ext/groupIdartifactIdjersey-spring3/artifactIdversion${jersey.version}/versionexclusionsexclusiongroupIdorg.springframework/groupIdartifactIdspring-core/artifactId/exclusion exclusiongroupIdorg.springframework/groupIdartifactIdspring-web/artifactId/exclusionexclusiongroupIdorg.springframework/groupIdartifactIdspring-beans/artifactId/exclusion/exclusions
/dependency
dependencygroupIdorg.glassfish.jersey.media/groupIdartifactIdjersey-media-json-jackson/artifactIdversion2.4.1/version
/dependency 注意 jersey-spring3.jar为Spring库使用其自己的版本因此要使用所需的库在本例中为Spring 4.0.3请释放您需要手动排除这些库。 代码警报如果您想查看项目中还需要哪些其他依赖项例如SpringHibernateJetty maven插件测试等则可以查看GitHub上完整的pom.xml文件。 web.xml Web应用程序部署描述符 ?xml version1.0 encodingUTF-8?
web-app version3.0 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_3_0.xsddisplay-nameDemo - Restful Web Application/display-namelistenerlistener-classorg.springframework.web.context.ContextLoaderListener/listener-class/listenercontext-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:spring/applicationContext.xml/param-value/context-paramservletservlet-namejersey-serlvet/servlet-nameservlet-classorg.glassfish.jersey.servlet.ServletContainer/servlet-classinit-paramparam-namejavax.ws.rs.Application/param-nameparam-valueorg.codingpedia.demo.rest.RestDemoJaxRsApplication/param-value /init-param load-on-startup1/load-on-startup/servletservlet-mappingservlet-namejersey-serlvet/servlet-nameurl-pattern/*/url-pattern/servlet-mappingresource-refdescriptionDatabase resource rest demo web application /descriptionres-ref-namejdbc/restDemoDB/res-ref-nameres-typejavax.sql.DataSource/res-typeres-authContainer/res-auth/resource-ref
/web-app泽西小服务器 注意Jersey Servlet配置[行18-33]。 javax.ws.rs.core.Application类定义JAX-RS应用程序的组件根资源和提供程序类。 我使用了ResourceConfig,它是Jersey类Application自己的实现并且提供了高级功能来简化JAX-RS组件的注册。 请查阅文档中的JAX-RS应用程序模型 以了解更多可能性。 我对ResourceConfig类的实现org.codingpedia.demo.rest.RestDemoJaxRsApplication ,注册了应用程序资源过滤器异常映射器和功能 org.codingpedia.demo.rest.service.MyDemoApplication package org.codingpedia.demo.rest.service;//imports omitted for brevity /*** Registers the components to be used by the JAX-RS application* * author ama* */
public class RestDemoJaxRsApplication extends ResourceConfig {/*** Register JAX-RS application components.*/public RestDemoJaxRsApplication() {// register application resourcesregister(PodcastResource.class);register(PodcastLegacyResource.class);// register filtersregister(RequestContextFilter.class);register(LoggingResponseFilter.class);register(CORSResponseFilter.class);// register exception mappersregister(GenericExceptionMapper.class);register(AppExceptionMapper.class);register(NotFoundExceptionMapper.class);// register featuresregister(JacksonFeature.class);register(MultiPartFeature.class);}
} 请注意 org.glassfish.jersey.server.spring.scope.RequestContextFilter 它是一个Spring过滤器提供了JAX-RS和Spring请求属性之间的桥梁 org.codingpedia.demo.rest.resource.PodcastsResource 这是一个“外观”组件它通过注释公开了REST API并将在稍后的文章中进行详尽介绍。 org.glassfish.jersey.jackson.JacksonFeature 它是注册Jackson JSON提供程序的功能-应用程序需要它才能理解JSON数据 2.1.2.2。 Spring应用程序上下文配置 Spring应用程序上下文配置位于spring/applicationContext.xml下的类路径中 Spring应用程序上下文配置 beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/contextxmlns:txhttp://www.springframework.org/schema/tx xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdcontext:component-scan base-packageorg.codingpedia.demo.rest.* /!-- ************ JPA configuration *********** --tx:annotation-driven transaction-managertransactionManager / bean idtransactionManager classorg.springframework.orm.jpa.JpaTransactionManagerproperty nameentityManagerFactory refentityManagerFactory //beanbean idtransactionManagerLegacy classorg.springframework.orm.jpa.JpaTransactionManagerproperty nameentityManagerFactory refentityManagerFactoryLegacy //bean bean identityManagerFactory classorg.springframework.orm.jpa.LocalContainerEntityManagerFactoryBeanproperty namepersistenceXmlLocation valueclasspath:config/persistence-demo.xml /property namepersistenceUnitName valuedemoRestPersistence / property namedataSource refrestDemoDS /property namepackagesToScan valueorg.codingpedia.demo.* /property namejpaVendorAdapterbean classorg.springframework.orm.jpa.vendor.HibernateJpaVendorAdapterproperty nameshowSql valuetrue /property namedatabasePlatform valueorg.hibernate.dialect.MySQLDialect //bean/property/bean bean identityManagerFactoryLegacy classorg.springframework.orm.jpa.LocalContainerEntityManagerFactoryBeanproperty namepersistenceXmlLocation valueclasspath:config/persistence-demo.xml /property namepersistenceUnitName valuedemoRestPersistenceLegacy /property namedataSource refrestDemoLegacyDS /property namepackagesToScan valueorg.codingpedia.demo.* /property namejpaVendorAdapterbean classorg.springframework.orm.jpa.vendor.HibernateJpaVendorAdapterproperty nameshowSql valuetrue /property namedatabasePlatform valueorg.hibernate.dialect.MySQLDialect //bean/property/bean bean idpodcastDao classorg.codingpedia.demo.rest.dao.PodcastDaoJPA2Impl/ bean idpodcastService classorg.codingpedia.demo.rest.service.PodcastServiceDbAccessImpl / bean idpodcastsResource classorg.codingpedia.demo.rest.resource.PodcastsResource /bean idpodcastLegacyResource classorg.codingpedia.demo.rest.resource.PodcastLegacyResource /bean idrestDemoDS classorg.springframework.jndi.JndiObjectFactoryBean scopesingletonproperty namejndiName valuejava:comp/env/jdbc/restDemoDB /property nameresourceRef valuetrue / /beanbean idrestDemoLegacyDS classorg.springframework.jndi.JndiObjectFactoryBean scopesingletonproperty namejndiName valuejava:comp/env/jdbc/restDemoLegacyDB /property nameresourceRef valuetrue / /bean
/beans 这里没什么特别的它只是定义了整个演示应用程序所需的bean例如podcastsResource 这是我们REST API的入口点类。 3. REST API设计和实现 资源资源 3.1.1。 设计 如前所述演示应用程序管理播客播客代表了我们REST API中的资源 。 资源是REST中的中心概念其特点是有两点 每个引用都有一个全局标识符例如HTTP中的URI 。 具有一个或多个表示形式它们公开给外部世界并且可以使用在此示例中我们将主要使用JSON表示形式进行操作 在REST中资源通常用名词播客客户用户帐户等而不是动词getPodcastdeleteUser等表示。 本教程中使用的端点是 /podcasts – 注意复数 URI标识表示播客集合的资源 /podcasts/{id} –通过播客ID标识播客资源的URI 3.1.2。 实作 为了简单起见播客将仅具有以下属性 id –唯一标识播客 feed -播客的URL饲料 title –播客标题 linkOnPodcastpedia –您可以在Podcastpedia.org上找到播客 description -播客的简短描述 我本可以只使用一个Java类来表示代码中的播客资源但是在那种情况下该类及其属性/方法可能会因JPA和XML / JAXB / JSON注释而变得混乱不堪。 我想避免这种情况而是使用了两个具有几乎相同属性的表示形式 PodcastEntity.java –在DB和业务层中使用的JPA注释类 Podcast.java –在正面和业务层中使用的带有 JAXB / JSON注释的类 注意我仍在努力使自己相信这是更好的方法因此如果对此有任何建议请发表评论。 Podcast.java类如下所示 播客 package org.codingpedia.demo.rest.resource;//imports omitted for brevity/*** Podcast resource placeholder for json/xml representation * * author ama**/
SuppressWarnings(restriction)
XmlRootElement
XmlAccessorType(XmlAccessType.FIELD)
public class Podcast implements Serializable {private static final long serialVersionUID -8039686696076337053L;/** id of the podcast */XmlElement(name id) private Long id;/** title of the podcast */XmlElement(name title) private String title;/** link of the podcast on Podcastpedia.org */XmlElement(name linkOnPodcastpedia) private String linkOnPodcastpedia;/** url of the feed */XmlElement(name feed) private String feed;/** description of the podcast */XmlElement(name description)private String description; /** insertion date in the database */XmlElement(name insertionDate)XmlJavaTypeAdapter(DateISO8601Adapter.class) PodcastDetailedViewprivate Date insertionDate;public Podcast(PodcastEntity podcastEntity){try {BeanUtils.copyProperties(this, podcastEntity);} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public Podcast(String title, String linkOnPodcastpedia, String feed,String description) {this.title title;this.linkOnPodcastpedia linkOnPodcastpedia;this.feed feed;this.description description;}public Podcast(){}//getters and setters now shown for brevity
} 并转换为以下JSON表示形式它实际上是当今与REST一起使用的事实上的媒体类型 {id:1,title:Quarks Co - zum Mitnehmen-modified,linkOnPodcastpedia:http://www.podcastpedia.org/podcasts/1/Quarks-Co-zum-Mitnehmen,feed:http://podcast.wdr.de/quarks.xml,description:Quarks Co: Das Wissenschaftsmagazin,insertionDate:2014-05-30T10:26:12.000200
} 即使JSON越来越成为REST API中的首选表示形式您也不应忽略XML表示形式因为大多数系统仍使用XML格式与其他方进行通信。 好消息是在泽西岛您可以一枪杀死两只兔子–使用JAXB bean如上所述您将能够使用相同的Java模型生成JSON和XML表示形式。 另一个优势是使用这种模型的简单性以及Java SE Platform中API的可用性。 注意本教程中定义的大多数方法都将产生和使用application / xml媒体类型其中application / json是首选方式。 方法 在向您介绍API之前让我告诉您 创建 POST 读取获取 更新 PUT 删除删除 并且不是严格的11映射。 为什么 因为您还可以将PUT用于创建将POST用于更新。 在接下来的段落中将对此进行解释和演示。 注意对于“读取和删除”非常清楚它们确实使用GET和DELETE HTTP操作一对一映射。 无论如何REST是一种体系结构风格而不是一种规范您应该使体系结构适应您的需求但是如果您想公开自己的API并希望有人使用它则应该遵循一些“最佳实践”。 如前所述 PodcastRestResource类是处理所有其余请求的类 package org.codingpedia.demo.rest.resource;
//imports
......................
Component
Path(/podcasts)
public class PodcastResource {Autowiredprivate PodcastService podcastService;.....................
} 注意类定义之前的Path(/podcasts) –与播客资源相关的所有内容都将在此路径下发生。 Path批注的值是相对URI路径。 在上面的示例中Java类将托管在URI路径/podcasts 。 PodcastService接口将业务逻辑公开给REST外观层。 代码警报您可以在GitHub – PodcastResource.java上找到该类的全部内容。 我们将逐步介绍该文件并说明与不同操作对应的不同方法。 3.2.1。 创建播客 3.2.1.1。 设计 尽管资源创建的“最著名”方法是使用POST但是如前所述要创建新资源我可以同时使用POST和PUT方法而我做到了 描述 URI HTTP方法 HTTP状态响应 添加新的播客 /播客/ 开机自检 创建了201 添加新的播客必须发送所有值 / podcasts / {id} 放 创建了201 使用POST非幂等之间的最大区别 “ POST方法用于请求源服务器接受请求中包含的实体作为请求行中由Request-URI标识的资源的新下级[…]如果在源服务器上创建了资源响应应为201已创建并包含描述请求状态并引用新资源的实体以及位置标头” [1] 和PUT幂等 “ PUT方法请求将封闭的实体存储在提供的Request-URI […]下如果Request-URI没有指向现有资源并且请求用户代理能够将该URI定义为新资源原始服务器可以使用该URI创建资源。 如果创建了新资源则原始服务器务必通过201已创建响应通知用户代理。” [1] 是对于PUT您应该事先知道将在其中创建资源的位置并发送该条目的所有可能值。 3.2.1.2。 实作 3.2.1.2.1。 使用POST创建单个资源 从JSON创建单个播客资源 /*** Adds a new resource (podcast) from the given json format (at least title* and feed elements are required at the DB level)* * param podcast* return* throws AppException*/
POST
Consumes({ MediaType.APPLICATION_JSON })
Produces({ MediaType.TEXT_HTML })
public Response createPodcast(Podcast podcast) throws AppException {Long createPodcastId podcastService.createPodcast(podcast);return Response.status(Response.Status.CREATED)// 201.entity(A new podcast has been created).header(Location,http://localhost:8888/demo-rest-jersey-spring/podcasts/ String.valueOf(createPodcastId)).build();
} 注解 POST –表示该方法响应HTTP POST请求 Consumes({MediaType.APPLICATION_JSON}) –定义方法接受的媒体类型在这种情况下为application/json Produces({MediaType.TEXT_HTML}) –定义方法可以产生的媒体类型在这种情况下为text/html 。 响应 成功文本/ html文档HTTP状态为201 Created 位置标头指定在何处创建资源 错误时 400 Bad request如果没有提供足够的数据 3.2.1.2.2。 使用PUT创建单个资源“播客” 这将在下面的“更新播客”部分中进行处理。 3.2.1.2.3。 奖励–从表单创建单个资源“播客” 从表单创建单个播客资源 /*** Adds a new podcast (resource) from form (at least title and feed* elements are required at the DB level)* * param title* param linkOnPodcastpedia* param feed* param description* return* throws AppException*/
POST
Consumes({ MediaType.APPLICATION_FORM_URLENCODED })
Produces({ MediaType.TEXT_HTML })
Transactional
public Response createPodcastFromApplicationFormURLencoded(FormParam(title) String title,FormParam(linkOnPodcastpedia) String linkOnPodcastpedia,FormParam(feed) String feed,FormParam(description) String description) throws AppException {Podcast podcast new Podcast(title, linkOnPodcastpedia, feed,description);Long createPodcastid podcastService.createPodcast(podcast);return Response.status(Response.Status.CREATED)// 201.entity(A new podcast/resource has been created at /demo-rest-jersey-spring/podcasts/ createPodcastid).header(Location,http://localhost:8888/demo-rest-jersey-spring/podcasts/ String.valueOf(createPodcastid)).build();
} 注解 POST –表示该方法响应HTTP POST请求 Consumes({MediaType.APPLICATION_FORM_URLENCODED}) –定义该方法接受的媒体类型在这种情况下为application/x-www-form-urlencoded FormParam –在方法的输入参数之前此注释将请求实体主体中包含的表单参数的值绑定到资源方法参数。 除非使用“ Encoded注释”将其禁用否则将对值进行URL解码 Produces({MediaType.TEXT_HTML}) –定义该方法可以产生的媒体类型在这种情况下为“ text / html”。 响应将是状态为201的html文档它向调用方指示请求已得到满足并导致创建了新资源。 响应 成功文本/ html文档HTTP状态为201 Created 位置标头指定在何处创建资源 错误时 400 Bad request如果没有提供足够的数据 3.2.2。 阅读播客 3.2.2.1。 设计 该API支持两种读取操作 返回播客的集合 返回由ID标识的播客 描述 URI HTTP方法 HTTP状态响应 返回所有播客 / podcasts /orderByInsertionDate {ASC | DESC}numberDaysToLookBack {val} 得到 200 OK 添加新的播客必须发送所有值 / podcasts / {id} 得到 200 OK 注意收集资源的查询参数– orderByInsertionDate和numberDaysToLookBack。 将过滤器添加为URI中的查询参数而不是路径的一部分是很有意义的。 3.2.2.2。 实作 3.2.2.2.1。 阅读所有播客“ /” 阅读所有资源 /*** Returns all resources (podcasts) from the database* * return* throws IOException* throws JsonMappingException* throws JsonGenerationException* throws AppException*/
GET
Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public ListPodcast getPodcasts(QueryParam(orderByInsertionDate) String orderByInsertionDate,QueryParam(numberDaysToLookBack) Integer numberDaysToLookBack)throws JsonGenerationException, JsonMappingException, IOException,AppException {ListPodcast podcasts podcastService.getPodcasts(orderByInsertionDate, numberDaysToLookBack);return podcasts;
} 注解 GET –表示该方法响应HTTP GET请求 Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) –定义该方法可以产生的媒体类型在这种情况下为application/json或application/xml 您需要在XmlRootElement前面Podcast类。 响应将是JSON或XML格式的播客列表。 响应 数据库中的播客列表和HTTP状态200 OK 3.2.2.2.1。 阅读一个播客 按ID读取一种资源 GET
Path({id})
Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response getPodcastById(PathParam(id) Long id)throws JsonGenerationException, JsonMappingException, IOException,AppException {Podcast podcastById podcastService.getPodcastById(id);return Response.status(200).entity(podcastById).header(Access-Control-Allow-Headers, X-extra-header).allow(OPTIONS).build();
} 注解 GET –表示该方法响应HTTP GET请求 Path({id}) –标识类方法将为其请求服务的URI路径。 “ id”值是构成URI路径模板的嵌入式变量。 它与PathParam变量结合使用。 PathParam(id) –将URI模板参数“ id”的值绑定到资源方法参数。 Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) –定义方法可以产生的媒体类型在这种情况下为application/json或application/xml 您需要在Podcast前面的XmlRootElement课。 响应 成功请求的播客的HTTP状态为200 OK 。 格式为xml或JSON具体取决于客户端发送的Accept-header值可能押于application / xml或application / json 错误 404 Not found如果数据库中不存在具有给定ID的播客则找不到 3.2.3。 更新播客 3.2.3.1。 设计 描述 URI HTTP方法 HTTP状态响应 更新播客 完整 / podcasts / {id} 放 200 OK 更新播客 部分 / podcasts / {id} 开机自检 200 OK 在REST领域您将进行两种更新 全面更新–您将在其中提供所有 部分更新–当仅一些属性通过网络发送以进行更新时 要进行完整的更新很明显可以使用PUT方法并且符合RFC 2616中该方法的规范。 现在对于部分更新有很多关于使用什么的建议/辩论 通过PUT 通过POST 通过PATCH 让我告诉我为什么我认为第一种选择使用PUT是行不通的。 好吧根据规格 “如果请求URI引用了已经存在的资源则应将封闭的实体视为驻留在源服务器上的实体的修改版本。” [1] 如果我只想更新ID为2的播客的title属性 PUT命令进行部分更新 PUT http://localhost:8888/demo-rest-jersey-spring/podcasts/2 HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/json
Content-Length: 155
Host: localhost:8888
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5){title:New Title
} 然后根据规范“存储”在该位置的资源应仅具有ID和标题显然我的意图不是那样。 通过POST的第二个选项……好吧我们可以“滥用”此选项 这恰恰是我在实现中所做的但是它似乎与我不符因为POST的规范指出 “发布的实体从属于该URI就像文件从属于包含它的目录新闻文章从属于发布它的新闻组或记录从属于数据库一样。” [1 ] 在我看来这似乎不是部分更新案例…… 第三种选择是使用PATCH我想这是该方法成功的主要原因 “几个扩展超文本传输协议HTTP的应用程序 需要功能来进行部分资源修改。 现有的 HTTP PUT方法仅允许完全替换文档。 该提案添加了新的HTTP方法PATCH以修改现有的 HTTP资源。” [2] 我很确定将来会使用它进行部分更新但是由于它还不是规范的一部分并且尚未在Jersey中实现因此我选择在演示中使用POST的第二个选项。 如果您真的想用PATCH在Java中实现部分更新请查看这篇文章– JAX-RS 2.0中的透明PATCH支持 3.2.3.1。 实作 3.2.3.1.1。 完整更新 创建或完全更新资源实现方法 PUT
Path({id})
Consumes({ MediaType.APPLICATION_JSON })
Produces({ MediaType.TEXT_HTML })
public Response putPodcastById(PathParam(id) Long id, Podcast podcast)throws AppException {Podcast podcastById podcastService.verifyPodcastExistenceById(id);if (podcastById null) {// resource not existent yet, and should be created under the// specified URILong createPodcastId podcastService.createPodcast(podcast);return Response.status(Response.Status.CREATED)// 201.entity(A new podcast has been created AT THE LOCATION you specified).header(Location,http://localhost:8888/demo-rest-jersey-spring/podcasts/ String.valueOf(createPodcastId)).build();} else {// resource is existent and a full update should occurpodcastService.updateFullyPodcast(podcast);return Response.status(Response.Status.OK)// 200.entity(The podcast you specified has been fully updated created AT THE LOCATION you specified).header(Location,http://localhost:8888/demo-rest-jersey-spring/podcasts/ String.valueOf(id)).build();}
} 注解 PUT –表示该方法响应HTTP PUT请求 Path({id}) –标识类方法将为其请求服务的URI路径。 “ id”值是构成URI路径模板的嵌入式变量。 它与PathParam变量结合使用。 PathParam(id) –将URI模板参数“ id”的值绑定到资源方法参数。 Consumes({MediaType.APPLICATION_JSON}) –定义方法接受的媒体类型在这种情况下为application/json Produces({MediaType.TEXT_HTML}) –定义方法可以产生的媒体类型在这种情况下为“ text / html”。 将是一个html文档其中包含不同的消息和状态具体取决于已采取的措施 回应 在创作时 成功时 201 Created并且在Location标头中指定了创建资源的指定位置 错误时 400 Bad request如果未提供用于插入的最低要求的属性 完整更新 成功 200 OK 3.2.3.1.2。 部分更新 部分更新 //PARTIAL update
POST
Path({id})
Consumes({ MediaType.APPLICATION_JSON })
Produces({ MediaType.TEXT_HTML })
public Response partialUpdatePodcast(PathParam(id) Long id, Podcast podcast) throws AppException {podcast.setId(id);podcastService.updatePartiallyPodcast(podcast);return Response.status(Response.Status.OK)// 200.entity(The podcast you specified has been successfully updated).build();
} 注解 POST –表示该方法响应HTTP POST请求 Path({id}) –标识类方法将为其请求服务的URI路径。 “ id”值是构成URI路径模板的嵌入式变量。 它与PathParam变量结合使用。 PathParam(id) –将URI模板参数“ id”的值绑定到资源方法参数。 Consumes({MediaType.APPLICATION_JSON}) –定义方法接受的媒体类型在这种情况下为application/json Produces({MediaType.TEXT_HTML}) –定义方法可以产生的媒体类型在这种情况下为text/html 。 响应 成功 200 OK 错误 404 Not Found 如果在提供的位置没有资源可用 3.2.4。 删除播客 3.2.4.1。 设计 描述 URI HTTP方法 HTTP状态响应 删除所有播客 /播客/ 删除 204没有内容 删除指定位置的播客 / podcasts / {id} 删除 204没有内容 3.2.4.2。 实作 3.2.4.2.1。 删除所有资源 删除所有资源 DELETE
Produces({ MediaType.TEXT_HTML })
public Response deletePodcasts() {podcastService.deletePodcasts();return Response.status(Response.Status.NO_CONTENT)// 204.entity(All podcasts have been successfully removed).build();
} 注解 DELETE –表示该方法响应HTTP DELETE请求 Produces({MediaType.TEXT_HTML}) –定义该方法可以产生的媒体类型在这种情况下为“ text / html”。 响应 响应将是一个html文档状态为204 No content向调用方指示请求已完成。 3.2.4.2.2。 删除一项资源 删除一项资源 DELETE
Path({id})
Produces({ MediaType.TEXT_HTML })
public Response deletePodcastById(PathParam(id) Long id) {podcastService.deletePodcastById(id);return Response.status(Response.Status.NO_CONTENT)// 204.entity(Podcast successfully removed from database).build();
} 注解 DELETE –表示该方法响应HTTP DELETE请求 Path({id}) –标识类方法将为其请求服务的URI路径。 “ id”值是构成URI路径模板的嵌入式变量。 它与PathParam变量结合使用。 PathParam(id) –将URI模板参数“ id”的值绑定到资源方法参数。 Produces({MediaType.TEXT_HTML}) –定义该方法可以产生的媒体类型在这种情况下为“ text / html”。 响应 成功时如果删除播客则返回204 No Content成功状态 出现错误播客不再可用并且返回404 Not found状态 4.记录 当日志记录级别设置为DEBUG时将记录每个请求的路径和响应的实体。 在Jetty过滤器的帮助下它像包装器一样具有AOP样式的功能。 有关此问题的更多详细信息请参见我的文章“ 如何使用SLF4J和Logback登录Spring” 。 5.异常处理 如果出现错误我决定使用统一的错误消息结构进行响应。 这是一个错误响应的示例 示例–错误消息响应 {status: 400,code: 400,message: Provided data not sufficient for insertion,link: http://www.codingpedia.org/ama/tutorial-rest-api-design-and-implementation-with-jersey-and-spring,developerMessage: Please verify that the feed is properly generated/set
} 注意请继续关注因为以下文章将提供有关使用Jersey的REST中的错误处理的更多详细信息。 6.在服务器端添加CORS支持 我扩展了为教程开发的API的功能以支持服务器端的跨源资源共享CORS 。 有关此问题的更多详细信息请参阅我的文章“ 如何使用Jersey向Java的服务器端添加CORS支持” 。 7.测试 Java集成测试 为了测试该应用程序我将使用Jersey Client并针对正在运行的Jetty服务器和部署了该应用程序的服务器执行请求。 为此我将使用Maven故障安全插件 。 7.1.1。 组态 7.1.1.1 Jersey客户端依赖性 要构建Jersey客户端必须在类路径中提供jersey-client jar。 使用Maven您可以将其作为依赖项添加到pom.xml文件中 Jersey Client Maven依赖项 dependencygroupIdorg.glassfish.jersey.core/groupIdartifactIdjersey-client/artifactIdversion${jersey.version}/versionscopetest/scope
/dependency 7.1.1.2。 故障安全插件 Failsafe插件用于构建生命周期的集成测试和验证阶段以执行应用程序的集成测试。 Failsafe插件在集成测试阶段不会使构建失败从而使集成后测试阶段能够执行。 要使用故障安全插件您需要将以下配置添加到pom.xml Maven故障安全插件配置 plugins[...]plugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-failsafe-plugin/artifactIdversion2.16/versionexecutionsexecutionidintegration-test/idgoalsgoalintegration-test/goal/goals/executionexecutionidverify/idgoalsgoalverify/goal/goals/execution/executions/plugin[...]
/plugins 7.1.1.2。 Jetty Maven插件 集成测试将针对正在运行的码头服务器执行该服务器仅在执行测试时启动。 为此您必须在jetty-maven-plugin配置以下执行 用于集成测试的Jetty Maven插件配置 pluginsplugingroupIdorg.eclipse.jetty/groupIdartifactIdjetty-maven-plugin/artifactIdversion${jetty.version}/versionconfigurationjettyConfig${project.basedir}/src/main/resources/config/jetty9.xml/jettyConfigstopKeySTOP/stopKeystopPort9999/stopPortstopWait5/stopWaitscanIntervalSeconds5/scanIntervalSeconds[...]/configurationexecutionsexecutionidstart-jetty/idphasepre-integration-test/phasegoals!-- stop any previous instance to free up the port --goalstop/goal goalrun-exploded/goal/goalsconfigurationscanIntervalSeconds0/scanIntervalSecondsdaemontrue/daemon/configuration/executionexecutionidstop-jetty/idphasepost-integration-test/phasegoalsgoalstop/goal/goals/execution/executions/plugin[...]
/plugins 注意在pre-integration-test阶段在停止任何正在运行的实例以释放端口之后将启动Jetty服务器在post-integration-phase 它将停止。 必须将scanIntervalSeconds设置为0并将daemon为true。 代码警报在GitHub上找到完整的pom.xml文件 7.1.2。 建立整合测试 我正在使用JUnit作为测试框架。 默认情况下故障安全插件将自动包含具有以下通配符模式的所有测试类 **/IT*.java –包括其所有子目录以及以“ IT”开头的所有Java文件名。 **/*IT.java –包括其所有子目录以及所有以“ IT”结尾的java文件名。 **/*ITCase.java –包括其所有子目录以及所有以“ ITCase”结尾的java文件名。 我已经创建了一个测试类RestDemoServiceIT 它将测试读取GET方法但是所有其他过程均应相同 public class RestDemoServiceIT {[....]Testpublic void testGetPodcast() throws JsonGenerationException,JsonMappingException, IOException {ClientConfig clientConfig new ClientConfig();clientConfig.register(JacksonFeature.class);Client client ClientBuilder.newClient(clientConfig);WebTarget webTarget client.target(http://localhost:8888/demo-rest-jersey-spring/podcasts/2);Builder request webTarget.request(MediaType.APPLICATION_JSON);Response response request.get();Assert.assertTrue(response.getStatus() 200);Podcast podcast response.readEntity(Podcast.class);ObjectMapper mapper new ObjectMapper();System.out.print(Received podcast from database *************************** mapper.writerWithDefaultPrettyPrinter().writeValueAsString(podcast));}
} 注意 我也必须为客户端注册JacksonFeature以便可以以JSON格式编组播客响应– response.readEntityPodcast.class 我正在针对端口8888上正在运行的Jetty进行测试–下一节将向您展示如何在所需的端口上启动Jetty 我希望我的要求为200 借助帮助org.codehaus.jackson.map.ObjectMapper我正在以相当格式显示JSON响应 7.1.3。 运行集成测试 可以通过调用构建生命周期的verify阶段来调用故障安全插件。 Maven命令调用集成测试 mvn verify 要在端口8888上启动码头您需要将jetty.port属性设置为8888。在Eclipse中我使用以下配置 从Eclipse运行集成测试 与SoapUI的集成测试 最近在大量使用SoapUI测试基于SOAP的Web服务之后我重新发现了SoapUI 。 使用最新版本在撰写本文时最新版本是5.0.0它提供了很好的功能来测试基于REST的Web服务并且以后的版本应该对此进行改进。 因此除非您开发自己的框架/基础结构来测试REST服务否则为什么不尝试使用SoapUI。 我做到了到目前为止我对结果感到满意因此我决定制作一个视频教程现在您可以在我们的频道的YouTube上找到该视频教程 8.版本控制 有三种主要可能性 网址 “ / v1 / podcasts / {id}” 接受/内容类型标头 application / json; 版本 1 因为我是开发人员而不是RESTafarian 所以我会选择URL选项。 在本例中我在实现方面要做的就是将PodcastResource类上的Path的值批注从修改为 路径中的版本控制 Component
Path(/v1/podcasts)
public class PodcastResource {...} 当然在生产应用程序上您不希望每个资源类都以版本号为前缀而是希望以某种方式通过AOP过滤器对版本进行处理。 也许这样的事情会出现在下面的帖子中…… 以下是一些对事情有更好理解的人们的宝贵资源 [视频] REST JSON API设计–开发人员最佳实践 您的API版本错误这就是为什么我决定通过troyhunt用3种不同的错误方式进行操作 REST服务版本控制 API版本控制的最佳做法 –关于Stackoverflow的有趣讨论 9.总结 好就是这样。 如果您走了这么远我必须向您表示祝贺但是我希望您可以从本教程中了解有关REST的知识例如设计REST API用Java实现REST API测试REST API等。 如果您愿意我将不胜感激如果您通过发表评论或在TwitterGoogle 或Facebook上分享评论来帮助其传播。 谢谢 不要忘记也查看Podcastpedia.org 您肯定会找到有趣的Podcast和情节。 感谢您的支持。 如果您喜欢这篇文章我们将非常感谢您为我们的工作做出的一点贡献 立即向Paypal捐款。 10.资源 源代码 GitHub – Codingpedia / demo-rest-jersey-spring 有关如何安装和运行项目的说明 网络资源 HTTP –超文本传输协议— HTTP / 1.1 – RFC2616 rfc5789 – HTTP的PATCH方法 泽西岛用户指南 HTTP状态码定义 REST – http://en.wikipedia.org/wiki/Representational_State_Transfer CRUD – http://en.wikipedia.org/wiki/Create,_read,_update_and_delete RESTful服务的Java APIJAX-RS 泽西岛-Java中的RESTful Web服务 HTTP PUTPATCH或POST –是部分更新还是完全替换 JAX-RS 2.0中的透明PATCH支持 Maven故障安全插件 Maven故障安全插件用法 SoapUI 5.0今天发布了 SoapUI –使用脚本断言 [视频] REST JSON API设计–开发人员最佳实践 [视频] RESTful API设计–第二版 得墨meter耳定律 Codingpedia相关资源 带有SpringJPA2和Hibernate的Java持久性示例 http://www.codingpedia.org/ama/spring-mybatis-integration-example/ http://www.codingpedia.org/ama/tomcat-jdbc-connection-pool-configuration-for-production-and-development/ http://www.codingpedia.org/ama/error-when-executing-jettyrun-with-jetty-maven-plugin-version-9-java-lang-unsupportedclassversionerror-unsupported-major-minor-version-51-0/ http://www.codingpedia.org/ama/autocomplete-search-box-with-jquery-and-spring-mvc/ 翻译自: https://www.javacodegeeks.com/2014/08/tutorial-rest-api-design-and-implementation-in-java-with-jersey-and-spring.htmljersey spring