Springboot的簡(jiǎn)單RestfulCRUD實(shí)驗(yàn)(下)

代碼鏈接
Springboot的簡(jiǎn)單RestfulCRUD實(shí)驗(yàn)(上)

三苛聘、CRUD-員工列表

實(shí)驗(yàn)要求:
1)设哗、RestfulCRUD:CRUD滿足Rest風(fēng)格;
URI: /資源名稱/資源標(biāo)識(shí) HTTP請(qǐng)求方式區(qū)分對(duì)資源CRUD操作

1

2)震缭、實(shí)驗(yàn)的請(qǐng)求架構(gòu)
2

效果圖:
3

4

3)拣宰、員工列表:
thymeleaf公共頁(yè)面元素抽取

1巡社、抽取公共片段
<div th:fragment="copy">
&copy; 2011 The Good Thymes Virtual Grocery
</div>

2手趣、引入公共片段
<div th:insert="~{footer :: copy}"></div>
~{templatename::selector}:模板名::選擇器
~{templatename::fragmentname}:模板名::片段名

3绿渣、默認(rèn)效果:
insert的公共片段在div標(biāo)簽中
如果使用th:insert等屬性進(jìn)行引入,可以不用寫~{}:
行內(nèi)寫法可以加上:[[~{}]];[(~{})]潜圃;

三種引入公共片段的th屬性:

th:insert:將公共片段整個(gè)插入到聲明引入的元素中

th:replace:將聲明引入的元素替換為公共片段

th:include:將被引入的片段的內(nèi)容包含進(jìn)這個(gè)標(biāo)簽中

<footer th:fragment="copy">
&copy; 2011 The Good Thymes Virtual Grocery
</footer>

引入方式
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>

效果
<div>
    <footer>
    &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
</div>

<footer>
&copy; 2011 The Good Thymes Virtual Grocery
</footer>

<div>
&copy; 2011 The Good Thymes Virtual Grocery
</div>

引入片段的時(shí)候傳入?yún)?shù):

<nav class="col-md-2 d-none d-md-block bg-light sidebar" id="sidebar">
    <div class="sidebar-sticky">
        <ul class="nav flex-column">
            <li class="nav-item">
                <a class="nav-link active"
                   th:class="${activeUri=='main.html'?'nav-link active':'nav-link'}"
                   href="#" th:href="@{/main.html}">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home">
                        <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                        <polyline points="9 22 9 12 15 12 15 22"></polyline>
                    </svg>
                    Dashboard <span class="sr-only">(current)</span>
                </a>
            </li>

<!--引入側(cè)邊欄;傳入?yún)?shù)-->
<div th:replace="commons/bar::#sidebar(activeUri='emps')"></div>

6)谭期、CRUD-員工添加
添加頁(yè)面

<form>
    <div class="form-group">
        <label>LastName</label>
        <input type="text" class="form-control" placeholder="zhangsan">
    </div>
    <div class="form-group">
        <label>Email</label>
        <input type="email" class="form-control" placeholder="zhangsan@atguigu.com">
    </div>
    <div class="form-group">
        <label>Gender</label><br/>
        <div class="form-check form-check-inline">
            <input class="form-check-input" type="radio" name="gender"  value="1">
            <label class="form-check-label">男</label>
        </div>
        <div class="form-check form-check-inline">
            <input class="form-check-input" type="radio" name="gender"  value="0">
            <label class="form-check-label">女</label>
        </div>
    </div>
    <div class="form-group">
        <label>department</label>
        <select class="form-control">
            <option>1</option>
            <option>2</option>
            <option>3</option>
            <option>4</option>
            <option>5</option>
        </select>
    </div>
    <div class="form-group">
        <label>Birth</label>
        <input type="text" class="form-control" placeholder="zhangsan">
    </div>
    <button type="submit" class="btn btn-primary">添加</button>
</form>

提交的數(shù)據(jù)格式不對(duì):生日:日期踏志;
2017-12-12胀瞪;2017/12/12;2017.12.12圆雁;
日期的格式化帆谍;SpringMVC將頁(yè)面提交的值需要轉(zhuǎn)換為指定的類型;
2017-12-12---Date汛蝙; 類型轉(zhuǎn)換,格式化;
默認(rèn)日期是按照/的方式坚洽;
7)西土、CRUD-員工修改
修改添加二合一表單

<!--需要區(qū)分是員工修改還是添加;-->
<form th:action="@{/emp}" method="post">
    <!--發(fā)送put請(qǐng)求修改員工數(shù)據(jù)-->
    <!--
1绘雁、SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot自動(dòng)配置好的)
2、頁(yè)面創(chuàng)建一個(gè)post表單
3欣除、創(chuàng)建一個(gè)input項(xiàng),name="_method";值就是我們指定的請(qǐng)求方式
-->
    <input type="hidden" name="_method" value="put" th:if="${emp!=null}"/>
    <input type="hidden" name="id" th:if="${emp!=null}" th:value="${emp.id}">
    <div class="form-group">
        <label>LastName</label>
        <input name="lastName" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${emp.lastName}">
    </div>
    <div class="form-group">
        <label>Email</label>
        <input name="email" type="email" class="form-control" placeholder="zhangsan@atguigu.com" th:value="${emp!=null}?${emp.email}">
    </div>
    <div class="form-group">
        <label>Gender</label><br/>
        <div class="form-check form-check-inline">
            <input class="form-check-input" type="radio" name="gender" value="1" th:checked="${emp!=null}?${emp.gender==1}">
            <label class="form-check-label">男</label>
        </div>
        <div class="form-check form-check-inline">
            <input class="form-check-input" type="radio" name="gender" value="0" th:checked="${emp!=null}?${emp.gender==0}">
            <label class="form-check-label">女</label>
        </div>
    </div>
    <div class="form-group">
        <label>department</label>
        <!--提交的是部門的id-->
        <select class="form-control" name="department.id">
            <option th:selected="${emp!=null}?${dept.id == emp.department.id}" th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option>
        </select>
    </div>
    <div class="form-group">
        <label>Birth</label>
        <input name="birth" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}">
    </div>
    <button type="submit" class="btn btn-primary" th:text="${emp!=null}?'修改':'添加'">添加</button>
</form>

8)、CRUD-員工刪除

<tr th:each="emp:${emps}">
    <td th:text="${emp.id}"></td>
    <td>[[${emp.lastName}]]</td>
    <td th:text="${emp.email}"></td>
    <td th:text="${emp.gender}==0?'女':'男'"></td>
    <td th:text="${emp.department.departmentName}"></td>
    <td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"></td>
    <td>
        <a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">編輯</a>
        <button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">刪除</button>
    </td>
</tr>


<script>
    $(".deleteBtn").click(function(){
        //刪除當(dāng)前員工的
        $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
        return false;
    });
</script>

四、錯(cuò)誤處理機(jī)制

1)禽拔、SpringBoot默認(rèn)的錯(cuò)誤處理機(jī)制

默認(rèn)效果:
?1)、瀏覽器硫惕,返回一個(gè)默認(rèn)的錯(cuò)誤頁(yè)面恼除,瀏覽器發(fā)送請(qǐng)求的請(qǐng)求頭:
2)、如果是其他客戶端令野,默認(rèn)響應(yīng)一個(gè)json數(shù)據(jù)
原理:
? 可以參照ErrorMvcAutoConfiguration徽级;錯(cuò)誤處理的自動(dòng)配置;
給容器中添加了以下組件
? 1堵幽、DefaultErrorAttributes:

幫我們?cè)陧?yè)面共享信息弹澎;
@Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
            boolean includeStackTrace) {
        Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
        errorAttributes.put("timestamp", new Date());
        addStatus(errorAttributes, requestAttributes);
        addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
        addPath(errorAttributes, requestAttributes);
        return errorAttributes;
    }

2苦蒿、BasicErrorController:處理默認(rèn)/error請(qǐng)求

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
    
    @RequestMapping(produces = "text/html")//產(chǎn)生html類型的數(shù)據(jù);瀏覽器發(fā)送的請(qǐng)求來(lái)到這個(gè)方法處理
    public ModelAndView errorHtml(HttpServletRequest request,
            HttpServletResponse response) {
        HttpStatus status = getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
                request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        
        //去哪個(gè)頁(yè)面作為錯(cuò)誤頁(yè)面团滥;包含頁(yè)面地址和頁(yè)面內(nèi)容
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
    }

    @RequestMapping
    @ResponseBody    //產(chǎn)生json數(shù)據(jù)灸姊,其他客戶端來(lái)到這個(gè)方法處理秉溉;
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = getStatus(request);
        return new ResponseEntity<Map<String, Object>>(body, status);
    }

3召嘶、ErrorPageCustomizer:

@Value("${error.path:/error}")
    private String path = "/error";  系統(tǒng)出現(xiàn)錯(cuò)誤以后來(lái)到error請(qǐng)求進(jìn)行處理;(web.xml注冊(cè)的錯(cuò)誤頁(yè)面規(guī)則)

4甲喝、DefaultErrorViewResolver

@Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
            Map<String, Object> model) {
        ModelAndView modelAndView = resolve(String.valueOf(status), model);
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
            modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
        }
        return modelAndView;
    }

    private ModelAndView resolve(String viewName, Map<String, Object> model) {
        //默認(rèn)SpringBoot可以去找到一個(gè)頁(yè)面铛只?  error/404
        String errorViewName = "error/" + viewName;
        
        //模板引擎可以解析這個(gè)頁(yè)面地址就用模板引擎解析
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
                .getProvider(errorViewName, this.applicationContext);
        if (provider != null) {
            //模板引擎可用的情況下返回到errorViewName指定的視圖地址
            return new ModelAndView(errorViewName, model);
        }
        //模板引擎不可用,就在靜態(tài)資源文件夾下找errorViewName對(duì)應(yīng)的頁(yè)面   error/404.html
        return resolveResource(errorViewName, model);
    }

步驟:
? 一但系統(tǒng)出現(xiàn)4xx或者5xx之類的錯(cuò)誤诵冒;ErrorPageCustomizer就會(huì)生效(定制錯(cuò)誤的響應(yīng)規(guī)則)谊惭;就會(huì)來(lái)到/error請(qǐng)求;就會(huì)被
BasicErrorController處理豹芯;
? 1)響應(yīng)頁(yè)面驱敲;去哪個(gè)頁(yè)面是由
DefaultErrorViewResolver解析得到的众眨;

protected ModelAndView resolveErrorView(HttpServletRequest request,
      HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
    //所有的ErrorViewResolver得到ModelAndView
   for (ErrorViewResolver resolver : this.errorViewResolvers) {
      ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
      if (modelAndView != null) {
         return modelAndView;
      }
   }
   return null;
}

2)、如何定制錯(cuò)誤響應(yīng):

1)沿腰、如何定制錯(cuò)誤的頁(yè)面狈定;
? (1)、有模板引擎的情況下措嵌;error/狀態(tài)碼; 【將錯(cuò)誤頁(yè)面命名為 錯(cuò)誤狀態(tài)碼.html 放在模板引擎文件夾里面的 error文件夾下】芦缰,發(fā)生此狀態(tài)碼的錯(cuò)誤就會(huì)來(lái)到 對(duì)應(yīng)的頁(yè)面让蕾;
? 我們可以使用4xx和5xx作為錯(cuò)誤頁(yè)面的文件名來(lái)匹配這種類型的所有錯(cuò)誤,精確優(yōu)先(優(yōu)先尋找精確的狀態(tài)碼.html);
? 頁(yè)面能獲取的信息神帅;
? timestamp:時(shí)間戳
? status:狀態(tài)碼
? error:錯(cuò)誤提示
? exception:異常對(duì)象
? message:異常消息
? errors:JSR303數(shù)據(jù)校驗(yàn)的錯(cuò)誤都在這里
? (2)找御、沒(méi)有模板引擎(模板引擎找不到這個(gè)錯(cuò)誤頁(yè)面)绍填,靜態(tài)資源文件夾下找讨永;
? (3)遇革、以上都沒(méi)有錯(cuò)誤頁(yè)面,就是默認(rèn)來(lái)到SpringBoot默認(rèn)的錯(cuò)誤提示頁(yè)面锻霎;
2)揪漩、如何定制錯(cuò)誤的json數(shù)據(jù);
(1)冰更、自定義異常處理&返回定制json數(shù)據(jù)昂勒;

@ControllerAdvice
public class MyExceptionHandler {

    @ResponseBody
    @ExceptionHandler(UserNotExistException.class)
    public Map<String,Object> handleException(Exception e){
        Map<String,Object> map = new HashMap<>();
        map.put("code","user.notexist");
        map.put("message",e.getMessage());
        return map;
    }
}
//沒(méi)有自適應(yīng)效果...

(2)叁怪、轉(zhuǎn)發(fā)到/error進(jìn)行自適應(yīng)響應(yīng)效果處理

@ExceptionHandler(UserNotExistException.class)
    public String handleException(Exception e, HttpServletRequest request){
        Map<String,Object> map = new HashMap<>();
        //傳入我們自己的錯(cuò)誤狀態(tài)碼  4xx 5xx,否則就不會(huì)進(jìn)入定制錯(cuò)誤頁(yè)面的解析流程
        /**
         * Integer statusCode = (Integer) request
         .getAttribute("javax.servlet.error.status_code");
         */
        request.setAttribute("javax.servlet.error.status_code",500);
        map.put("code","user.notexist");
        map.put("message",e.getMessage());
        //轉(zhuǎn)發(fā)到/error
        return "forward:/error";
    }

(3)涣觉、將我們的定制數(shù)據(jù)攜帶出去血柳;
出現(xiàn)錯(cuò)誤以后难捌,會(huì)來(lái)到/error請(qǐng)求,會(huì)被BasicErrorController處理员淫,響應(yīng)出去可以獲取的數(shù)據(jù)是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)規(guī)定的方法)击敌;
? 1、完全來(lái)編寫一個(gè)ErrorController的實(shí)現(xiàn)類【或者是編寫AbstractErrorController的子類】圣蝎,放在容器中;
? 2牲证、頁(yè)面上能用的數(shù)據(jù)关面,或者是json返回能用的數(shù)據(jù)都是通過(guò)errorAttributes.getErrorAttributes得到缭裆;
3、容器中DefaultErrorAttributes.getErrorAttributes()辛燥;默認(rèn)進(jìn)行數(shù)據(jù)處理的缝其;自定義ErrorAttributes
//給容器中加入我們自己定義的ErrorAttributes
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {

@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
    Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
    map.put("company","atguigu");
    return map;
}

}
最終的效果:響應(yīng)是自適應(yīng)的内边,可以通過(guò)定制ErrorAttributes改變需要返回的內(nèi)容

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嘴高,隨后出現(xiàn)的幾起案子拴驮,更是在濱河造成了極大的恐慌柴信,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件潜沦,死亡現(xiàn)場(chǎng)離奇詭異唆鸡,居然都是意外死亡枣察,警方通過(guò)查閱死者的電腦和手機(jī)袄琳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)刻蟹,“玉大人嘿辟,你說(shuō)我怎么就攤上這事∮⒐牛” “怎么了昙读?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵蛮浑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我艺沼,道長(zhǎng)蕴掏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮饶唤,結(jié)果婚禮上募狂,老公的妹妹穿的比我還像新娘。我一直安慰自己性穿,他們只是感情好雷滚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呆万,像睡著了一般谋减。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上出爹,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天严就,我揣著相機(jī)與錄音,去河邊找鬼渐行。 笑死铸董,一個(gè)胖子當(dāng)著我的面吹牛袒炉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播孽文,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼夺艰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼郁副!你這毒婦竟也來(lái)了减牺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤存谎,失蹤者是張志新(化名)和其女友劉穎拔疚,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體既荚,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡稚失,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恰聘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片句各。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖凿宾,靈堂內(nèi)的尸體忽然破棺而出矾屯,到底是詐尸還是另有隱情,我是刑警寧澤初厚,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布问拘,位于F島的核電站,受9級(jí)特大地震影響惧所,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绪杏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一下愈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蕾久,春花似錦势似、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至盹愚,卻和暖如春栅迄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背皆怕。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工毅舆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人愈腾。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓憋活,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親虱黄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悦即,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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