默认访问首页 1.配置路由 在任意controller下添加路由,接收/*的请求然后返回index.html
@RequestMapping ({"/" ,"/index.html" })public String index () { return "index" ; }
@Configuration public class MyMVCconfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers (ViewControllerRegistry registry) { registry.addViewController("/zenshin" ).setViewName("index" ); } @Bean public WebMvcConfigurerAdapter webMvcConfigurerAdapter () { WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() { @Override public void addViewControllers (ViewControllerRegistry registry) { registry.addViewController("/" ).setViewName("index" ); registry.addViewController("/index.html" ).setViewName("index" ); } }; return adapter; } }
这两种方法都是将”/“路由转到index视图;
国际化 SpringMVC国际化需要这么几步
编写国际化配置文件
使用ResourceBundleMessageSource管理国际化资源文件
在页面使用fmt:message取出国际化内容。
SpringBoot实现国际化
编写国际化配置文件
SpringBoot自动配置好了管理资源文件组件
@ConfigurationProperties (prefix = "spring.messages" )public class MessageSourceAutoConfiguration { private String basename = "messages" ; @Bean public MessageSource messageSource () { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); if (StringUtils.hasText(this .basename)) { messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace(this .basename))); } if (this .encoding != null ) { messageSource.setDefaultEncoding(this .encoding.name()); } messageSource.setFallbackToSystemLocale(this .fallbackToSystemLocale); messageSource.setCacheSeconds(this .cacheSeconds); messageSource.setAlwaysUseMessageFormat(this .alwaysUseMessageFormat); return messageSource; } }
<body class ="text-center" > <form class ="form-signin" action ="dashboard.html" > <img class ="mb-4" src ="asserts/img/bootstrap-solid.svg" alt ="" width ="72" height ="72" > <h1 class ="h3 mb-3 font-weight-normal" th:text ="#{login.tip}" > </h1 > <label class ="sr-only" th:text ="#{login.username}" > </label > <input type ="text" class ="form-control" th:placeholder ="#{login.username}" required ="" autofocus ="" > <label class ="sr-only" th:text ="#{login.password}" > </label > <input type ="password" class ="form-control" th:placeholder ="#{login.password}" required ="" > <div class ="checkbox mb-3" > <label > <input type ="checkbox" value ="remember-me" /> [[#{login.Rememberme}]] </label > </div > <button class ="btn btn-lg btn-primary btn-block" type ="submit" th:text ="#{login.Signin}" > </button > <p class ="mt-5 mb-3 text-muted" > © 2017-2018</p > <a class ="btn btn-sm" > 中文</a > <a class ="btn btn-sm" > English</a > </form > </body >
效果:根据浏览器语言设置的信息切换国际化 这样可能会出现乱码的现象,是因为idea没有给文件配置编码信息,没有转为ascii。 我们需要在IDEA中进行设置,File->Other Setting -> Setting for new project 这样以后新创建的项目就会变为这个配置
SpringBoot实现国际化原理
国际化能有效是因为有个一个对象为Local(区域信息对象)->有一个组件叫做LocaleResolver(获取区域信息对象) SpringBoot中有一配置类:WebMvcAutoConfiguration,下面有一个Bean叫做localeResolver区域信息解析器
@Bean @ConditionalOnMissingBean @ConditionalOnProperty (prefix = "spring.mvc" , name = "locale" )public LocaleResolver localeResolver () { if (this .mvcProperties .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) { return new FixedLocaleResolver(this .mvcProperties.getLocale()); } AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver(); localeResolver.setDefaultLocale(this .mvcProperties.getLocale()); return localeResolver; } @Override public Locale resolveLocale (HttpServletRequest request) { Locale defaultLocale = getDefaultLocale(); if (defaultLocale != null && request.getHeader("Accept-Language" ) == null ) { return defaultLocale; } Locale requestLocale = request.getLocale(); if (isSupportedLocale(requestLocale)) { return requestLocale; } Locale supportedLocale = findSupportedLocale(request); if (supportedLocale != null ) { return supportedLocale; } return (defaultLocale != null ? defaultLocale : requestLocale); }
SpringBoot默认的区域信息解析器是根据请求头的信息进行解析的 如果我们想要用自己的方式进行国际化,可以对区域解析器进行重写
实现自己的区域解析器
public class MylocaleResolver implements LocaleResolver { @Override public Locale resolveLocale (HttpServletRequest request) { String l = request.getParameter("l" ); Locale locale = Locale.getDefault(); if (!StringUtils.isEmpty(l)) { String[] spl = l.split("_" ); locale = new Locale(spl[0 ],spl[1 ]); } return locale; } @Override public void setLocale (HttpServletRequest request, HttpServletResponse response, Locale locale) { } }
@Bean public LocaleResolver localeResolver () { return new MylocaleResolver(); }
<a class ="btn btn-sm" th:href ="@{/index.html(l='zh_CN')}" > 中文</a > <a class ="btn btn-sm" th:href ="@{/index.html(l='en_US')}" > English</a >
这样就实现的点击切换
登陆
开发期间模板引擎页面修改以后,要实时生效
禁用模板引擎缓存
页面修改完以后ctrl+f9重新编译一下
spring.thymeleaf.cache =false
登陆操作
<body class ="text-center" > <form class ="form-signin" th:action ="@{/user/login}" method ="post" > <img class ="mb-4" th:src ="@{/asserts/img/bootstrap-solid.svg}" alt ="" width ="72" height ="72" > <h1 class ="h3 mb-3 font-weight-normal" th:text ="#{login.tip}" > </h1 > <p style ="color: red" th:text ="${msg}" th:if ="${not #strings.isEmpty(msg)}" > </p > <label class ="sr-only" th:text ="#{login.username}" > </label > <input type ="text" name ="username" class ="form-control" th:placeholder ="#{login.username}" required ="" autofocus ="" > <label class ="sr-only" th:text ="#{login.password}" > </label > <input type ="password" name ="password" class ="form-control" th:placeholder ="#{login.password}" required ="" > <div class ="checkbox mb-3" > <label > <input type ="checkbox" value ="remember-me" /> [[#{login.Rememberme}]] </label > </div > <button class ="btn btn-lg btn-primary btn-block" type ="submit" th:text ="#{login.Signin}" > </button > <p class ="mt-5 mb-3 text-muted" > © 2017-2018</p > <a class ="btn btn-sm" th:href ="@{/index.html(l='zh_CN')}" > 中文</a > <a class ="btn btn-sm" th:href ="@{/index.html(l='en_US')}" > English</a > </form > </body >
服务端接收前端信息 创建LoginController类,然后添加路由方法
@Controller public class LoginController { @PostMapping (value = "/user/login" ) public String login (@RequestParam("username" ) String username, @RequestParam ("password" ) String password, Map<String,Object> map) { if (!StringUtils.isEmpty(username) && "123456" .equals(password)) { return "dashboard" ; } else { map.put("msg" ,"用户名或密码错误" ); return "index" ; } } }
跳转到页面后,浏览器还是显示提交链接,需要重定向 重定向其实是又请求了一次页面,所有这里分两步实现重定向功能
@Bean public WebMvcConfigurerAdapter webMvcConfigurerAdapter () { WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() { @Override public void addViewControllers (ViewControllerRegistry registry) { registry.addViewController("/" ).setViewName("index" ); registry.addViewController("/index.html" ).setViewName("index" ); registry.addViewController("/main" ).setViewName("dashboard" ); } }; return adapter; }
if (!StringUtils.isEmpty(username) && "123456" .equals(password)){ return "redirect:/main" ; }
利用拦截器进行登陆检查
要使用拦截器需要定义一个类然后实现HandlerInterceptor接口
public class LoginHeadlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user = request.getSession().getAttribute("loginUser" ); if (user==null ) { request.setAttribute("msg" ,"没有权限请先登陆" ); request.getRequestDispatcher("/" ).forward(request,response); return false ; } else { return true ; } } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
注册HandlerInterceptor到WebMvcConfigurerAdapter中
@Bean public WebMvcConfigurerAdapter webMvcConfigurerAdapter () { @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(new LoginHeadlerInterceptor()) .addPathPatterns("/**" ) .excludePathPatterns("/" ,"index.html" ,"/user/login" ); } }; return adapter; }
这样拦截器就会生效,没有登陆就进不去main页面
<a class ="navbar-brand col-sm-3 col-md-2 mr-0" href ="#" > [[${session.loginUser}]]</a >
员工列表功能-CRUD Restful-CRUD
CRUD满足Restful风格 URI: /资源名称/资源标识 HTTP请求方式区分对资源CRUD操作
普通CRUD(uri来区分操作)
RestfulCRUD
查询
getEmp
emp—GET
添加
addEmp?xxx
emp—POST
修改
updateEmp?id=xxx&xxx=xx
emp/{id}—PUT
删除
deleteEmp?id=1
emp/{id}—DELETE
请求的架构
实验功能
请求URI
请求方式
查询所有员工
emps
GET
查询某个员工(来到修改页面)
emp/{id}
GET
来到添加页面
emp
GET
添加员工
emp
POST
来到修改页面(查出员工进行信息回显)
emp/{id}
GET
修改员工
emp
PUT
删除员工
emp/{id}
DELETE
thymeleaf抽取公共元素 抽取公共片段 <div th:fragment ="copy" > © 2011 The Good Thymes Virtual Grocery</div >
引入公共片段 <div th:insert ="~{footer :: copy}" > </div >
默认效果:
insert的公共片段在div标签中 如果使用th:insert等属性进行引入,可以不用写~{}: 行内写法可以加上:[[~{}]];[(~{})];
三种引入公共片段的th属性:
th:insert:将公共片段整个插入到声明引入的元素中
th:replace:将声明引入的元素替换为公共片段
th:include:将被引入的片段的内容包含进这个标签中 效果
<footer th:fragment ="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 > © 2011 The Good Thymes Virtual Grocery </footer > </div > <footer > © 2011 The Good Thymes Virtual Grocery</footer > <div > © 2011 The Good Thymes Virtual Grocery</div >
例子
公共部分
<nav class ="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment ="topbar" id ="topbar" > <a class ="navbar-brand col-sm-3 col-md-2 mr-0" href ="#" > [[${session.loginUser}]]</a > <input class ="form-control form-control-dark w-100" type ="text" placeholder ="Search" aria-label ="Search" > <ul class ="navbar-nav px-3" > <li class ="nav-item text-nowrap" > <a class ="nav-link" href ="http://getbootstrap.com/docs/4.0/examples/dashboard/#" > Sign out</a > </li > </ul > </nav >
使用部分
<div th:replace ="dashboard::topbar" > </div > <div th:replace ="dashboard::#topbar" >
引入片段传递参数
<a th:class ="${activeUri == 'emps' ? 'nav-link active' : 'nav-link'}" th:href ="@{/emps}" >
<div th:replace ="commons/bar::#sidebar(activeUri='emps')" > </div >
员工列表 后续完整代码会在后面展示
员工添加功能 点击员工添加按钮跳转到添加页面
@GetMapping ("/emp" )public String toAddPage (Model model) { Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("depts" ,departments); return "emp/add" ; }
前端页面代码
<a class ="btn btn-success" th:href ="@{/emp}" > 员工添加</a >
前端页面 <form th:action ="@{/emp}" method ="post" > <div class ="form-group" > <label > LastName</label > <input name ="lastName" type ="text" class ="form-control" placeholder ="zhangsan" > </div > <div class ="form-group" > <label > Email</label > <input name ="email" 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 name ="department.id" class ="form-control" > <option th:value ="${dept.getId()}" th:each ="dept:${depts}" th:text ="${dept.getDepartmentName()}" > </option > </select > </div > <div class ="form-group" > <label > Birth</label > <input name ="birth" type ="text" class ="form-control" placeholder ="zhangsan" > </div > <button type ="submit" class ="btn btn-primary" > 添加</button > </form >
链接到后端代码 @PostMapping ("/emp" )public String addEmp (Employee employee) { employeeDao.save(employee); return "redirect:/emps" ; }
这里的日期格式需要注意一下,日期格式。SpringBoot默认的日期格式是dd/MM/yyyy,如果修改,那么可以修改配置文件进行修改。
spring.mvc.date-format=yyyy-MM-dd HH:mm
当然可以通过前端表单配合实现。
员工修改功能 点击员工编辑按钮跳转到修改页面(页面与添加页面相同),然后将要修改的人的信息显示出来 编辑按钮代码
<a class ="btn btn-sm btn-primary" th:href ="@{/emp/}+${emp.getId()}" > 编辑</a >
这样就会带着员工id去当问后端,那么后端代码如何接收整个请求呢,
@GetMapping ("/emp/{id}" )public String ToEditPage (@PathVariable("id" ) Integer id,Model model) { Employee employee = employeeDao.get(id); model.addAttribute("emp" ,employee); Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("depts" ,departments); return "emp/add" ; }
将该人员得信息返回到前端以后,需要改造前端代码才能将信息显示出来,因为这里是与添加在一个表单中所以我们需要做一些判断去与添加页面分开,并且我们在添加的时候需要使用post方式但是修改的时候需要使用put方式去实现修改,因为put是幂等的,post并不是幂等操作。但是表单是不支持put请求的所以为了支持Spring做了一些处理
<form th:action ="@{/emp}" method ="post" > <input type ="hidden" name ="_method" value ="put" th:if ="${emp!=null}" > <input type ="hidden" name ="id" th:if ="${emp!=null}" th:value ="${emp.getId()}" > <div class ="form-group" > <label > LastName</label > <input name ="lastName" type ="text" class ="form-control" placeholder ="zhangsan" th:value ="${emp!=null}?${emp.getLastName()}" > </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.getEmail()}" > </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.getGender()==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.getGender()==0}" > <label class ="form-check-label" > 女</label > </div > </div > <div class ="form-group" > <label > department</label > <select name ="department.id" class ="form-control" > <option th:selected ="${emp!=null}?${dept.getId() == emp.getDepartment().getId()}" th:value ="${dept.getId()}" th:each ="dept:${depts}" th:text ="${dept.getDepartmentName()}" > </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.getBirth(),'yyyy-MM-dd')}" > </div > <button type ="submit" class ="btn btn-primary" th:text ="${emp!=null}?'修改':'添加'" > 添加</button > </form >
这样点击修改后就会走put请求去请求/emp,后端根据前端返回去实现修改方法
@PutMapping ("/emp" )public String updateEmployee (Employee employee) { employeeDao.save(employee); return "redirect:/emps" ; }
至此,我们的员工修改功能就实现了。
员工删除功能 我们需要在之前删除按钮上做一些操作,
<form th:action ="@{/emp/}+${emp.getId()}" method ="post" > <input type ="hidden" name ="_method" value ="delete" > <button type ="submit" class ="btn btn-sm btn-danger" > 删除</button > </form >
后端需要一个方式进行接受请求,删除的方法
@DeleteMapping ("/emp/{id}" )public String deleteEmployee (@PathVariable("id" ) Integer id) { employeeDao.delete(id); return "redirect:/emps" ; }
因为一个按钮会出现一个表单,所以不建议这种方式实现,我们可以在外部放一个表单,然后利用js实现表单提交 1、将form提出来,但是不要写action,将其去掉
<form id = "deleteEmpForm" method ="post" > <input type ="hidden" name ="_method" value ="delete" > </form >
2、改造button
<button th:attr ="del_uri=@{/emp/}+${emp.getId()}" class ="btn btn-sm btn-danger deleteBtn" > 删除</button >
3、编写js
<script> $(".deleteBtn" ).click(function ( ) { $("#deleteEmpForm" ).attr("action" ,$(this ).attr("del_uri" )).submit(); }); </script>
这样就可以实现一个表单多个删除了。
错误处理机制 SpringBoot默认的错误处理机制 1、SpringBoot出现的默认效果是这样的,返回一个默认的错误页面
2、如果单纯访问链接,返回的错误是这样的(利用postman代替)
SpringBoot错误处理实现原理 在SpringBoot的自动配置的web文件夹中有一个ErrorMvcAutoConfiguration类,错误处理机制自动配置。 给容器中添加了如下重要组件 1、DefaultErrorAttributes
:帮我们在页面共享信息
@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
: 处理/error请求
@Controller @RequestMapping ("${server.error.path:${error.path:/error}}" )public class BasicErrorController extends AbstractErrorController { @RequestMapping (produces = "text/html" ) 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()); ModelAndView modelAndView = resolveErrorView(request, response, status, model); return (modelAndView == null ? new ModelAndView("error" , model) : modelAndView); } @RequestMapping @ResponseBody 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); } protected ModelAndView resolveErrorView (HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) { for (ErrorViewResolver resolver : this .errorViewResolvers) { ModelAndView modelAndView = resolver.resolveErrorView(request, status, model); if (modelAndView != null ) { return modelAndView; } } return null ; } }
3、ErrorPageCustomizer
: 系统出现错误以后来到error请求进行处理(web.xml注册的错误页面规则)
@Override public void registerErrorPages (ErrorPageRegistry errorPageRegistry) { ErrorPage errorPage = new ErrorPage(this .properties.getServletPrefix() + this .properties.getError().getPath()); errorPageRegistry.addErrorPages(errorPage); } @Value ("${error.path:/error}" )private String path = "/error" ;public String getPath () { return this .path; }
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) { String errorViewName = "error/" + viewName; TemplateAvailabilityProvider provider = this .templateAvailabilityProviders .getProvider(errorViewName, this .applicationContext); if (provider != null ) { return new ModelAndView(errorViewName, model); } return resolveResource(errorViewName, model); }
步骤: 一旦系统出现4XX或者5XX的错误的时候,ErrorPageCustomizer
就会生效,就会来到/error请求,然后就会来到BasicErrorController
的代码中,被BasicErrorController
处理,这里面获取了错误代码和错误原因以后通过ErrorViewResolver
得到一个ModelAndView
,去哪个页面是由DefaultErrorViewResolver
解析得到的。
定制错误处理 定制错误页面
如果有模板引擎找的是视图下的状态码 :error/状态码
将错误页面命名为错误状态码.html放在模板引擎文件加下的error文件夹下,发生此状态码的错误后来到对应的页面;
我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html)
没有模板引擎的时候,在静态资源文件下找error/404.html
模板引擎与静态资源都没有的时候来到SpringBoot默认的错位提示页面
@Configuration @ConditionalOnProperty (prefix = "server.error.whitelabel" , name = "enabled" , matchIfMissing = true )@Conditional (ErrorTemplateMissingCondition.class ) protected static class WhitelabelErrorViewConfiguration {private final SpelView defaultErrorView = new SpelView( "<html><body><h1>Whitelabel Error Page</h1>" + "<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>" + "<div id='created'>${timestamp}</div>" + "<div>There was an unexpected error (type=${error}, status=${status}).</div>" + "<div>${message}</div></body></html>" ); @Bean (name = "error" )@ConditionalOnMissingBean (name = "error" )public View defaultErrorView () { return this .defaultErrorView; }
页面能获取到的信息
timestamp:时间戳
status:错误状态码
error:错误提示
message:异常消息
errors:JSR303数据校验的错误
这些信息的获取是在DefaultErrorAttributes
中定义的@RequestMapping (produces = "text/html" )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()); ModelAndView modelAndView = resolveErrorView(request, response, status, model); return (modelAndView == null ? new ModelAndView("error" , model) : modelAndView); } protected Map<String, Object> getErrorAttributes (HttpServletRequest request, boolean includeStackTrace) { RequestAttributes requestAttributes = new ServletRequestAttributes(request); return this .errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace); }
DefaultErrorAttributes
中塞入的信息就是我们能够找到的信息
定制错误json
自定义异常处理&返回值定制json数据
编写自定义异常public class UserNotExistException extends RuntimeException { public UserNotExistException () { super ("用户不存在" ); } }
在特定位置抛出异常
编写Hendler@ControllerAdvice public class MyExceptionHendler { @ResponseBody @ExceptionHandler (UserNotExistException.class )//这里就获取到了具体得异常类型 public Map <String ,Object > handException (Exception e ) { Map<String,Object> map = new HashMap<>(); map.put("code" ,"user.notexit" ); map.put("message" ,e.getMessage()); return map; } }
变成自适应,就是把请求转发到error来进行自适应
@ControllerAdvice public class MyExceptionHendler { @ExceptionHandler (UserNotExistException.class ) public String handException (Exception e , HttpServletRequest request ) { Map<String,Object> map = new HashMap<>(); request.setAttribute("javax.servlet.error.status_code" ,400 ); map.put("code" ,"user.notexit" ); map.put("message" ,e.getMessage()); return "forward:/error" ; } }
将我们得定制数据携带出去 出现错误以后,会来到/error请求,会被BasicErrorController
处理,相应出去可以获取得数据是由getEooroAttributes()
得到的(是AbstractErrorController
规定的方法) 1、完全来编写一个ErrorController得实现类(或者编写继承AbstractErrorController的子类)放到容器中,因为BasicErrorController
的注册条件是如果有该类或者该类的子类注册了,就不会重复注册了。这里应该继承一下BasicErrorController
然后重写方法errorHtml()
和error
这样实现把自己的model写进去。但是这样太麻烦了。 2、页面上可以使用的数据或者是json能用的数据都是通过ErrorAttributes.getErrorAttributes()
得到的这里的ErrorAttributes是一个接口,同样的SpringBoot也是使用同样的方法注册了一个DefaultErrorAttributes
我们也可以通过实现ErrorAttributes
接口来自定义ErrorAttributes。
@ControllerAdvice public class MyExceptionHendler { @ExceptionHandler (UserNotExistException.class ) public String handException (Exception e , HttpServletRequest request ) { Map<String,Object> map = new HashMap<>(); request.setAttribute("javax.servlet.error.status_code" ,404 ); map.put("code" ,"user.notexit" ); map.put("message" ,e.getMessage()); request.setAttribute("ext" ,map); return "forward:/error" ; } } @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("name" ,"zenshin" ); Map<String, Object> ext = (Map<String, Object>) requestAttributes.getAttribute("ext" , SCOPE_REQUEST); map.put("ext" ,ext); return map; } }
最终的效果响应式自适应的,可以通过定制ErrorAttributes改变需要返回的model值