【8】SpringBoot的web开发

一、默认访问首页

使用WebMvcConfigurerAdapter(基于SpringBoot 1.x)可以来扩展SpringMVC的功能,详情查看【7】SpringBoot的SpringMVC自动配置 - 第三节

//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
//@EnableWebMvc   不要接管SpringMVC
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    //所有的WebMvcConfigurerAdapter组件都会一起起作用
    @Bean //将组件注册在容器
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
        WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                registry.addViewController("/").setViewName("login");
                registry.addViewController("/index.html").setViewName("login");
            }
        };
        return adapter;
    }
}

二、国际化

SpringMVC步骤:

  1. 编写国际化配置文件;
  2. 使用ResourceBundleMessageSource管理国际化资源文件
  3. 在页面使用fmt:message取出国际化内容

SpringBoot步骤:

  1. 编写国际化配置文件

编写国际化配置文件

使用IDEA Resouces Bundle视图编写,点击查看教程
在这里插入图片描述

SpringBoot的国际化自动配置

SpringBoot自动配置好了管理国际化资源文件的组件,源码:

@ConfigurationProperties(prefix = "spring.messages")
public class MessageSourceAutoConfiguration {
    
    /**
	 * Comma-separated list of basenames (essentially a fully-qualified classpath
	 * location), each following the ResourceBundle convention with relaxed support for
	 * slash based locations. If it doesn't contain a package qualifier (such as
	 * "org.mypackage"), it will be resolved from the classpath root.
	 */
	private String basename = "messages";  
    //我们的配置文件可以直接放在类路径下叫messages.properties,能被SpringBoot直接识别
    
    @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;
	}

由上面源码可知,国际化配置文件的默认文件夹是classpath下的messages文件夹,如放在别的位置,需要指定国际化文件的位置

spring.messages.basename=*
#如我放在了resources文件夹下的i18n下
spring.messages.basename=i18n/login

页面使用

#{login.tip}

切换语言环境

  • SpringBoot默认就有区域信息解析器(LocaleResolver),根据当前浏览器环境自动切换
//WebMvcAutoConfigutation中的源码
@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;
}
  • 点击链接切换国际化

步骤:

  1. 在链接上携带国际化信息,如:xxx.html?lang=zh_CN
  2. 编写自己的区域信息解析器(LocaleResolver)
  3. 将自己的区域信息解析器(LocaleResolver),添加到容器中
  1. 在链接上携带国际化信息

<a class="btn btn-sm" th:href="@{/index.html(lang='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(lang='en_US')}">English</a>

  1. 编写自己的区域信息解析器
public class MyLocaleResolver implements LocaleResolver {
    //解析区域信息
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        //获取请求中的语言信息
        String lang = request.getParameter("lang");
        //构建默认Locale对象
        Locale locale = new Locale("zh", "CN");
        if (!StringUtils.isEmpty(lang)){
            String[] split = lang.split("_");
            locale = new Locale(split[0], split[1]);
        }
        return locale;
    }
    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
    }
}
  1. 将自己的区域信息解析器(LocaleResolver),添加到容器中
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
	/**
	 * 讲自己写的区域信息解析器添加到容器中
	 */
	@Bean
	public LocaleResolver localeResolver(){
	    return new MyLocaleResolver();
	}
}

三、登录

使用th:action标签提交表单数据

<form th:action="@{/user/login}" method="post">

添加视图解析器

@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    //所有的WebMvcConfigurerAdapter组件都会一起起作用
    @Bean //将组件注册在容器
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
        WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("login");
            registry.addViewController("/index.html").setViewName("login");
            registry.addViewController("/main.html").setViewName("dashboard");
            }
        };
        return adapter;
    }
}

编写Controller

@PostMapping(value = "/user/login")
public String login(@RequestParam("username") String username,
                    @RequestParam("password") String password,
                    Map<String,Object> map, HttpSession session){
    if(!StringUtils.isEmpty(username) && "123456".equals(password)){
        //登陆成功,防止表单重复提交,可以重定向到主页
        session.setAttribute("loginUser",username);
        return "redirect:/main.html";
    }else{
        //登陆失败
        map.put("msg","用户名密码错误");
        return  "login";
    }
}

注册拦截器,检查登录状态

@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    //注册拦截器
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
	    //静态资源;  *.css , *.js
	    //SpringBoot已经做好了静态资源映射
	    registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
	    	//排除拦截请求
            .excludePathPatterns("/index.html","/","/user/login");
		}};
      	return adapter;
  	}
}

四、错误处理机制

SpringBoot默认的错误处理机制

SpringBoot在ErrorMvcAutoConfiguration中已经添加了一些组件帮我们做了一些错误处理机制

主要有以下几种:

  1. DefaultErrorAttributes
  2. BasicErrorController
  3. ErrorPageCustomizer
  4. DefaultErrorViewResolver

处理逻辑

  1. 将请求转发到BasicErrorController控制器来处理请求,
  2. 浏览器请求响应BasicErrorController的errorHtml()方法,APP等客户端响应error()方法
  3. 以浏览器的404错为例:最终返回一个modelAndView
    3-1. 调用BasicErrorControllererrorHtml(HttpServletRequest request, HttpServletResponse response)方法,其中status=404
    3-2. 调用AbstractErrorControllerresolveErrorView方法,遍历ErrorMvcAutoConfiguration.errorViewResolvers,寻找需要的modelAndView;
    3-3. ErrorMvcAutoConfiguration.errorViewResolvers会有一个默认的DefaultErrorViewResolver,于是便执行DefaultErrorViewResolver.resolveErrorView()方法;
    3-4. DefaultErrorViewResolver.resolveErrorView()的具体实现:调用当前的this.resolve(status, model),创建modelAndView;//即寻找error/404页面
    3-5. 如果创建error/404视图失败(即找不到error/404视图),则创建error/4XX视图;否则,继续创建视图;
    3-6. 如果创建error/4XX视图失败(即找不到error/4XX视图),则创建默认名为error的视图,而error视图在静态累WhitelabelErrorViewConfiguration中进行配置和加载(即Springboot默认的Whitelabel Error Page页面);
    3-7. 根据实际获取到的视图(ModalAndView),进行渲染

默认的发生错误,它会将请求转发到BasicErrorController控制器来处理请求,下面是该controller类的源码:

@Controller
//如果server.error.path未配置,则跳转到error方法
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
    private final ErrorProperties errorProperties;

    public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
        this(errorAttributes, errorProperties, Collections.emptyList());
    }

    public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorViewResolvers);
        Assert.notNull(errorProperties, "ErrorProperties must not be null");
        this.errorProperties = errorProperties;
    }

    public String getErrorPath() {
        return this.errorProperties.getPath();
    }
	
	/*
	映射浏览器发送来的请求
	*/
    @RequestMapping(
        produces = {"text/html"}
    )
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        return modelAndView == null ? new ModelAndView("error", model) : modelAndView;
    }
	
	/*
	不是浏览器而是其他软件app客户端发送的错误请求
	*/
    @RequestMapping
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = this.getStatus(request);
        return new ResponseEntity(body, status);
    }

    protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
        IncludeStacktrace include = this.getErrorProperties().getIncludeStacktrace();
        if (include == IncludeStacktrace.ALWAYS) {
            return true;
        } else {
            return include == IncludeStacktrace.ON_TRACE_PARAM ? this.getTraceParameter(request) : false;
        }
    }

    protected ErrorProperties getErrorProperties() {
        return this.errorProperties;
    }
}
  • errorHtml:映射浏览器发送来的错误请求,产生HTML类型的数据
    浏览器发送的错误请求
  • error:映射不是浏览器而是其他软件app客户端发送的错误请求,产生JSON的数据
    其他客户端发送的错误请求
定制错误的页面
有模版引擎

在模版引擎src/main/resources/templates路径下新建文件夹error文件夹,然后新建4xx.html和5xx.html页面即可,当然也可以定制精确状态码页面例如400.html、404.html、500.html等,并遵循精确优先原则
在这里插入图片描述
演示效果:

以上默认返回视图(ModalAndView),会返回一些状态信息,主要有以下几种:

  • timestamp:时间戳
  • status:状态码
  • error:错误提示
  • exception:异常对象
  • message:异常消息
  • errors:JSR303数据校验的错误都在这里
    我们也可以在错误页面中,根据返回的状态信息,在错误页面输出内容:
<h3 color="red">自定义的错误页面4xx: [[${{status}}]]</h3>
<h3 color="red">错误提示: [[${{error}}]]</h3>
<h3 color="red">时间戳: [[${{timestamp}}]]</h3>

效果展示:
在这里插入图片描述

没有模版引擎

模板引擎找不到这个错误页面,默认在静态资源文件夹下找error/错误代码.html页面。
如果也没找到,显示SpringBoot默认的错误页面

定制错误的数据
自定义异常处理&返回定制json数据
  • 创建自定义异常:
public class UserNotExistException extends RuntimeException {
    public UserNotExistException() {
        super("用户不存在");
    }
}
  • 创建异常处理Controller:
@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;
    }
}
  • 编写请求方法模拟出异常:
@ResponseBody
@RequestMapping("/test_request_error")
public String testRequestError(@RequestParam("user") String user) {
    if (user.equals("aaa")){
        throw new UserNotExistException();
    }
    return null;
}

测试结果:
在这里插入图片描述

转发到/error进行自适应响应效果处理

如果需要让错误转发到相应的错误页面,只需要传入错误状态码(必须传入4xx/5xx),让SpringBoot默认的错误处理帮我们解析错误就可以了:

@ControllerAdvice
public class MyExceptionHandler {
    @ResponseBody
    @ExceptionHandler(UserNotExistException.class)
    public Map<String,Object> handleException(Exception e){
        Map<String,Object> map = new HashMap<>();
        //传入我们自己的错误状态码  4xx 5xx,否则就不会进入定制错误页面的解析流程
        /**
         * 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());
        //转发到/error
        return "forward:/error";
    }
}
既要自适应处理,也要自定义数据

。。。未完

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值