JAVA格物致知基础篇:你所不知道的返回码

上篇我们主要讲解利用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状态码提示错误:

时间: 2024-12-28 14:19:00

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

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

线程的创建和销毁都要耗费大量的时间,有什么更好的办法?用线程池! 太多的线程浪费内存资源,有什么更好的办法?用线程池! 太多线程有损性能,有什么更好的办法?用线程池!(⊙_⊙)? 线程池是什么?继前三篇线程基础之后,我们要来学学线程池了.注意,这些信息相当有用! 为了设计和实现可伸缩的.可响应的和可靠的应用程序或组建,线程池是你必须采用的核心技术. 线程池是CLR的,线程池自动为你管理线程的创建和销毁,线程池创建的一组线程将为各种任务而重用,极大提高了使用线程的成本,这也就意味着,你的应用程序其

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

随着服务器的处理能力越来越强,业务需求量的不断累积,越来越多的公司开始从单一服务器,单一业务承载变成了多服务器,多业务承载的快速扩展的过程中.传统的方法很难满足和应付这种业务量的增长和部署方式的改变.所以RESTful service作为一种分布式服务的最佳实践,应运而生. 说到RESTful Service,我们这里首先来明白一下他的基本概念:它是用于创建分布式超文本媒体的一种架构方式,我们可以通过标准的HTTP(GET,POST,PUT,DELETE)操作来构建基于面向资源的软件架构方式(R

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

上一篇(JavaScript中你所不知道的Object(一))说到,Object对象有大量的内部属性,而其中多数和外部属性的操作有关.最后留了个悬念,就是Boolean.Date.Number.String.Function等有更多的内部属性,而它们分别是什么呢? 这些内部属性不能像Object的内部属性一样一言以蔽之,因为它们各有各的用处和特点.其中核心的部分自然是最特殊的对象,Function对象.我们先从简单的开始: [[PrimitiveValue]]: 值的类型是基础数据类型.所以所有

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

这是Ted Neward在IBM developerWorks中5 things系列文章中的一篇,仍然讲述了关于Java并发集合API的一些应用窍门,值得大家学习.(2010.06.17最后更新) 摘要:除了便于编写并发应用的集合API外,java.util.concurrent还引入了其它的预置程序组件,这些组件能辅助你在多线程应用中控制和执行线程.Ted Neward再介绍了五个来自于java.util.concurrent的Java编程必备窍门. 通过提供线程安全,性能良好的数据结构,并发

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

文章简介: 关于html5相信大家早已经耳熟能详,但是他真正的意义在具体的开发中会有什么作用呢?相对于html,他又有怎样的新的定义与新理念在里面呢?为什么一些专家认为html5完全完成后,所有的工作都可以达到真正的云方式呢?这一系列的问题你是否已经想明白了呢? 本系列文章将为您一一解答你所不知道的关于html5与html中的那些事;具体会包括如:html5新的理念与想法,html5的新标签的用意与具体开发中场景应用,html5与css3的感情经历(用法搭配),包括html5的父亲html的一些

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

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/47028975 前几篇文章,我也是费劲心思写了一个ListView系列的三部曲,虽然在内容上可以说是绝对的精华,但是很多朋友都表示看不懂.好吧,这个系列不仅是把大家给难倒了,也确实是把我给难倒了,之前为了写瀑布流ListView的Demo就写了大半个月的时间.那么本篇文章我们就讲点轻松的东西,不去分析那么复杂的源码了,而是来谈一谈大家都熟知的Context. Context相信所有

JavaScript你所不知道的困惑(1)

困惑一: 先看一个例子: function test(){ message = "hi"; } test(); alert(message); 会输出字符串"hi" 在函数内部使用var定义的变量是局部变量,省略var操作符的变量是全局变量. 困惑二: alert(undefined == null) 结果是"true" 我们知道在js中分为基本类型和引用类型,基本类型包括number.string.boolean.undefined.null.

你所不知道的html5与html中的那些事(四)——文本标签

文章简介: 关于html5相信大家早已经耳熟能详,但是他真正的意义在具体的开发中会有什么作用呢?相对于html,他又有怎样的新的定义与新理念在里面呢?为什么一些专家认为html5完全完成后,所有的工作都可以达到真正的云方式呢?这一系列的问题你是否已经想明白了呢? 本系列文章将为您一一解答你所不知道的关于html5与html中的那些事;具体会包括如:html5新的理念与想法,html5的新标签的用意与具体开发中场景应用,html5与css3的感情经历(用法搭配),包括html5的父亲html的一些

你所不知道的html5与html中的那些事(三)

文章简介: 关于html5相信大家早已经耳熟能详,但是他真正的意义在具体的开发中会有什么作用呢?相对于html,他又有怎样的新的定义与新理念在里面呢?为什么一些专家认为html5完全完成后,所有的工作都可以达到真正的云方式呢?这一系列的问题你是否已经想明白了呢? 本系列文章将为您一一解答你所不知道的关于html5与html中的那些事;具体会包括如:html5新的理念与想法,html5的新标签的用意与具体开发中场景应用,html5与css3的感情经历(用法搭配),包括html5的父亲html的一些