JAVA格物致知基礎篇:你所不知道的返回碼

上篇咱們主要講解利用Jersey組件如何來寫一個能保證基本運行的Rest Service, 之因此說可以基本運行是由於接口暴露及其簡易,一旦遇到其餘的狀況了,就沒法正確的處理咱們的請求。同時,這個接口返回內容太簡單了,若是調用失敗,調用者根本沒法準確的知道具體的錯誤信息。那麼這節,咱們將完善接口,爲調用者提供 400-Bad Request, 500-Server Error, 304-Not Modified, 200-Response OK, 404-Not Found的識別標誌,讓調用者可以明白是什麼錯誤,錯在了什麼地方。html

返回碼概覽java

1.400-Bad Request: 這種錯誤碼通常是請求錯誤,好比說個人UserID須要傳入UUID類型的,可是我傳入了不符合要求的數據,好比說Int類型,那麼服務端收到這種請求,能夠直接給客戶端返回這個錯誤代碼並附加上相應的錯誤說明,那麼客戶端就能明白錯在什麼地方了。數據庫

2.500-Bad Request: 這種錯誤碼通常是處理錯誤,也就是對整個處理管道異常處理的捕捉,咱們能夠在catch裏面拋出這種錯誤給用戶並附加上相應的錯誤代碼。服務器

3.404-Not Found: 這種錯誤碼通常是找不到內容所致。也就是說若是請求方發給的請求,可是在數據庫中找不到相關信息,則能夠返回這種錯誤。app

4.200-Response OK: 當客戶端發起請求,服務端成功響應,則能夠發送這種狀態碼。less

5.304-Not Modified: 這種返回碼是經過計算ETag來進行的,具體的流程以下: 客戶端首先向服務器端發送請求,服務器端收到請求,而後計算出Etag,附加到header中傳遞給客戶端。客戶端之後再請求的話,須要附帶上 If-None-Match:Etag值,發送給服務器端,服務器接收到這個值後,而後從新生成Etag值,最後作比對,若是沒變,則返回客戶端304;若是有變化,則須要從數據庫提取數據,返回給客戶端200狀態碼。ide

代碼設計fetch

在這裏咱們不在詳細說明具體的設計步驟,我只展現具體的代碼。ui

首先,咱們定義一個Get的API操做方法:lua

    /**
     * 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狀態碼返回:

image

而後咱們看看Header的內容:

image

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

2.用戶ID不是UUID類型的時候,400狀態碼返回:

image

3.用戶ID在數據庫中不存在的時候,因爲查詢不到數據,404狀態碼返回:

image

4.用戶附加ETag,請求相同的數據的時候,因爲服務器以前返回過一致的內容了,因此這裏沒必要再返回,直接返回304狀態碼提示數據未更新:

image

5.用戶附加錯誤的ETag(未加雙引號),致使服務端解析出錯,直接返回500狀態碼提示錯誤:

image

相關文章
相關標籤/搜索