springboot修改html服务器,SpringBoot学习笔记十一:自定义 Web MVC配置

Spring MVC自动配置

当我们在项目中添加了spring-boot-starter-web依赖,Spring Boot会为Spring MVC提供自动配置,自动配置类为org.springframework.boot.autoconfigure.web.servlet包下的WebMvcAutoConfiguration

自动配置在Spring的默认值之上添加了以下功能:

配置InternalResourceViewResolver作为默认的视图解析器,包含ContentNegotiatingViewResolver和BeanNameViewResolver bean

支持提供静态资源,包括对WebJars的支持

自动注册Converter,GenericConverter和Formatter bean

支持HttpMessageConverters

自动注册MessageCodesResolver

静态index.html支持

自定义Favicon支持

自动使用ConfigurableWebBindingInitializer bean

如果要保留Spring Boot MVC功能并且想要添加其他 MVC配置(interceptors, formatters, view controllers以及其他功能),可以添加自己的类型为WebMvcConfigurer的配置类(以@Configuration注解标注),但不包含@EnableWebMvc。如果希望提供RequestMappingHandlerMapping,RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义实例,则可以声明WebMvcRegistrationsAdapter实例以提供此类组件。

如果想完全控制Spring MVC,可以使用@EnableWebMvc注解添加自己的配置类。

注意:Spring Boot 1.x时我们可以使用WebMvcConfigurerAdapter类来自定义Spring MVC配置,但Spring Boot 2.0已不推荐使用此类来进行自定义配置(已废弃),取而代之我们可以直接实现WebMvcConfigurer接口并重写相应方法来达到自定义Spring MVC配置的目的

大致原理就是WebMvcConfigurer接口基于java 8提供了默认方法,其实里面基本上也就是空实现

以下是WebMvcConfigurerAdapter的部分源码:

/**

* An implementation of {@link WebMvcConfigurer} with empty methods allowing

* subclasses to override only the methods they're interested in.

*

* @author Rossen Stoyanchev

* @since 3.1

* @deprecated as of 5.0 {@link WebMvcConfigurer} has default methods (made

* possible by a Java 8 baseline) and can be implemented directly without the

* need for this adapter

*/

@Deprecated

public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {

...

}

HttpMessageConverters

Spring MVC使用HttpMessageConverter接口来转换HTTP请求和响应。消息转换器是在HttpMessageConvertersAutoConfiguration类中自动注册的。org.springframework.boot.autoconfigure.http包下HttpMessageConverters相关配置类如下:

d0bddbf729af?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

消息转换器相关配置类

StringHttpMessageConverter是Spring Boot默认自动配置的HttpMessageConverter,除了默认的StringHttpMessageConverter,在HttpMessageConvertersAutoConfiguration配置类中还使用了@Import注解引入了JacksonHttpMessageConvertersConfiguration、GsonHttpMessageConvertersConfiguration、JsonbHttpMessageConvertersConfiguration,自动配置逻辑如下:

若jackson的相关jar包在类路径下,则通过JacksonHttpMessageConvertersConfiguration配置MappingJackson2HttpMessageConverter和MappingJackson2XmlHttpMessageConverter

若gson的相关jar包在类路径下且Jackson、Jsonb未依赖, 亦或gson的相关jar包在类路径下且设置了spring.http.converters.preferred-json-mapper属性值为gson,则通过GsonHttpMessageConvertersConfiguration配置GsonHttpMessageConverter

若JSON-B的相关jar包在类路径下且Jackson、Gson未依赖,亦或JSON-B的相关jar包在类路径下且设置了spring.http.converters.preferred-json-mapper属性值为jsonb,则通过JsonbHttpMessageConvertersConfiguration配置JsonbHttpMessageConverter

一般情况下,当我们在项目中添加了spring-boot-starter-web依赖,Spring Boot会默认引入jackson-core、jackson-databind、jackson-annotations依赖,但未引入jackson-dataformat-xml,因此会配置MappingJackson2HttpMessageConverter消息转换器。

最佳实践1:整合Gson

一般情况

排除Jackson依赖

org.springframework.boot

spring-boot-starter-web

com.fasterxml.jackson.core

jackson-core

com.fasterxml.jackson.core

jackson-databind

com.fasterxml.jackson.core

jackson-annotations

com.fasterxml.jackson.datatype

jackson-datatype-jdk8

com.fasterxml.jackson.datatype

jackson-datatype-jsr310

com.fasterxml.jackson.module

jackson-module-parameter-names

这里完全排除了spring-boot-starter-web引入的jackson相关依赖,一般情况下排除jackson-databind依赖即可

添加Gson依赖

com.google.code.gson

gson

配置Gson相关属性

Spring Boot关于Gson的自动配置类为GsonAutoConfiguration,相关配置属性封装在GsonProperties类中

src/main/resources/application.yml

spring:

gson:

date-format: yyyy-MM-dd HH:mm:ss

这里贴出所有gson配置属性

# GSON GsonProperties

spring.gson.date-format= # Format to use when serializing Date objects.

spring.gson.disable-html-escaping= # Whether to disable the escaping of HTML characters such as '', etc.

spring.gson.disable-inner-class-serialization= # Whether to exclude inner classes during serialization.

spring.gson.enable-complex-map-key-serialization= # Whether to enable serialization of complex map keys (i.e. non-primitives).

spring.gson.exclude-fields-without-expose-annotation= # Whether to exclude all fields from consideration for serialization or deserialization that do not have the "Expose" annotation.

spring.gson.field-naming-policy= # Naming policy that should be applied to an object's field during serialization and deserialization.

spring.gson.generate-non-executable-json= # Whether to generate non executable JSON by prefixing the output with some special text.

spring.gson.lenient= # Whether to be lenient about parsing JSON that doesn't conform to RFC 4627.

spring.gson.long-serialization-policy= # Serialization policy for Long and long types.

spring.gson.pretty-printing= # Whether to output serialized JSON that fits in a page for pretty printing.

spring.gson.serialize-nulls= # Whether to serialize null fields.

这里有个问题就是用上述这种排除Jackson来配置Gson的方法在项目引入spring-boot-starter-jpa依赖后会报如下错误:

java.lang.IllegalStateException: Failed to introspect Class [org.springframework.data.web.config.SpringDataWebConfiguration] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@4459eb14]

at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:659) ~[spring-core-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:556) ~[spring-core-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:541) ~[spring-core-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.util.ReflectionUtils.getUniqueDeclaredMethods(ReflectionUtils.java:599) ~[spring-core-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:718) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:659) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:627) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1489) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:419) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:389) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:510) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:502) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1198) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]

at org.springframework.boot.SpringApplication.getExitCodeFromMappedException(SpringApplication.java:892) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]

at org.springframework.boot.SpringApplication.getExitCodeFromException(SpringApplication.java:878) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]

at org.springframework.boot.SpringApplication.handleExitCode(SpringApplication.java:864) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]

at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:813) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]

at org.springframework.boot.SpringApplication.run(SpringApplication.java:341) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]

at org.springframework.boot.SpringApplication.run(SpringApplication.java:1258) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]

at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]

at com.example.springbootmvc.SpringBootMvcApplication.main(SpringBootMvcApplication.java:10) [classes/:na]

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]

at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]

at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]

at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.0.4.RELEASE.jar:2.0.4.RELEASE]

Caused by: java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/ObjectMapper

at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]

at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3119) ~[na:na]

at java.base/java.lang.Class.getDeclaredMethods(Class.java:2268) ~[na:na]

at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:641) ~[spring-core-5.0.8.RELEASE.jar:5.0.8.RELEASE]

... 25 common frames omitted

Caused by: java.lang.ClassNotFoundException: com.fasterxml.jackson.databind.ObjectMapper

at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) ~[na:na]

at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190) ~[na:na]

at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499) ~[na:na]

... 29 common frames omitted

以上错误暂时未找到解决方法

特殊情况(无法排除jackson依赖)

添加Gson依赖(同上)

设置preferred-json-mapper属性

spring:

http:

converters:

preferred-json-mapper: gson

设置preferred-json-mapper属性后依然可以使用spring.gson开头的属性在application.properties或application.yml文件中配置gson

最佳实践2:整合Fastjson

Fastjson简介

Fastjson是阿里巴巴旗下的一款开源json序列化与反序列化java类库,号称是java中最快的json类库

官方Github主页:https://github.com/alibaba/fastjson

Spring Boot整合Fastjson

添加依赖

com.alibaba

fastjson

1.2.49

集成Fastjson

参考自官方文档在 Spring 中集成 Fastjson

src/main/java/com/example/springbootmvc/config/WebMvcConfig.java

package com.example.springbootmvc.config;

import com.alibaba.fastjson.serializer.SerializerFeature;

import com.alibaba.fastjson.support.config.FastJsonConfig;

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;

import org.springframework.context.annotation.Configuration;

import org.springframework.http.converter.HttpMessageConverter;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration

public class WebMvcConfig implements WebMvcConfigurer {

@Override

public void extendMessageConverters(List> converters) {

// 创建Fastjson消息转换器

FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();

// 创建Fastjson配置对象

FastJsonConfig fastJsonConfig = new FastJsonConfig();

fastJsonConfig.setSerializerFeatures(

SerializerFeature.WriteNullBooleanAsFalse,

SerializerFeature.WriteNullListAsEmpty,

SerializerFeature.WriteNullNumberAsZero,

SerializerFeature.WriteNullStringAsEmpty

);

converter.setFastJsonConfig(fastJsonConfig);

converters.add(converter);

}

}

Fastjson SerializerFeatures常用枚举值

枚举值

含义

备注

WriteNullListAsEmpty

List字段如果为null,输出为[],而非null

WriteNullStringAsEmpty

字符类型字段如果为null,输出为"",而非null

WriteNullBooleanAsFalse

Boolean字段如果为null,输出为false,而非null

WriteNullNumberAsZero

数值字段如果为null,输出为0,而非null

WriteMapNullValue

是否输出值为null的字段,默认为false

自定义Jackson ObjectMapper

静态资源配置

Spring Boot中默认的静态资源配置是将类路径下的/static 、/public、/resources、/META-INF/resources文件夹中的静态资源直接映射为/**。这个默认行为是在WebMvcAutoConfiguration内部类WebMvcAutoConfigurationAdapter的addResourceHandlers方法中定义的,相关的属性配置类为ResourceProperties、WebMvcProperties

以下是部分源码:

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)

public class ResourceProperties {

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {

"classpath:/META-INF/resources/", "classpath:/resources/",

"classpath:/static/", "classpath:/public/" };

/**

* Locations of static resources. Defaults to classpath:[/META-INF/resources/,

* /resources/, /static/, /public/].

*/

private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;

}

@ConfigurationProperties(prefix = "spring.mvc")

public class WebMvcProperties {

...

/**

* Path pattern used for static resources.

*/

private String staticPathPattern = "/**";

...

}

@Override

public void addResourceHandlers(ResourceHandlerRegistry registry) {

if (!this.resourceProperties.isAddMappings()) {

logger.debug("Default resource handling disabled");

return;

}

Duration cachePeriod = this.resourceProperties.getCache().getPeriod();

CacheControl cacheControl = this.resourceProperties.getCache()

.getCachecontrol().toHttpCacheControl();

// webjars支持

if (!registry.hasMappingForPattern("/webjars/**")) {

customizeResourceHandlerRegistration(registry

.addResourceHandler("/webjars/**")

.addResourceLocations("classpath:/META-INF/resources/webjars/")

.setCachePeriod(getSeconds(cachePeriod))

.setCacheControl(cacheControl));

}

String staticPathPattern = this.mvcProperties.getStaticPathPattern();

// 默认静态资源处理

if (!registry.hasMappingForPattern(staticPathPattern)) {

customizeResourceHandlerRegistration(

registry.addResourceHandler(staticPathPattern)

.addResourceLocations(getResourceLocations(

this.resourceProperties.getStaticLocations()))

.setCachePeriod(getSeconds(cachePeriod))

.setCacheControl(cacheControl));

}

}

自定义静态资源配置

使用属性值配置

src/main/java/resources/application.yml

spring:

mvc:

static-path-pattern: /resources/**

resources:

static-locations: ["classpath:/META-INF/resources/", "classpath:/resources/",

"classpath:/static/", "classpath:/public/"]

重写addResourceHandlers进行配置

@Configuration

public class WebMvcConfig implements WebMvcConfigurer {

@Override

public void addResourceHandlers(ResourceHandlerRegistry registry) {

registry.addResourceHandler("/**")

.addResourceLocations("classpath:/META-INF/resources/", "classpath:/resources/",

"classpath:/static/", "classpath:/public/")

.addResourceLocations("file:/Users/fulgens/Downloads/");

}

}

以上代码相当于在默认配置基础上添加了虚拟目录

WebJars支持

WebJars简介

WebJars是将web前端资源(js,css等)打成jar包文件,然后借助Maven、Gradle等依赖管理及项目构建工具,以jar包形式对web前端资源进行统一依赖管理,保证这些Web资源版本唯一性。

官网地址 : https://www.webjars.org/

d0bddbf729af?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

WebJars官网

WebJars使用

引入依赖

官网首页,找到资源文件对应的maven依赖,写入项目pom.xml文件

org.webjars

webjars-locator

0.34

org.webjars

jquery

3.3.1

org.webjars

bootstrap

4.1.3

org.webjars

layui

2.3.0

这里引入了jquery、bootstrap、layui

页面引用

注意:这里由于添加了webjars-locator依赖,在引入前端资源时省略了版本号,推荐使用

如果未添加webjars-locator依赖,在引入前端资源时你需要添加版本号,像下面这样:

欢迎页Welcome Page

Spring Boot支持静态和模板化欢迎页面。 它首先在配置的静态资源目录中查找index.html文件。 如果找不到,则查找index模板。 如果找到任何一个,将自动用作应用程序的欢迎页面。

Spring Boot关于欢迎页的处理类为WelcomePageHandlerMapping

自定义Favicon

Spring Boot默认的提供的favicon是片小叶子,我们也可以自定义favicon,另Spring Boot关于Favicon的配置类为FaviconConfiguration,其默认会在配置的静态资源路径和类路径根目录下查找名为favicon.ico的Favicon图片,如果存在,则自动应用为应用的Favicon

关闭Favicon

设置spring.mvc.favicon.enabled属性值为false即可关闭Favicon,默认值为true

src/main/resources/application.yml

spring:

mvc:

favicon:

enabled: false # 禁用favicon

设置自己的Favicon

根据以上原理,我们只需要在静态资源路径或类路径根目录下放一张名为favicon.ico的Favicon图片即可

注意:需要注意浏览器缓存,导致favicon.ico没能及时更新,需浏览器清缓存。

模板引擎

整合Freemarker

添加依赖

org.springframework.boot

spring-boot-starter-freemarker

相关配置

src/main/resources/application.yml

spring:

freemarker:

suffix: .ftl

cache: false

charset: UTF-8

content-type: text/html

template-loader-path: "classpath:/templates/"

expose-request-attributes: true

expose-session-attributes: true

expose-spring-macro-helpers: true

request-context-attribute: request

这里贴出Freemarker所有配置属性

# FREEMARKER FreeMarkerProperties

spring.freemarker.allow-request-override=false # Whether HttpServletRequest attributes are allowed to override (hide) controller generated model attributes of the same name.

spring.freemarker.allow-session-override=false # Whether HttpSession attributes are allowed to override (hide) controller generated model attributes of the same name.

spring.freemarker.cache=false # Whether to enable template caching.

spring.freemarker.charset=UTF-8 # Template encoding.

spring.freemarker.check-template-location=true # Whether to check that the templates location exists.

spring.freemarker.content-type=text/html # Content-Type value.

spring.freemarker.enabled=true # Whether to enable MVC view resolution for this technology.

spring.freemarker.expose-request-attributes=false # Whether all request attributes should be added to the model prior to merging with the template.

spring.freemarker.expose-session-attributes=false # Whether all HttpSession attributes should be added to the model prior to merging with the template.

spring.freemarker.expose-spring-macro-helpers=true # Whether to expose a RequestContext for use by Spring's macro library, under the name "springMacroRequestContext".

spring.freemarker.prefer-file-system-access=true # Whether to prefer file system access for template loading. File system access enables hot detection of template changes.

spring.freemarker.prefix= # Prefix that gets prepended to view names when building a URL.

spring.freemarker.request-context-attribute= # Name of the RequestContext attribute for all views.

spring.freemarker.settings.*= # Well-known FreeMarker keys which are passed to FreeMarker's Configuration.

spring.freemarker.suffix=.ftl # Suffix that gets appended to view names when building a URL.

spring.freemarker.template-loader-path=classpath:/templates/ # Comma-separated list of template paths.

spring.freemarker.view-names= # White list of view names that can be resolved.

简单实践

src/main/resources/templates/index.ftl

后台首页
Hello World!

layui.use('element', function(){

var element = layui.element;

});

src/main/resources/templates/app.ftl

src/main/resources/templates/layout/header.ftl

src/main/resources/templates/layout/footer.ftl

src/main/resources/templates/layout/menu.ftl

视图控制器配置

对于一个传统的非前后端分离项目来说,视图控制器的配置是至关重要的,可以通过重写WebMvcConfigurer的addViewControllers(ViewControllerRegistry registry)方法来配置

@Override

public void addViewControllers(ViewControllerRegistry registry) {

registry.addViewController("/").setViewName("/index");

registry.addViewController("/index").setViewName("/index");

registry.addViewController("/register").setViewName("/register");

registry.addViewController("/login").setViewName("/login");

}

上面的代码与我们自己写一个Controller完成视图映射是一样的

@Controller

public class RouterController {

@GetMapping(value = {"/", "/index"})

public String toIndex() {

return "/index";

}

@GetMapping("/register")

public String toRegister() {

return "/register";

}

@GetMapping("/login")

public String toLogin() {

return "/login";

}

}

拦截器配置

这里是一个简单的登录校验拦截器

src/main/java/com/example/springbootmvc/web/interceptor/LoginInterceptor

package com.example.springbootmvc.web.interceptor;

import com.alibaba.fastjson.JSON;

import com.example.springbootmvc.common.constants.CommonContext;

import com.example.springbootmvc.common.utils.ServerResponse;

import com.example.springbootmvc.entity.User;

import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.web.method.HandlerMethod;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

import java.io.OutputStream;

import java.util.Arrays;

import java.util.Iterator;

import java.util.Map;

/**

* 登录校验拦截器

*/

public class LoginInterceptor implements HandlerInterceptor {

private static final Logger log = LoggerFactory.getLogger(LoginInterceptor.class);

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

if (handler instanceof HandlerMethod) {

HandlerMethod handlerMethod = (HandlerMethod) handler;

String methodName = handlerMethod.getMethod().getName();

String className = handlerMethod.getBean().getClass().getSimpleName();

log.info("拦截controller: {}, 拦截方法: {}", className, methodName);

}

// 获取请求url

String toURL = request.getRequestURI();

String queryString = request.getQueryString();

if (StringUtils.isNotEmpty(queryString)) {

toURL += "?" + queryString;

}

log.info("拦截请求URL: {}", toURL);

// 获取拦截请求的请求参数

StringBuffer sb = new StringBuffer();

Map parameterMap = request.getParameterMap();

Iterator> iterator = parameterMap.entrySet().iterator();

while (iterator.hasNext()) {

Map.Entry entry = iterator.next();

String mapKey = entry.getKey();

String mapValue = StringUtils.EMPTY;

mapValue = Arrays.toString(entry.getValue());

sb.append(mapKey).append("=").append(mapValue);

}

log.info("拦截请求入参: {}", sb.toString());

User currenUser = (User) request.getSession().getAttribute(CommonContext.CURRENT_USER_CONTEXT);

if (currenUser == null) {

// 用户未登录跳转登录页面

// response.sendRedirect("/login");

request.getRequestDispatcher("/login").forward(request, response);

// 保存用户请求url用于登录成功后跳转

request.getSession().setAttribute(CommonContext.LOGIN_REDIRECT_URL, toURL);

return false;

}

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 {

}

}

对于前后端分离的项目可能在拦截校验不通过时需要向前台返回一些信息

private void returnErrorMsg(HttpServletResponse response, ServerResponse serverResponse) {

try (OutputStream os = response.getOutputStream()) {

response.setCharacterEncoding("UTF-8");

response.setContentType("application/json;charset=UTF-8");

os.write(JSON.toJSONString(serverResponse).getBytes());

os.flush();

} catch (IOException e) {

log.error("登录校验拦截器输出错误信息发生异常,异常信息: {}", e);

}

}

这里ServerResponse是封装的一个通用服务端响应对象

通过重写WebMvcConfigurer的addInterceptors(InterceptorRegistry registry)方法配置拦截器

@Override

public void addInterceptors(InterceptorRegistry registry) {

String[] excludePath = {"/login", "/doLogin", "/register", "/doRegister",

"/error", "/**/*.js", "/**/*.css", "/**/*.jpg", "/**/*.jpeg",

"/**/*.png", "/**/*.ico"};

registry.addInterceptor(new LoginInterceptor())

.addPathPatterns("/**")

.excludePathPatterns(excludePath);

}

全局异常处理

Spring Boot默认的错误处理机制

默认情况下,Spring Boot提供/error错误映射,以合理的方式处理所有错误,并在servlet容器中注册为“全局”错误页面。对于机器客户端(machine clients),它会生成一个JSON响应,其中包含错误信、HTTP状态和异常消息的详细信息。对于浏览器客户端(browser clients),有一个“whitelabel”错误视图,以HTML格式呈现相同的数据(要自定义它,须添加一个解析错误的视图)。要完全替换默认行为,可以实现ErrorController接口并注册该类型的bean,或者添加ErrorAttributes类型的bean以使用现有机制但替换内容。

比如,我们自定义一个产生异常的映射:

@GetMapping("/test")

public void testException() {

int i = 1/0;

}

d0bddbf729af?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Spring Boot默认whitelabel错误页面

再使用Restlet、Postman等接口测试工具访问

d0bddbf729af?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Spring Boot生成的JSON错误信息

其实,Spring Boot默认错误处理机制在BasicErrorController中定义,部分源码如下:

@Controller

@RequestMapping("${server.error.path:${error.path:/error}}")

public class BasicErrorController extends AbstractErrorController {

private final ErrorProperties errorProperties;

// ...

@RequestMapping(produces = "text/html")

public ModelAndView errorHtml(HttpServletRequest request,

HttpServletResponse response) {

HttpStatus status = getStatus(request);

Map model = Collections.unmodifiableMap(getErrorAttributes(

request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));

response.setStatus(status.value());

ModelAndView modelAndView = resolveErrorView(request, response, status, model);

return (modelAndView != null ? modelAndView : new ModelAndView("error", model));

}

@RequestMapping

@ResponseBody

public ResponseEntity> error(HttpServletRequest request) {

Map body = getErrorAttributes(request,

isIncludeStackTrace(request, MediaType.ALL));

HttpStatus status = getStatus(request);

return new ResponseEntity<>(body, status);

}

// ...

}

d0bddbf729af?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

BasicErrorController类图

自定义错误页面

如果要显示给定状态码的自定义HTML错误页面,可以将文件添加到/error目录。 错误页面可以是静态HTML(即,添加到任何静态资源文件夹下),也可以使用模板构建。 文件名应该是确切的状态代码或系列掩码。

例如,要映射404错误到静态HTML文件,目录结构将如下所示:

src/

+- main/

+- java/

| +

+- resources/

+- public/

+- error/

| +- 404.html

+-

同样映射500错误我们可以在/error目录下放一个500.html

使用FreeMarker模板映射所有5xx错误,目录结构如下:

src/

+- main/

+- java/

| +

+- resources/

+- templates/

+- error/

| +- 5xx.ftl

+-

这里给出一个简单的5xx.ftl模板

5xx

Oh, There is something wrong

timestamp: ${timestamp?datetime}

status: ${status}

error: ${error}

message: ${message}

path: ${path}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值