Jersey——30分钟速读《Java RESTful Web Service 实战》
一、Jersey入门
1.1 REST简述
REST(Representational State Transfer,表述性状态转移),源于REST之父Roy Thomas Fielding博士在2000年就读加州大学欧文分校期间发表的一篇学术论文——《 Architectural Styles and the Design of Network-based Software Architectures 》。论文中提出了REST的6个特点,分别是:客户端-服务器端模式、无状态的、可缓存的、统一接口的、分层系统和按需编码。
- 客户端-服务器端模式
通信只能由客户端单方面发起,表现为请求-响应的形式。 - 无状态的
对服务器端的请求应该是无状态的,完整、独立的请求不要求服务器在处理请求时检索任何类型的应用程序上下文或状态。无状态约束使服务器的变化对客户端是不可见的,因为在两次连续的请求中,客户端并不依赖于同一台服务器。一个客户端从某台服务器上收到一份包含链接的文档,当它要做一些处理时,这台服务器宕掉了,可能是硬盘坏掉而被拿去修理,可能是软件需要升级重启——如果这个客户端访问了从这台服务器接收的链接,它不会察觉到后台的服务器已经改变了。通过超链接实现有状态交互,即请求消息是自包含的(每次交互都包含完整的信息),有多种技术实现了不同请求间状态信息的传输,例如 URI 重新,cookies 和隐藏表单字段等,状态可以嵌入到应答消息里,这样一来状态在接下来的交互中仍然有效。REST 风格应用可以实现交互,但它却天然地具有服务器无状态的特征。在状态迁移的过程中,服务器不需要记录任何 Session,所有的状态都通过 URI 的形式记录在了客户端。更准确地说,这里的无状态服务器,是指服务器不保存会话状态(Session);而资源本身则是天然的状态,通常是需要被保存的;这里所指无状态服务器均指无会话状态服务器。 - 可缓存的
响应内容可以在通信链的某处被缓存,以改善网络效率。 - 统一接口的
系统中的每一个对象或是资源都可以通过一个唯一的 URI 来进行寻址,URI 的结构应该简单、可预测且易于理解,比如定义目录结构式的 URI。- 若要在服务器上创建资源,应该使用 POST 方法;
- 若要检索某个资源,应该使用 GET 方法;
- 若要更新或者添加资源,应该使用 PUT 方法;
- 若要删除某个资源,应该使用 DELETE 方法。
- 分层系统
通过限制组件的行为(即,每个组件只能“看到”与其交互的紧邻层),将架构分解为若干等级的层。 - 按需编码
服务器能够通过向客户端传输可以执行的逻辑来临时扩展或自定义客户端的功能。 这样的示例可以包括编译的组件,例如Java applet和客户端脚本,例如JavaScript。
1.2 JAX-RS & Jersey
JAX-RS是Java领域的REST式的WEB服务的标准规范。直到Java EE6通过JCP组织的JSR 311才将REST在Java领域标准化。JSR 311,其参考实现是GlassFish项目的Jersey1.0,在时隔5年后JavaEE7包含了JSR 339,即JAX-RS2.0,JAX-RS2.0在前面的版本基础上加上了很多实用功能,比如REST客户端API的定义,异步REST等。
JAX-RS的版本对应参考实现的Jersey版本信息如小表:
JAX-RS标准 | JAX-RS名称 | JAX-RS实现 | JDK版本 |
---|---|---|---|
311 | JAX-RS 1.0 | Jersey1.0 | Java1.6 |
339 | JAX-RS 2.0 | Jersey2.0 | Java1.7 |
1.3 SpringMVC是否为REST实现?
SpringMVC是REST实现,但并没有对JAX-RS标准做实现。
二、Jersey部署
2.1 Springboot + Jersey
2.1.1 Maven依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jersey</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
2.1.2 Springboot启动类
@SpringBootApplication
public class SpringbootApplication {public static void main(String[] args) {SpringApplication.run(SpringbootApplication.class, args);}@BeanResourceConfig resourceConfig() {return new ResourceConfig(HelloResource.class).register(ServiceExceptionMapper.class).register(LogProvider.class);}}
2.1.3 HelloResource
package com.jersey.springboot;import org.glassfish.jersey.server.JSONP;
import org.springframework.stereotype.Component;import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;@Component
@Path("/demo/{id}/hello")
public class HelloResource {@Path("index")@GETpublic Response hello() {return Response.ok("hello world!").build();}@Path("2")@GETpublic String hello2() {return "helloworld";}@Path("3")@GET@HelloResourceBinding@Produces(MediaType.APPLICATION_JSON)public Response hello3() {return Response.ok(new User("kk", "10")).build();}/*** jersey 集成json-p例子** @param callback* @return*/@Path("jsonp")@GET@Produces("application/x-javascript")@JSONP(queryParam = JSONP.DEFAULT_QUERY)public Response jsonp(String callback) {return Response.ok(new User("kk", "10")).build();}@Path("e")@GETpublic Response postException() {throw new ServiceException("哈哈你哈哈哈哈");}public static class User {private String name;private String age;public User(String name, String age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}}
}
2.2 V5集成Jersey介绍
现系统支持JDK6及以上应用容器(Tomcat,WAS,WebLogic),由于未知原因,在我们的系统中事实上存在WAS使用Jersey1.0,而其他使用Jersey2.0的现状,对于这两种集成情况,将分WAS-Jersey1.0和Tomcat-Jersey2.0做讲述。
2.2.1 WAS-Jersey1.0
<servlet><servlet-name>rest</servlet-name><servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> <init-param><param-name>javax.ws.rs.Application</param-name><param-value>com.xx.yy.rest.XXRestApplication</param-value></init-param> <init-param><param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name><param-value>com.xx.yy.rest.filter.ResponseFilter</param-value></init-param><init-param><param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name><param-value>com.XX.yy.rest.filter.AuthorizationRequestFilter</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>rest</servlet-name><url-pattern>/rest/*</url-pattern>
</servlet-mapping>
在XXRestApplication类中扫描各个XXResource类达到注入各个Resource接口的目的,在此需注意,这部分代码只扫描BaseResource的子类,所以开发同学在做接口开发的时候切记切记</font>。
package com.xx.yy.rest;import java.io.IOException;
import java.util.Set;import javax.ws.rs.core.Application;import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.logging.Log;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;import com.google.common.collect.Sets;
import com.xx.yy.common.log.CtpLogFactory;
import com.xx.yy.rest.resources.BaseResource;
import com.xx.yy.rest.resources.RestExceptionMapper;
import com.xx.yy.util.ServerDetector;public class XXRestApplication extends Application {private static final Log LOG = LogFactory.getLog(XXRestApplication.class);private static Set<Class<?>> classes = Sets.newHashSet();static {LOG.info("初始化REST资源..");try {// jersey基础类String basePackage = ServerDetector.isWebSphere() ? "org.codehaus.jackson.jaxrs" : "com.fasterxml.jackson.jaxrs.json";classes.addAll(loadTopLevelClasses(basePackage));// V5基础类XXString XXPackage = "com.xx.yy.rest.resources";Set<Class<?>> set = loadTopLevelClasses(XXPackage);for (Class<?> clazz : set) {if (BaseResource.class.isAssignableFrom(clazz)) {classes.add(clazz);}}classes.add(RestExceptionMapper.class);} catch (IOException e) {LOG.error("初始化REST资源异常:", e);}}@Overridepublic Set<Class<?>> getClasses() {return classes;}private static Set<Class<?>> loadTopLevelClasses(String basePackage) throws IOException {Set<Class<?>> classes = Sets.newHashSet();ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage)) + "/*.class";Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);if (ArrayUtils.isNotEmpty(resources)) {MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);for (Resource resource : resources) {if (resource.isReadable()) {MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);String className = metadataReader.getClassMetadata().getClassName();if (className.indexOf("$") > -1 || className.endsWith("Test") || "package-info".equals(className)) {//不加载内部类continue;}try {Class<?> clazz = Class.forName(className);classes.add(clazz);} catch (ClassNotFoundException e) {LOG.error("类加载异常:", e);}}}}return classes;}
}
使用点评:
在jersey1.0的时候已经支持扫描方式注入Resource了,为什么自己开发注入Resource方式(前期是硬代码注入到XXRestApplication的),对此我表示很费解。如需更多了解Jersey1.0使用可以移步Jersey 1.x - Hello, world!。
2.2.2 Tomcat-Jersey2.0
在这个使用有两处对Jersey做了配置,分别是web.xml和注解类CTPResourceConfig。
- web.xml
<servlet><servlet-name>rest</servlet-name><servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class><init-param><param-name>jersey.config.server.provider.packages</param-name><param-value>com.xx.yy.rest.resources,com.fasterxml.jackson.jaxrs,com.fasterxml.jackson.jaxrs.json,com.fasterxml.jackson.jaxrs.xml,com.xx.yy.rest.filter</param-value></init-param><init-param><param-name>jersey.config.server.provider.classnames</param-name><param-value>com.xx.yy.rest.filter.AuthorizationRequestFilter,com.xx.yy.rest.filter.ResponseFilter,org.glassfish.jersey.media.multipart.MultiPartFeature</param-value></init-param><init-param><param-name>jersey.config.server.provider.scanning.recursive</param-name><param-value>false</param-value></init-param><load-on-startup>4</load-on-startup></servlet><servlet-mapping><servlet-name>rest</servlet-name><url-pattern>/rest/*</url-pattern></servlet-mapping>
- CTPResourceConfig
package com.xx.yy.rest;import javax.ws.rs.ApplicationPath;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.glassfish.jersey.server.ResourceConfig;@ApplicationPath("/rest/*")
public class XXResourceConfig extends ResourceConfig {private final static Log LOG = LogFactory.getLog(XXResourceConfig .class);public CTPResourceConfig() {packages("com.xx.yy.rest.resources");packages("com.fasterxml.jackson.jaxrs");add("com.xx.yy.rest.filter.AuthorizationRequestFilter");add("com.xx.yy.rest.filter.ResponseFilter");add("com.fasterxml.jackson.jaxrs.annotation.JacksonFeatures");add("org.glassfish.jersey.media.multipart.MultiPartFeature");}private void add(String className) {try {register(Class.forName(className));} catch (ClassNotFoundException e) {LOG.error(e.getLocalizedMessage(),e);}}
}
对于一个V5应用中有两个地方配置了Jersey,这就导致了在tomcat/logs/catalina.2019-06-26.log文件中输出了如下日志。
26-Jun-2019 16:40:01.543 警告 [localhost-startStop-1] org.glassfish.jersey.servlet.init.JerseyServletContainerInitializer.addServletWithApplication Mapping conflict. A Servlet registration exists with same mapping as the Jersey servlet application, named com.xx.yy.rest.CTPResourceConfig, at the servlet mapping, /rest/*.
如果在fastjson升级到fastjson-1.2.52后,由于fastJson内部实现了JAX-RS的Provider,导致我们的数据写入和写出使用了fastjson的实现了
2.3 Jersey服务配置加载示意图
2.4 Jersey web.xml配置参数
对于web.xml中配置Jersey的参数请参考jersey-server-xx.jar中类org.glassfish.jersey.server.ServerProperties,具体参数及说明请参看源码doc说明
public final class ServerProperties
{public static final String PROVIDER_PACKAGES = "jersey.config.server.provider.packages";public static final String PROVIDER_SCANNING_RECURSIVE = "jersey.config.server.provider.scanning.recursive";public static final String PROVIDER_CLASSPATH = "jersey.config.server.provider.classpath";public static final String PROVIDER_CLASSNAMES = "jersey.config.server.provider.classnames";public static final String MEDIA_TYPE_MAPPINGS = "jersey.config.server.mediaTypeMappings";public static final String LANGUAGE_MAPPINGS = "jersey.config.server.languageMappings";public static final String HTTP_METHOD_OVERRIDE = "jersey.config.server.httpMethodOverride";public static final String WADL_GENERATOR_CONFIG = "jersey.config.server.wadl.generatorConfig";public static final String WADL_FEATURE_DISABLE = "jersey.config.server.wadl.disableWadl";public static final String BV_FEATURE_DISABLE = "jersey.config.beanValidation.disable.server";public static final String BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK = "jersey.config.beanValidation.disable.validateOnExecutableCheck.server";public static final String BV_SEND_ERROR_IN_RESPONSE = "jersey.config.beanValidation.enableOutputValidationErrorEntity.server";public static final String REDUCE_CONTEXT_PATH_SLASHES_ENABLED = "jersey.config.server.reduceContextPathSlashes.enabled";@PropertyAliaspublic static final String FEATURE_AUTO_DISCOVERY_DISABLE = "jersey.config.server.disableAutoDiscovery";@PropertyAliaspublic static final String OUTBOUND_CONTENT_LENGTH_BUFFER = "jersey.config.server.contentLength.buffer";@PropertyAliaspublic static final String JSON_PROCESSING_FEATURE_DISABLE = "jersey.config.server.disableJsonProcessing";@PropertyAliaspublic static final String METAINF_SERVICES_LOOKUP_DISABLE = "jersey.config.server.disableMetainfServicesLookup";@PropertyAliaspublic static final String MOXY_JSON_FEATURE_DISABLE = "jersey.config.server.disableMoxyJson";public static final String RESOURCE_VALIDATION_DISABLE = "jersey.config.server.resource.validation.disable";public static final String RESOURCE_VALIDATION_IGNORE_ERRORS = "jersey.config.server.resource.validation.ignoreErrors";public static final String MONITORING_ENABLED = "jersey.config.server.monitoring.enabled";public static final String MONITORING_STATISTICS_ENABLED = "jersey.config.server.monitoring.statistics.enabled";public static final String MONITORING_STATISTICS_MBEANS_ENABLED = "jersey.config.server.monitoring.statistics.mbeans.enabled";public static final String MONITORING_STATISTICS_REFRESH_INTERVAL = "jersey.config.server.monitoring.statistics.refresh.interval";public static final String APPLICATION_NAME = "jersey.config.server.application.name";public static final String TRACING = "jersey.config.server.tracing.type";public static final String TRACING_THRESHOLD = "jersey.config.server.tracing.threshold";public static final String RESPONSE_SET_STATUS_OVER_SEND_ERROR = "jersey.config.server.response.setStatusOverSendError";public static final String PROCESSING_RESPONSE_ERRORS_ENABLED = "jersey.config.server.exception.processResponseErrors";public static final String SUBRESOURCE_LOCATOR_CACHE_SIZE = "jersey.config.server.subresource.cache.size";public static final int SUBRESOURCE_LOCATOR_DEFAULT_CACHE_SIZE = 64;public static final String SUBRESOURCE_LOCATOR_CACHE_AGE = "jersey.config.server.subresource.cache.age";public static final String SUBRESOURCE_LOCATOR_CACHE_JERSEY_RESOURCE_ENABLED = "jersey.config.server.subresource.cache.jersey.resource.enabled";public static final String LOCATION_HEADER_RELATIVE_URI_RESOLUTION_RFC7231 = "jersey.config.server.headers.location.relative.resolution.rfc7231";public static final String LOCATION_HEADER_RELATIVE_URI_RESOLUTION_DISABLED = "jersey.config.server.headers.location.relative.resolution.disabled";}
三、REST API设计
REST式的WEB服务使用HTTP的通用方法作为统一接口的标准词汇,REST式的Web服务所提供的方法信息都在HTTP方法里。在实际开发过程中一般根据业务为其定义资源路径,以对外提供REST服务。图书资源路径下表:
资源路径 | 接口说明 | HTTP方式 | 输入 | 输出 |
---|---|---|---|---|
/book/list | 获取全部图书资源 | GET | 图书资源列表 | |
/book/{bookId} | 通过主键获取图书资源 | GET | bookId | 图书资源 |
/book | 新增图书资源 | POST | Book对象 | |
/book | 保存图书资源 | PUT | ||
/book/{bookId} | 删除图书资源 | DELETE |
在V5实际开发过程中,我们只是能使用GET和POST方式,如果还有其他操作的时候,就不够使用了,笔者建议对于其他操作采用动词方式,例如:
资源路径 | 说明 |
---|---|
/book/{bookId}/rename | 重命名 |
/book/{bookId}/restat | 修改状态 |
/book/{bookId}/resort | 修改排序 |
3.1 Jersey 常用注解
3.1.1 @QueryParam
@QueryParam用来定义查询参数
findDepartment(@QueryParam("accountId") Long accountId, final @QueryParam("q") String q)
3.1.2 @PathParam
@PathParam注解用来定义路径参数,每个参数对应一个子资源。
- 普通方式
@Path("{bizId}/{spaceId}/restate")
method1(@PathParam("bizId") Long bizId, @PathParam("spaceId") Long spaceId) {
- 正则表达式
@Path("{from:\\d+}-{to:\\d+}")
method2(@PathParam("from") Integer from, @PathParam("to") Integer to)
- 路径区间
@GET
@Path("{themeId}/{path:.+}")
public Response getThemeCss(@PathParam("themeId") Long themeId, @PathParam("path") List<PathSegment> paths) {StringBuilder sb = new StringBuilder();for (PathSegment pathSegment : paths) {sb.append(pathSegment.getPath()).append("/");}return Response.ok(sb.toString()).build();
}
3.1.3 @Context
@Context注解用来解析上下文参数,可以用来定义Application,Request,Response,Providers,UriInfo, HttpHeader
@GET
getByAddress(@Context Application application,@Context HttpServletRequest request,@Context HttpServletResponse response,@Context Providers providers,@Context UriInfo uriInfo,@Context HttpHeaders headers)
3.2 HTTP 状态码
JAX-RS规定REST的WEB服务的基本异常有3类,分别对应:
- HTTP状态码为3XX的:表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向;
- HTTP状态码为4XX的:这些状态代码表示请求可能出错,妨碍了服务器的处理;
- HTTP状态码为5XX的:这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错;
状态码 | 说明 | 异常类 |
---|---|---|
200 OK | 服务器正常处理 | |
201 Created | 创建新实体,响应头Location指定访问该实体的URL | |
202 Accepted | 服务器接收请求,处理尚未完成。可用于异步处理机制 | |
204 No Content | 服务器正常响应,但响应实体为空 | |
301 Move Permanently | 请求资源的地址发生永久变动,响应头Location指定新的URL | |
302 Found | 请求资源的地址发送临时变动 | |
304 Not Modified | 客户端缓存资源依然有效 | |
400 Bad Request | 请求信息出现语法错误 | BadRequestException |
401 Unauthorized | 请求资源未授权给未验证用户 | NotAuthorizedException |
403 Forbidden | 请求资源未授权给当前用户 | ForbiddenException |
404 Not Found | 请求资源不存在 | NotFoundException |
405 Method Not Allowed | 请求方法不匹配 | NotAllowedException |
406 Not Acceptable | 请求资源的媒体类型不匹配 | NotAcceptableException |
500 Internal Server Error | 服务器内部错误,意外终止响应 | InternalServerErrorException |
501 Not Implemented | 服务器不支持当前请求 |
四、REST 请求流程
在一次URL请求的过程中会经过以上调用链最后返回将数据返回给调用方(也可能是异常),对于里面出现了非常重要的组件ContainerRequestFilter,ContainerResponseFilter, MessageBodyReader和MessageWriter,再此做简单说明。
4.1 ContainerRequestFilter
- 用于判断用户是否是登录状态,也就是要判断session存不存在
- 用于记录日志
4.2 ContainerResponseFilter
与ContainerRequestFilter组合使用
4.3 MessageBodyReader
将流转换指定Java类,MessageBodyReader可以与Consumers进行注释,以限制将被适合的媒体类型。
4.3 MessageWriterReader
与MessageBodyReader做的事情恰好相反,将指定的Java类转换为数据流。
5、REST 接口开发最佳实践
5.1 创建XXResource并继承CAPBaseResource类
/*** @Description:* 1. 通常情况是用json做为输入/输出(若有其他输入/输出格式请在方法上定义);* 2. 复写BaseResource#success解决数据为Long数据丢失精度;* 3. 对于返回的数据格式为非application/json类型,请使用Response自行构建* @Date: 2019-06-24 20:23*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CAPBaseResource extends BaseResource {/*** fastjson序列化器** @return*/protected FastJsonConfig getFastJsonConfig() {FastJsonConfig fastJsonConfig = new FastJsonConfig();SerializeConfig serializeConfig = SerializeConfig.globalInstance;serializeConfig.put(BigInteger.class, ToStringSerializer.instance);serializeConfig.put(Long.class, ToStringSerializer.instance);serializeConfig.put(Long.TYPE, ToStringSerializer.instance);serializeConfig.put(Integer.class, ToStringSerializer.instance);serializeConfig.put(Integer.TYPE, ToStringSerializer.instance);serializeConfig.put(Date.class, new DateObjectSerializer());fastJsonConfig.setSerializeConfig(serializeConfig);fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);return fastJsonConfig;}@Overrideprotected Response success(Object object) {Map<String, Object> map = Maps.newHashMap();map.put("code", 0);map.put("data", object);FastJsonConfig fastJsonConfig = getFastJsonConfig();Object entity = JSON.toJSONString(map, fastJsonConfig.getSerializeConfig(), fastJsonConfig.getSerializerFeatures());return Response.ok(entity).build();}/*** 日期输出转换器*/private class DateObjectSerializer implements ObjectSerializer {@Overridepublic void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {SerializeWriter out = serializer.out;if (object == null) {out.writeNull();} else {String strVal = Datetimes.formatDatetime((Date)object);out.writeString(strVal);}}}
}
5.2 在XXResource指定Path & 在方法上指定Path和请求方式
@Path("CC4/org")
public class CC4OrgResource extends CAPBaseResource {private static final Log logger = XXLogFactory.getLog(CC4OrgResource .class);private OrganizationAdapterManager getOrganizationAdapterManager() {return (OrganizationAdapterManager)AppContext.getBean("organizationAdapterManager");}/*** 获取用户可访问单位列表** @return*/@GET@Path("account")public Response findAccessableAccounts() {try {List<V3xOrgAccount> list = getCurrentUser();DefaultDataSetList dataSetList = new DefaultDataSetList();Set<String> accountIds = Sets.newHashSet();for (V3xOrgAccount account : list) {String parentId = account.getSuperior().toString();Node branchNode = dataSetList.createBranchNode(account.getId().toString(), account.getName(), parentId);dataSetList.addNode(branchNode);accountIds.add(parentId);}for(Node node : dataSetList.getList()) {if (!accountIds.contains(node.getId())) {node.setBranch(false);}}return success(dataSetList);} catch (BusinessException e) {logger.error("", e);}return success(ServiceResult.internalError());}
}
6、Jersey扩展
- Jersey推送 & 异步通信
- JerseyAOP
- Jersey安全
7、参考链接
- HTTP各种状态码的意义(HTTP Status Code)
- REST in Action 《REST 实战》
- RESTful 架构风格概述
- Jersey中ContainerRequestFilter的使用
- REST原则六约束与Richardson成熟度模型
Jersey——30分钟速读《Java RESTful Web Service 实战》相关推荐
- 2018年读《Java Restful Web Service 实战》笔记
2018年大年初四读<Java Restful Web Service 实战>看到一句话写的挺好的,做个记录: **搞技术的人,是停不下来的. 时而要开疆拓土,学习和研究新的知识点,弥补自 ...
- Java RESTful Web Service实战
编者按: InfoQ开设栏目"品味书香",精选技术书籍的精彩章节,以及分享看完书留下的思考和收获,欢迎大家关注.本文节选自韩陆著<Java RESTful Web Servi ...
- Java RESTful Web Service实战(第2版)
Java核心技术系列 Java RESTful Web Service实战 (第2版) 韩陆 著 图书在版编目(CIP)数据 Java RESTful Web Service实战 / 韩陆著. -2版 ...
- Java RESTful Web Service实战(第2版) 2.3 传输格式
2.3 传输格式 本节要考虑的就是如何设计表述,即传输过程中数据采用什么样的数据格式.通常,REST接口会以XML和JSON作为主要的传输格式,这两种格式数据的处理是本节的重点.那么Jersey是否还 ...
- java 创建restful_使用Java创建RESTful Web Service
[RESTful Web Service是轻量级的service,可以通过HTTP的方式来实现对后台数据库的CRUD,在Web开发和移动开发时使用的比较广泛,非常方便.在Java世界里, JAX-RS ...
- jersey (RESTful Web Service框架)
jersey是一个RESTful Web Service框架.web service即远程函数调用,通过该特性,在互联网中跨机器调用其他服务器上的函数,就像调用本地代码一样简单又方便.原理是框架把请求 ...
- Java开源Web Service(转)
为什么80%的码农都做不了架构师?>>> Axis Apache Axis 是Apache WebService项目中的子项目,其最初起源于IBM的"SOAP4J& ...
- 使用JAX-RS创建RESTful Web Service
guice resteasy http://www.cnblogs.com/ydxblog/p/7891224.html http://blog.csdn.net/withiter/article/d ...
- 在GlassFish应用服务器上创建并运行你的第一个Restful Web Service【翻译】
前言 本人一直开发Android应用,目前Android就业形势恶劣,甚至会一路下滑,因此决定学习服务器开发.采用的语言是java,IDE是Intellij,在下载Intellij的同时看到官网很多优 ...
最新文章
- PermGen space错误解决方法
- 用c语言批量删除指定文件夹,C语言删除文件夹下所有代码的注释for Mac
- Python网络爬虫--BeautifulSoup库的基本元素
- 0点mysql_【转载】MySQL查询当天0点,昨天时间
- CodeForces - 766C - Mahmoud and a Message dp
- 【vue-element-admin 】侧栏原始图标颜色一键指定
- idea mac 替换_史上最全的IntelliJ IDEA For Mac快捷键!快来收藏吧!
- 自己动手,刷一台迷你缓存服务器玩玩
- 一位小创业者血泪史:培养过很多技术大佬,但我还在发传单(转)
- 解决关于升级macOS12后Alfred for mac无法运行PHP插件的问题
- 案例分享:巧用工具提升无源码系统的性能和稳定性
- 学习3ds max插件开发过程中的一些小结
- Python3优雅操作-时间处理与定时任务
- 关于Linux系统之VM安装配置(每一个步骤都超级详细的哦!)
- IDM:从Google Drive快速直接下载大文件
- 《白帽子讲web安全》我的安全世界观
- Ubuntu 安装QT
- KindEditor的简单应用
- 在yandex投放广告的话,需要注册俄罗斯常用的域名吗?
- 中小园区网配置案例 超详细
热门文章
- [Openfire]使用WebSocket建立Openfire的客户端
- Java-使用多态实现主人给宠物投喂食物功能
- 华为服务器安装centos7系统教程,云服务器安装centos7教程
- java 重写compareTo方法实现类自定义排序
- python简单心形代码爱情闪字_qq主人寄语闪字代码:完整保存你给的爱
- bzoj 2820 YY的GCD - 莫比乌斯反演 - 线性筛
- android环形list,一个非常精美的Flutter Todo-List项目
- css新建样式表文件,CSS教程:创建性感的CSS样式表
- Windows 10 每次开机都自动弹出 “今日热点”、“热点资讯” “360每日趣玩”等广告窗口
- NENU - 字符串处理课后作业(问题A~问题H) 解析+参考代码(含有C++中的string函数库)