SpringBoot Web應(yīng)用范例

本文主要是介紹一個(gè)基于SpringBoot的Web應(yīng)用范例士鸥,范例中根據(jù)自己多年的工作經(jīng)驗(yàn)及總結(jié)提供了一些使用建議,希望對(duì)大家有所啟發(fā)

  • 代碼結(jié)構(gòu)概覽
    代碼結(jié)構(gòu)很簡(jiǎn)單块饺,就是很常見(jiàn)的三層架構(gòu),通過(guò)包名來(lái)清晰的展現(xiàn)該種層次結(jié)構(gòu):


    image.png
  • Web API封裝
    Web API層主要是給前端提供web api接口,我們規(guī)定了拴袭,所有的api接口響應(yīng)報(bào)文必須是如下結(jié)構(gòu)體:

public class Response<T> {

    private int status;
    private String msg;
    private T data;

    public Response() {
    }

    public Response(int status, String msg, T data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public int getStatus() {
        return status;
    }

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

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

其中 status表明接口狀態(tài)碼(成功/失敗/其他...,0表示成功), msg是對(duì)狀態(tài)碼的簡(jiǎn)單文字描述(比如:success曙博、失敗原因 等等), data是接口返回的具體數(shù)據(jù)

對(duì)返回報(bào)文格式進(jìn)行統(tǒng)一規(guī)定拥刻,有利于前端做一些公共邏輯:比如對(duì)非0狀態(tài)碼記錄console日志、對(duì)一些特定狀態(tài)碼執(zhí)行一些統(tǒng)一邏輯等等

返回統(tǒng)一報(bào)文格式是通過(guò)AOP來(lái)實(shí)現(xiàn)的父泳,并不需要在業(yè)務(wù)代碼中轉(zhuǎn)換成Response實(shí)體般哼,封裝代碼如下:

  1. 接口正常返回的情況:
@Order(1)
@ControllerAdvice(basePackages = AppContants.API_BASE_PACKAGE)
public class ApiResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    private static final Logger logger = LoggerFactory.getLogger(ApiResponseBodyAdvice.class);

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                    Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                    ServerHttpResponse response) {
        Response<Object> resp = new Response<>();
        resp.setStatus(0);
        resp.setMsg("success");
        resp.setData(body);

        if(returnType.getMethod().getReturnType() == String.class){
            return JSON.toJSONString(resp);
        }else{
            return resp;
        }
    }
}
  1. 接口拋異常的情況:
@ControllerAdvice(basePackages = AppContants.API_BASE_PACKAGE)
public class ApiExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private static final int UNKNOWN_EXCEPTION_STATUS = -2;
    private static final int ILLEGAL_ARGUMENT_STATUS = -1;

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Response defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        Response response = new Response();
        if (e instanceof AppException) {
            response.setStatus(((AppException) e).getCode());
            response.setMsg(e.getMessage());
        } else if (e instanceof IllegalArgumentException) {
            response.setStatus(ILLEGAL_ARGUMENT_STATUS);
            response.setMsg(e.getMessage());
        } else {
            logger.error("Got exception,url=" + req.getRequestURI(), e);
            response.setStatus(UNKNOWN_EXCEPTION_STATUS);
            response.setMsg("接口異常");
        }
        return response;
    }
}

經(jīng)過(guò)這樣的封裝后吴汪,業(yè)務(wù)代碼中不再涉及Response實(shí)體的轉(zhuǎn)換,如下例子所示:

@RestController
@Api(description = "用戶相關(guān)接口")
@RequestMapping("/api/user")
public class UserEndpoint {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private UserService userService;

    @ApiOperation(value = "查詢用戶", notes = "根據(jù)用戶id查詢用戶詳情")
    @GetMapping("/{userId}")
    public User getUser(@PathVariable("userId") Integer userId) {
        Preconditions.checkNotNull(userId, "user id not provided");
        Preconditions.checkArgument(userId > 0, "user id must greater than 0");

        return this.userService.getUser(userId);
    }

    @ApiOperation(value = "更新用戶信息")
    @PostMapping("/update")
    public void updateUser(@RequestBody User user) {
        logger.info("User:{}", user);
    }
}

在這里簡(jiǎn)單說(shuō)一下蒸眠,上面代碼有幾句關(guān)于參數(shù)校驗(yàn)的代碼(如下所示)漾橙,我們只需要簡(jiǎn)單用Preconditions工具類來(lái)進(jìn)行判斷,如果條件不滿足將會(huì)拋出IllegalArgumentException楞卡,這個(gè)未捕獲的異常將會(huì)在ApiExceptionHandler得到處理 并將返回status設(shè)置為-1霜运,msg設(shè)置為異常信息。這樣處理后顯得代碼特別簡(jiǎn)潔 有木有~

Preconditions.checkNotNull(userId, "user id not provided");
Preconditions.checkArgument(userId > 0, "user id must greater than 0");
  • Swagger API文檔
    關(guān)于Swagger的介紹蒋腮,網(wǎng)上有很多淘捡,不了解的可以看看:Spring Boot中使用Swagger2構(gòu)建強(qiáng)大的RESTful API文檔

    Springboot 可以很簡(jiǎn)單地就把swagger 集成到應(yīng)用中,通過(guò)swagger api文檔池摧,可以減少前后端的溝通成本焦除,并且也給后端人員提供了便捷的調(diào)試接口的方式,非常推薦使用

  • Github 源代碼
    本文中所涉及的完整的代碼已經(jīng)放到github上险绘,有興趣的童鞋可以看看:springboot-web-showcase

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踢京,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子宦棺,更是在濱河造成了極大的恐慌瓣距,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件代咸,死亡現(xiàn)場(chǎng)離奇詭異蹈丸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)呐芥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門逻杖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人思瘟,你說(shuō)我怎么就攤上這事荸百。” “怎么了滨攻?”我有些...
    開(kāi)封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵够话,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我光绕,道長(zhǎng)女嘲,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任诞帐,我火速辦了婚禮欣尼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘停蕉。我一直安慰自己愕鼓,他們只是感情好钙态,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著拒啰,像睡著了一般驯绎。 火紅的嫁衣襯著肌膚如雪窃躲。 梳的紋絲不亂的頭發(fā)上不傅,一...
    開(kāi)封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天忧勿,我揣著相機(jī)與錄音烫映,去河邊找鬼伯顶。 笑死斟冕,一個(gè)胖子當(dāng)著我的面吹牛坷澡,可吹牛的內(nèi)容都是我干的讨便。 我是一名探鬼主播甲捏,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼演熟,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了司顿?” 一聲冷哼從身側(cè)響起芒粹,我...
    開(kāi)封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎大溜,沒(méi)想到半個(gè)月后化漆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钦奋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年座云,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片付材。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡朦拖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出厌衔,到底是詐尸還是另有隱情璧帝,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布富寿,位于F島的核電站睬隶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏作喘。R本人自食惡果不足惜理疙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一晕城、第九天 我趴在偏房一處隱蔽的房頂上張望泞坦。 院中可真熱鬧,春花似錦砖顷、人聲如沸贰锁。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)豌熄。三九已至授嘀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锣险,已是汗流浹背蹄皱。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芯肤,地道東北人巷折。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像崖咨,于是被迫代替她去往敵國(guó)和親锻拘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容