先後臺分離的項目中,如何優雅的傳輸boolean類型的參數

前言:需求

這是一個Spring + Angular先後臺分離的項目,目前有一個查看做業列表的功能,而且已經設置了分頁和幾個查詢參數,如圖。數據庫

圖片.png

如今須要增長一個已評閱未評閱的查詢功能。
圖片.pngapp

Work實體的屬性以下:ui

id: number;
content: string;
createTime: Date;
item = new Item();
score: number;
student = new Student();
updateTime: Date;
reviewed: boolean;
attachments = new Array<Attachment>();

已評閱對應的屬性是reviewed,是boolean類型。this

目前前臺服務層的代碼:url

// 已經寫了分頁參數、按學生姓名查找做業、按學號查找做業,共5個參數
getAll(params: {page: number, size: number, studentName?: string, studentSno?: string, itemId?: number }): Observable<Page<Work>> {
    const _params = {
      page: params.page.toLocaleString(),
      size: params.size.toLocaleString(),
      studentName: params.studentName ? params.studentName : null,
      studentNo: params.studentSno ? params.studentSno : null,
      itemId: params.itemId ? params.itemId.toLocaleString() : null
    };

    return this.httpClient.get<Page<Work>>(`${this.url}/getAll`, {params: _params});
}

因爲先後臺中間隔着一層Http,而且boolean具備特殊性,因此向後臺傳值比較困難,做爲新手,我首先能想到幾種方法:spa

  • 後臺不動,直接在前臺過濾
  • 直接把boolean的變量做爲參數
  • 把boolean轉換爲string類型,經過http傳輸,到後臺轉換爲boolean
  • 不增長參數,在後臺創建三個方法,前臺分別請求不一樣的方法,達到傳輸boolean的目的

嘗試1、前臺過濾(失敗)

這個是最簡單的,由於後臺不用動,直接請求整頁的數據,只在前臺顯示未批閱的做業便可。3d

/**
   * 單選框被用戶點擊時
   * @param $event 彈射值
   * @param reviewed 評閱狀態碼1默認2已評閱3未評閱
   */
  onCheckBoxChange($event: Event, reviewed: number) {
    this.reviewed = reviewed;
    this.load();
  }

那麼問題來了,因爲請求的是一整頁(10條)數據,若是這一頁都是已批閱,那麼這頁直接就是白的...一條數據也沒有。code

圖片.png

雖然不符合操做邏輯,但這個又不算錯誤,系統不會報錯...orm

前臺過濾,失敗。對象

嘗試二:直接傳boolean(失敗)

直接在前臺的M層加入參數:

reviewed: params.reviewed ? params.reviewed : null,

那麼問題來了,httpClient只能傳輸String和對象(即便是傳輸對象,也是序列化變成字符串再傳輸的),不能傳輸number、boolean等類型。

圖片.png

對於number,須要轉化成String類型來傳輸,好比:

itemId: params.itemId ? params.itemId.toLocaleString() : null

但對於boolean類型,並無直接轉化成String類型的方法。

所以,直接傳boolean類型的參數的方式,失敗。

嘗試3、轉化成String類型(成功)

除了Http請求,其餘位置繼續用Boolean傳值,到發起請求的時候轉化爲String。
這樣的好處是:只須要在前臺的服務層後臺的C層增長轉化代碼,其餘位置繼續按照常規的邏輯,直接用Boolean。

在前臺向後臺的傳輸過程當中:

true -> "true" -> true
false -> "false" -> false
undefined -> 前臺不傳值 -> null

這樣就用三個值表示了「已評閱」「未評閱」「所有」三種狀態。

缺點也比較明顯,布爾值只有true和false,若是判斷寫成:

reviewed: params.reviewed ? 'true' : null,

那麼undefined 和false都按false來處理,都不會傳值。

因此就必須分開處理undefined和false,能夠用一個嵌套判斷來實現:

reviewed: params.reviewed === undefined ? null : params.reviewed ? '1' : '0',

同理,後臺也須要判斷,轉化成Boolean:

Boolean _reviewed = null;
if (reviewed != null){
    switch (reviewed) {
        case "1": _reviewed = true;break;
        case "0": _reviewed = false;break;
    }
}

如今,實現了效果:
圖片.png

轉化爲字符串的方法,成功。

代碼邏輯:

圖片.png

嘗試4、經過不一樣方法間接傳輸參數(成功)

「嘗試三」的寫法,主要有兩個問題:

  • 分別處理undefined和false比較麻煩
  • 代碼可讀性差

因爲boolean具備特殊性,只有兩個值,因此能夠不傳輸這個參數,經過調用不一樣的方法,間接傳輸boolean。

現有的後臺C層方法:

/**
     * 獲取全部做業
     * @param pageable 分頁信息
     * @return 全部做業
     */
    @GetMapping("getAll")
    @JsonView(GetAllJsonView.class)
    public Page<Work> findAll(
            @RequestParam(required = false) Long itemId,
            @RequestParam(required = false) String studentName,
            @RequestParam(required = false) String studentNo,
            Pageable pageable) {
        
        return this.workService.getAll(
                itemId,
                studentName,
                studentNo,
                pageable);
    }

目前有三個參數,若是不想增長參數,就再增長兩個方法,而參數不變:

// 原C層的方法
public Page<Work> findAll(){}
// 獲取已評閱的做業
public Page<Work> findAllReviewed(){}
// 獲取未評閱的做業
public Page<Work> findAllUnReviewed(){}

寫到這裏你們應該明白了:在前臺向後臺的傳輸過程當中

true -> 請求findAllReviewed() -> true
false -> 請求findAllUnreviewed() -> false
undefined -> 請求findAll() -> null

因此前臺這麼寫:

getAll(params: {page: number, size: number, studentName?: string, studentSno?: string, itemId?: number, reviewed?: boolean}):
    Observable<Page<Work>> {
    // 準備參數
    const _params = {
      page: params.page.toLocaleString(),
      size: params.size.toLocaleString(),
      studentName: params.studentName ? params.studentName : null,
      studentNo: params.studentSno ? params.studentSno : null,
      itemId: params.itemId ? params.itemId.toLocaleString() : null
    };
    // 判斷,向對應的方法發起請求
    switch (params.reviewed) {
      case true: return this.httpClient.get<Page<Work>>(`${this.url}/getAllReviewed`, {params: _params}); break;
      case false: return this.httpClient.get<Page<Work>>(`${this.url}/getAllUnReviewed`, {params: _params}); break;
    }
    // 默認請求getAll()
    return this.httpClient.get<Page<Work>>(`${this.url}/getAll`, {params: _params});
  }

而後,後臺的三個方法都調用同一個服務層,這時候就能夠按照常規邏輯,傳入Boolean類型了。

代碼邏輯:

圖片.png

總結

經過Http傳輸boolean參數的方法:

  • 前臺轉化成字符串進行傳輸,後臺接收字符串轉化回來
  • 不在方法中傳輸參數,經過請求不一樣的請求,間接傳輸boolean參數

做爲新手,我知道這必定不是最優解,若是有更好的方式,歡迎來打臉,哈哈哈。

擴展

在實際項目中,除了傳值,還遇到了一個難處理的問題:數據庫的條件查詢(使用JPA)。

後臺的倉庫層WorkRepository功能:

default Page getAll(Item item,  String studentName, String studentSno, @NotNull Pageable pageable) {
    Assert.notNull(pageable, "傳入的Pageable不能爲null");
    Specification<Work> specification = WorkSpecs.containingName(studentName)
            .and(WorkSpecs.startWithNo(studentSno))
            .and(WorkSpecs.belongToItem(item));
    return this.findAll(specification, pageable);
}

其中的

Specification<Work> specification = WorkSpecs.containingName(studentName)
            .and(WorkSpecs.startWithNo(studentSno))
            .and(WorkSpecs.belongToItem(item));

就是查詢條件,它分別調用了三個方法,分別按照姓名、學號、實驗項目來查詢。

查看WorkSpecs類,三個方法的代碼以下:

public class WorkSpecs {

    public static Specification<Work> belongToItem(Item item) {
        if (null == item || null == item.getId()) {
            return Specification.where(null);
        }
        return (Specification<Work>) (root, criteriaQuery, criteriaBuilder) ->
                criteriaBuilder.equal(root.get("item").as(Item.class), item);
    }

    public static Specification<Work> containingName(String studentName) {
        if (studentName != null) {
            return (Specification<Work>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.like(root.get("student").get("name").as(String.class), String.format("%%%s%%", studentName));
        } else {
            return Specification.where(null);
        }
    }

    public static Specification<Work> startWithNo(String studentNo) {
        if (studentNo == null) {
            return Specification.where(null);
        }
        return (Specification<Work>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.like(root.get("student").get("no").as(String.class), String.format("%s%%", studentNo));
    }
}

如今增長一個reviewed參數,就要增長相應的查詢條件。

因此第四個條件這樣寫:

public static Specification<Work> isReviewed(Boolean reviewed) {
        if (reviewed == null) {
            return Specification.where(null);
        }
        return (Specification<Work>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.equal(root.get("reviewed").as(Boolean.class),  reviewed);
    }

在倉庫層getAll()方法增長:

.and(WorkSpecs.isReviewed(reviewed));

就是實現JPA經過Boolean參數查詢數據庫了。

相關文章
相關標籤/搜索