上篇我们主要讲解利用Jersey组件如何来写一个能保证基本运行的Rest Service, 之所以说能够基本运行是因为接口暴露及其简易,一旦遇到其他的情况了,就无法正确的处理我们的请求。同时,这个接口返回内容太简单了,如果调用失败,调用者根本无法准确的知道具体的错误信息。那么这节,我们将完善接口,为调用者提供 400-Bad Request, 500-Server Error, 304-Not Modified, 200-Response OK, 404-Not Found的识别标志,让调用者能够明白是什么错误,错在了什么地方。

返回码概览

1.400-Bad Request: 这种错误码一般是请求错误,比如说我的UserID需要传入UUID类型的,但是我传入了不符合要求的数据,比如说Int类型,那么服务端收到这种请求,可以直接给客户端返回这个错误代码并附加上相应的错误说明,那么客户端就能明白错在什么地方了。

2.500-Bad Request: 这种错误码一般是处理错误,也就是对整个处理管道异常处理的捕捉,我们可以在catch里面抛出这种错误给用户并附加上相应的错误代码。

3.404-Not Found: 这种错误码一般是找不到内容所致。也就是说如果请求方发给的请求,但是在数据库中找不到相关信息,则可以返回这种错误。

4.200-Response OK: 当客户端发起请求,服务端成功响应,则可以发送这种状态码。

5.304-Not Modified: 这种返回码是通过计算ETag来进行的,具体的流程如下: 客户端首先向服务器端发送请求,服务器端收到请求,然后计算出Etag,附加到header中传递给客户端。客户端以后再请求的话,需要附带上 If-None-Match:Etag值,发送给服务器端,服务器接收到这个值后,然后重新生成Etag值,最后做比对,如果没变,则返回客户端304;如果有变化,则需要从数据库提取数据,返回给客户端200状态码。

代码设计

在这里我们不在详细说明具体的设计步骤,我只展示具体的代码。

首先,我们定义一个Get的API操作方法:

    /**
* Provide the endpoint used to get the summary of the user progress
*/
@GET
@Path("{"+ PtsResourcePaths.PROGRESS_SUMMARY_ROOT_PATH_RESOURCE+"}/"+ PtsResourcePaths.PROGRESS_SUMMARY_BINARY_PATH_RESOURCE)
@Produces(MediaType.APPLICATION_JSON)
Response UserLearningSummaryRequest(@PathParam(PtsResourcePaths.PROGRESS_SUMMARY_ROOT_PATH_RESOURCE) String titanUserId, @Context Request request);

从上面的定义中,我们可以看到其接收两个参数,一个是用户ID,另外一个是客户端请求上下文(Request对象可以进行ETag值的计算)。

然后,我们来看看数据库Bean处理:

    @Override
public List<UserLearningCourse> findLearnerProgressSummaryByTitanUserIdent(String titanUserId) {
return userLearningCourseDao.fetchSummaryProgressByUserIdent(titanUserId);
}

是不是很简单,数据库处理这块直接从数据库获取数据集合,返回即可。

然后,我们来看看业务Bean处理:

 @Override
public GenericResponseWrapperDto<UserLearningCoursesResponse> processUserLearningSummaryRequest(String titanUserId, Request request)
throws DataValidationException {
GenericResponseWrapperDto<UserLearningCoursesResponse> responseWrapper = new GenericResponseWrapperDto<UserLearningCoursesResponse>(); UserLearningCoursesResponseDto userLearningCoursesResponseDto = new UserLearningCoursesResponseDto();
List userLearningSummaryDtoCollection = new ArrayList(); //if the titanUserId match the uuid format
if (!titanUserId.matches("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}")) {
responseWrapper.setHttpStatusCode(PtsErrors.ErrorEnum.TITAN_USER_IDENT_INVALID.getStatus().getStatusCode());
responseWrapper.setErrorMessage(PtsErrors.ErrorEnum.TITAN_USER_IDENT_INVALID.getErrorMessage());
responseWrapper.setErrorCode(PtsErrors.ErrorEnum.TITAN_USER_IDENT_INVALID.getErrorCode());
return responseWrapper;
} List<UserLearningCourse> resultCollection = userLearningSummaryBean.findLearnerProgressSummaryByTitanUserIdent(titanUserId); if (resultCollection == null || resultCollection.size()==0) {
responseWrapper.setHttpStatusCode(PtsErrors.ErrorEnum.USER_LEARNING_COURSE_SUMMARY_EMPTY.getStatus().getStatusCode());
responseWrapper.setErrorMessage(PtsErrors.ErrorEnum.USER_LEARNING_COURSE_SUMMARY_EMPTY.getErrorMessage());
responseWrapper.setErrorCode(PtsErrors.ErrorEnum.USER_LEARNING_COURSE_SUMMARY_EMPTY.getErrorCode());
return responseWrapper;
} //mapper between the response entity and responsedto entity
String etagStr = "";
for (UserLearningCourse userLearningCourse : resultCollection) {
UserLearningCourseResponseDto userLearningCourseResponseDto = new UserLearningCourseResponseDto(); userLearningCourseResponseDto.setCourseId(userLearningCourse.getCourseIdentifier());
userLearningCourseResponseDto.setBestGrade(userLearningCourse.getBestCourseGrade());
userLearningCourseResponseDto.setLatestGrade(userLearningCourse.getLatestCourseGrade()); List<UserLearningSequence> userLearningSequences = userLearningCourse.getUserLearningSequences();
List<UserLearningSequenceResponse> userLearningSequenceResponses = new ArrayList<UserLearningSequenceResponse>(); if (userLearningSequences != null && userLearningSequences.size() > 0) {
for (UserLearningSequence userLearningSequence : userLearningSequences) { UserLearningSequenceResponseDto userLearningSequenceResponse = new UserLearningSequenceResponseDto(); userLearningSequenceResponse.setSequenceId(userLearningSequence.getSequenceIdentifier());
userLearningSequenceResponse.setBestGrade(userLearningSequence.getBestSequenceGrade());
userLearningSequenceResponse.setLatestGrade(userLearningSequence.getLatestSequenceGrade());
userLearningSequenceResponses.add(userLearningSequenceResponse);
}
} userLearningCourseResponseDto.setSequences(userLearningSequenceResponses);
userLearningSummaryDtoCollection.add(userLearningCourseResponseDto);
//calculate the ETAG
etagStr += formatDate(userLearningCourse.getlastModifiedAt());
} userLearningCoursesResponseDto.setUserId(titanUserId);
userLearningCoursesResponseDto.setCourses(userLearningSummaryDtoCollection); //the hashcode should based on the last_modify_date in database.
EntityTag eTag = new EntityTag(etagStr.hashCode() + "");
//verify if it matched with etag available in http request
Response.ResponseBuilder builder = request.evaluatePreconditions(eTag);
//set Etag value
setETag(eTag); if (builder != null) {
//304 here, Not modified,directly return
responseWrapper.setHttpStatusCode(Response.Status.NOT_MODIFIED.getStatusCode());
} else {
//200 here, Content changed
responseWrapper.setHttpStatusCode(Response.Status.OK.getStatusCode());
responseWrapper.setPayload(userLearningCoursesResponseDto);
} return responseWrapper;
}

从上面的代码中,我们可以看到,ETAG的计算是把LastModifiyAt的时间相加,然后通过HashCode方法得到处理结果,然后发送给客户端。客户端再次请求发送Etag过来的时候,我们可以通过request.evaluatePreconditions(eTag)来计算当前的ETag和客户端发来的ETag是否相等,如果一致则返回304状态码,如果不一致,则表明数据库数据有变化,则直接从数据库重新获取数据,然后发送给客户端。

最后我们看看服务端如何把ETag附加给客户端:

 @Override
public Response UserLearningSummaryRequest(String titanUserId,Request request) { GenericResponseWrapper<?> responseWrapper = userLearningSummaryHandlerBean
.processUserLearningSummaryRequest(titanUserId, request); ResponseBuilder responseBuilder = Response.status(responseWrapper.getHttpStatusCode());
responseBuilder.entity(responseWrapper); //Send ETag back to client
responseBuilder.tag(userLearningSummaryHandlerBean.getETag()); return responseBuilder.build();
}

通过responseBuilder即可返回,responseBuilder是对Jersey的Client对象的封装。

接口调试

1.正常请求,200状态码返回:

然后我们看看Header的内容:

很清晰的看到了ETag的返回值。

2.用户ID不是UUID类型的时候,400状态码返回:

3.用户ID在数据库中不存在的时候,由于查询不到数据,404状态码返回:

4.用户附加ETag,请求相同的数据的时候,由于服务器之前返回过一致的内容了,所以这里不必再返回,直接返回304状态码提示数据未更新:

5.用户附加错误的ETag(未加双引号),导致服务端解析出错,直接返回500状态码提示错误:

JAVA格物致知基础篇:你所不知道的返回码的更多相关文章

  1. 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制

    你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...

  2. C#线程篇---你所不知道的线程池(4)

    线程的创建和销毁都要耗费大量的时间,有什么更好的办法?用线程池! 太多的线程浪费内存资源,有什么更好的办法?用线程池! 太多线程有损性能,有什么更好的办法?用线程池!(⊙_⊙)? 线程池是什么?继前三 ...

  3. java方法句柄-----4.你所不知道的MethodHandle【翻译】

    Method Handles in Java 1.介绍 在本文中,我们将探讨一个重要的API,它是在Java 7中引入的,并在Java 7版本之后更加完善:全限定名是:Java.lang.invoke ...

  4. JAVA格物致知基础篇:用JAX-RS和Jersey打造RESTful Service

    随着服务器的处理能力越来越强,业务需求量的不断累积,越来越多的公司开始从单一服务器,单一业务承载变成了多服务器,多业务承载的快速扩展的过程中.传统的方法很难满足和应付这种业务量的增长和部署方式的改变. ...

  5. 你所不知道的 CSS 滤镜技巧与细节

    承接上一篇你所不知道的 CSS 动画技巧与细节,本文主要介绍 CSS 滤镜的不常用用法,希望能给读者带来一些干货! OK,下面直接进入正文.本文所描述的滤镜,指的是 CSS3 出来后的滤镜,不是 IE ...

  6. 你所不知道的五件事情--java.util.concurrent(第二部分)

    这是Ted Neward在IBM developerWorks中5 things系列文章中的一篇,仍然讲述了关于Java并发集合API的一些应用窍门,值得大家学习.(2010.06.17最后更新) 摘 ...

  7. JavaScript中你所不知道的Object(二)--Function篇

    上一篇(JavaScript中你所不知道的Object(一))说到,Object对象有大量的内部属性,而其中多数和外部属性的操作有关.最后留了个悬念,就是Boolean.Date.Number.Str ...

  8. 你所不知道的html5与html中的那些事第三篇

    文章简介: 关于html5相信大家早已经耳熟能详,但是他真正的意义在具体的开发中会有什么作用呢?相对于html,他又有怎样的新的定义与新理念在里面呢?为什么一些专家认为html5完全完成后,所有的工作 ...

  9. Android Context完全解析,你所不知道的Context的各种细节

    Context相信所有的Android开发人员基本上每天都在接触,因为它太常见了.但是这并不代表Context没有什么东西好讲的,实际上Context有太多小的细节并不被大家所关注,那么今天我们就来学 ...

随机推荐

  1. 第一个随笔,调试中,用的CSS3

    希望能在博客园很好的学习并得到技术上的提升!

  2. 原生JS实战:经典贪吃蛇(开局10倍速度,来看看你最高能得多少分!)

    本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5875523.html 该程序是本人的个人作品,写的不好,未经本人允许,请 ...

  3. gulp安装说明

    1.安装node-v6.3.0-x64,安装成功后再点击node-v6.3.0-x64卸载(点击remove). 2.安装node-v4.4.7-x64. 3.打开cmd命令行,输入node -v,查 ...

  4. Synchronization Service Manager

    You can use this UI Shell to check the User Profile log for the SharePoint.   It's stored in this pa ...

  5. OC 类簇与复合

    OC 类簇与复合 类簇: 类簇是Foundation框架中广泛使用的设计模式.类簇将一些私有的.具体的子类组合在一个公共的.抽象的超类下面,以这种方法来组织类可以简化一个面向对象框架的公开架构,而又不 ...

  6. java设计模式 模板方法模式Template Method

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性.毫无疑问,设计模式于己 ...

  7. iOS 学习 - 18.TextField 自定义菜单事件,复制和微信分享

    菜单事件包括,剪切.拷贝.全选.分享...,此 demo 只有 copy.share 1.定义 field 继承与 UITextField - (BOOL)canPerformAction:(SEL) ...

  8. [Java编程思想-学习笔记]第4章 控制执行流程

    4.1  return 关键字return有两方面的用途:一方面指定一个方法结束时返回一个值:一方面强行在return位置结束整个方法,如下所示: char test(int score) { if ...

  9. [分享] 很多人手机掉了,却不知道怎么找回来。LZ亲身经历讲述手机找回过程,申请加精!

    文章开头:(LZ文笔不好,以下全部是文字描述,懒得配图.因为有人说手机掉了,他们问我是怎么找回来的.所以想写这篇帖子.只不过前段时间忙,没时间.凑端午节给大家一些经验) 还是先谢谢被偷经历吧!5月22 ...

  10. .NET应用程序调试—原理、工具、方法

    阅读目录: 1.背景介绍 2.基本原理(Windows调试工具箱..NET调试扩展SOS.DLL.SOSEX.DLL) 2.1.Windows调试工具箱 2.2..NET调试扩展包,SOS.DLL.S ...