软件工程应用与实践(15)——请求与响应

Posted 叶卡捷琳堡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软件工程应用与实践(15)——请求与响应相关的知识,希望对你有一定的参考价值。

2021SC@SDUSC

一、概述

在老年健康知识图谱系统中,后端与前端的交互是很重要的,在前端向后端发送请求的过程中,后端需要向前端返回响应的数据,而本篇博客将重点针对本项目中关于请求与响应的部分做相关分析。

经过小组讨论,决定由我分析关于响应和请求的部分,关于这块内容,在项目中的源码主要涉及一下几个部分

在前端中,有封装好的,发送请求的js文件,这些js文件中封装了一个个请求的函数,在前端vue页面中可以调用。

而在后端,则对请求包装了相应的相应对象,这些相应对象发送到前端之后,前端会根据不同的响应对象给出不同的处理。而在后端处理的过程中,可能会遇到一些异常情况,而老年健康知识图谱系统中,又对异常情况进行了相应的处理。

在下面的源码分析中,我会对这些部分做进一步分析。

二、源码分析

2.1 响应

为了更好地理解请求与响应的交互关系,我决定先阅读响应部分的源码,之后再理解请求部分的源码。

首先我们可以看到,在项目中,定义了三个文件夹,这三个文件夹分别描述了

  • 后端处理前端请求时可能遇到的异常
  • 后端处理异常
  • 返回给前端的具体的响应信息

    1.响应信息

我们首先阅读响应信息部分的源码

首先我们可以看到,在对应的文件夹中,有一个BaseResponse类

在这个类中,描述了如下信息

  • 两个属性,相应的状态码和具体的信息
  • 有参构造,无参构造及相应的get和set方法
public class BaseResponse 

    private int statusCode = 200;
    private String message;

    public BaseResponse(int status, String message) 
        this.statusCode = status;
        this.message = message;
    

    public BaseResponse() 
    

    public String getMessage() 
        return message;
    

    public void setMessage(String message) 
        this.message = message;
    

    public int getStatusCode() 
        return statusCode;
    

    public void setStatusCode(int statusCode) 
        this.statusCode = statusCode;
    

可以知道,这个类的主要作用是,用于描述正常执行时的响应状态。

接下来我们看到下一个类TableResultResponse,从类名上可以知道,这个类主要用于返回与表格相关的相应数据

  • 使用一个TableData对象存储表格数据
  • 在内部类TableData中,一个List类型的变量存储了表格每一行的数据,而另一个total变量则存储了表格的行数
public class TableResultResponse<T> extends BaseResponse 

    TableData<T> data;

    public TableResultResponse(long total, List<T> rows) 
        this.data = new TableData<T>(total, rows);
    

    public TableResultResponse() 
        this.data = new TableData<T>();
    

    TableResultResponse<T> total(int total) 
        this.data.setTotal(total);
        return this;
    

    TableResultResponse<T> total(List<T> rows) 
        this.data.setRows(rows);
        return this;
    

    public TableData<T> getData() 
        return data;
    

    public void setData(TableData<T> data) 
        this.data = data;
    

    class TableData<T> 
        long total;
        List<T> rows;

        public TableData(long total, List<T> rows) 
            this.total = total;
            this.rows = rows;
        

        public TableData() 
        

        public long getTotal() 
            return total;
        

        public void setTotal(long total) 
            this.total = total;
        

        public List<T> getRows() 
            return rows;
        

        public void setRows(List<T> rows) 
            this.rows = rows;
        
    

本项目通过将表格数据的返回封装成一个对象,并在该类中使用了范型的技术,从而提高了利用效率,这是值得借鉴的。

接下来我们看到下一个类ObjectRestResponse,这个类主要用于返回对象类型的数据

  • 使用一个范型变量data存储对象的数据
  • 建立了相关的构造方法,get和set方法用于赋值
public class ObjectRestResponse<T> extends BaseResponse 

    T data;
    
    public ObjectRestResponse data(T data) 
        this.setData(data);
        return this;
    
    public T getData() 
        return data;
    

    public ObjectRestResponse setData(T data) 
        this.data = data;
        return this;
    

本类与上面的类作用相似,只不过上面的类用于返回表格数据,而本类用于返回对象类型的数据

此外,这两个类都继承了上面的BaseResponse类,所以我们可以知道,对与BaseResponse类来说,他存在三种形式,第一种是原生的形式,第二种是返回表格数据的形式,第三种是返回对象类型数据的形式

接下来我们关注返回错误信息的类

关于错误信息的类,本项目中主要有两个

第一个是TokenForbiddenResponse类,同样继承了BaseResponse类,在这个类中,直接调用父类的构造方法,传入对应的状态码和消息。

public class TokenForbiddenResponse  extends BaseResponse 
    public TokenForbiddenResponse(String message) 
        super(RestCodeConstants.TOKEN_FORBIDDEN_CODE, message);
    

第二个类与上面的类类似,同样返回了一个错误码

public class TokenErrorResponse extends BaseResponse 
    public TokenErrorResponse(String message) 
        super(RestCodeConstants.TOKEN_ERROR_CODE, message);
    

而错误码在项目中同样有定义,定义为类中的静态变量,并且设置为常量,不可修改

public class RestCodeConstants 
    public static final int TOKEN_ERROR_CODE = 40101;
    public static final int TOKEN_FORBIDDEN_CODE = 40301;

2.异常类定义及处理

在本项目中,对后端可能发生的异常进行了相关的处理,经过小组讨论,将由我的另一个队友对异常处理部分做详尽的描述,这里我只对异常处理做简单的介绍。

本项目的异常处理与之前的上面的响应处理类似,都先定义了一个Basexxx作为基类,之后对该基类进行拓展,最终完善整个功能

在异常处理这一部分,本项目同样使用了一个基类,即BaseException,这个类首先继承了RuntimeException,并定义了相关的状态码,及set和get方法

public class BaseException extends RuntimeException 

    private int status = 200;

    public int getStatus() 
        return status;
    

    public void setStatus(int status) 
        this.status = status;
    

    public BaseException() 
    

    public BaseException(String message,int status) 
        super(message);
        this.status = status;
    

    public BaseException(String message) 
        super(message);
    

    public BaseException(String message, Throwable cause) 
        super(message, cause);
    

    public BaseException(Throwable cause) 
        super(cause);
    

    public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) 
        super(message, cause, enableSuppression, writableStackTrace);
    

而之后的其他类,也利用上面提到的,同样的方法继承了该Base异常类,从而达到对各个异常进行处理的作用

以下的代码就是具体的例子

public class UserInvalidException extends BaseException 
    public UserInvalidException(String message) 
        super(message, CommonConstants.EX_USER_PASS_INVALID_CODE);
    

public class UserTokenException extends BaseException 
    public UserTokenException(String message) 
        super(message, CommonConstants.EX_USER_INVALID_CODE);
    

关于本项目的异常处理,主要由下面的这个类完成,使用@ExceptionHandler注解,在括号中传入对应的异常类,用于指定处理对应异常类的方法。针对不同的异常,进行不同的处理。

@ControllerAdvice("com.sdu.nurse")
@ResponseBody
public class GlobalExceptionHandler 

    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(ClientTokenException.class)
    public BaseResponse clientTokenExceptionHandler(HttpServletResponse response, ClientTokenException ex) 
        response.setStatus(403);
        logger.error(ex.getMessage(),ex);
        return new BaseResponse(ex.getStatus(), ex.getMessage());
    

    @ExceptionHandler(UserTokenException.class)
    public BaseResponse userTokenExceptionHandler(HttpServletResponse response, UserTokenException ex) 
        response.setStatus(401);
        logger.error(ex.getMessage(),ex);
        return new BaseResponse(ex.getStatus(), ex.getMessage());
    

    @ExceptionHandler(UserInvalidException.class)
    public BaseResponse userInvalidExceptionHandler(HttpServletResponse response, UserInvalidException ex) 
        response.setStatus(200);
        logger.error(ex.getMessage(),ex);
        return new BaseResponse(ex.getStatus(), ex.getMessage());
    

    @ExceptionHandler(BaseException.class)
    public BaseResponse baseExceptionHandler(HttpServletResponse response, BaseException ex) 
        logger.error(ex.getMessage(),ex);
        response.setStatus(500);
        return new BaseResponse(ex.getStatus(), ex.getMessage());
    

    @ExceptionHandler(Exception.class)
    public BaseResponse otherExceptionHandler(HttpServletResponse response, Exception ex) 
        response.setStatus(500);
        logger.error(ex.getMessage(),ex);
        return new BaseResponse(CommonConstants.EX_OTHER_CODE, ex.getMessage());
    

在本类中,还注入了日志对象,用这个日志对象输出对应的日志。

2.2 请求

在看完了响应的部分,接下来我们关注项目中关于“请求”的部分。

在本项目中,关于请求的部分比较简单,在之前的博客中我也略有提及,在这里详细说明一下

本项目中的请求封装在api包下的js文件中,以用户请求为例,代码如下,首先我们可以看到,js的开始部分,引入了axios的fetch,下面的四种请求,返回的都是一个fetch对象。

以get请求为例,传入一个query对象,直接调用fetch,指定对应对应的url,指定method,指定params(get请求所带的参数)。

而在post请求中,将要传递给后端接口的参数写在data中。

import fetch from '@/plugin/axios'

export function page (query) 
  return fetch(
    url: '/api/nurse/user/page',
    method: 'get',
    params: query
  )


export function addObj (obj) 
  return fetch(
    url: '/api/nurse/user',
    method: 'post',
    data: obj
  )


export function getObj (id) 
  return fetch(
    url: '/api/nurse/user/' + id,
    method: 'get'
  )


export function delObj (id) 
  return fetch(
    url: '/api/nurse/user/' + id,
    method: 'delete'
  )


export function putObj (id, obj) 
  return fetch(
    url: '/api/nurse/user/' + id,
    method: 'put',
    data: obj
  )

在具体的页面中,该项目中调用了上面封装好的请求代码,在按下确认按钮后,执行then方法,then方法中调用了delObj方法,而该方法封装在js代码中。

handleDelete (row) 
   this.$confirm('此操作将永久删除, 是否继续?', '提示', 
     confirmButtonText: '确定',
     cancelButtonText: '取消',
     type: 'warning'
   )
     .then(() => 
       delObj(row.id)
         .then(() => 
           this.$notify(
             title: '成功',
             message: '删除成功',
             type: 'success',
             duration: 2000
           )
           const index = this.list.indexOf(row)
           this.list.splice(index, 1)
         )
     )
 

三、总结

在本次阅读源码的过程中,我主要分析了老年健康知识图谱系统中关于请求和响应的内容。在本次阅读源码的过程中,我体会到了该项目在请求封装和异常类封装的巧妙之处,在这个过程中也感谢老师和小组成员对我的帮助,本学期的软件工程应用与实践的源码分析就分析到这里,之后还有一篇博客用于总结。

以上是关于软件工程应用与实践(15)——请求与响应的主要内容,如果未能解决你的问题,请参考以下文章

软件工程应用与实践(15)——请求与响应

HAProxy负载均衡与最佳实践

软件工程应用与实践(16)——总结

软件工程应用与实践(16)——总结

软件工程应用与实践(16)——总结

构建通用WebSocket推送网关的设计与实践