定制错误数据有status,timeStamp,exception,message,ext,其中status,timeStamp,exception是从容器中获取的,但是异常确没有显示,如下:
首先,前端页面是不可能出现获取数据的代码写错的情况,因为下面几个一模一样的获取方法,人家都出来了,就它没出来,所以应该是后端的问题:
下面为处理请求的controller
/**
* @Classname HelloController
* @Description TODO
* @Date 19-7-24 上午9:02
* @Created by xns
*/
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/hello")
public String hello(@RequestParam("user") String user){
//如果用户是aaa,则抛出UserNotExistException这个异常
if(user.equals("aaa")){
throw new UserNotExistException();
}
return "Hello World";
}
}
自定义的异常类:
/**
* @Classname UserNotExistException
* @Description TODO
* @Date 19-7-27 上午11:49
* @Created by xns
*/
public class UserNotExistException extends RuntimeException {
public UserNotExistException() {
super("用户不存在!!!");
}
}
下面是为controller定制异常处理器的类:
import com.xns.springboot.exception.UserNotExistException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* @Classname MyExceptionHandler
* @Description TODO
* @Date 19-7-27 上午11:58
* @Created by xns
*/
//异常指示器
@ControllerAdvice //定义全局异常处理类
public class MyExceptionHandler {
@ExceptionHandler(UserNotExistException.class) //handleException就会处理所有 Controller层抛出的UserNotExistException及其子类的异常
public String handleException(Exception e, HttpServletRequest request){
Map<String,Object> map = new HashMap<>();
//传入我们自己的错误状态码 4xx,5xx等
request.setAttribute("javax.servlet.error.status_code",500);
map.put("code","user.notexist");
map.put("message","用户出错了");
request.setAttribute("ext",map);
//转发到/error让BasicErrorController进行自适应页面
return "forward:/error";
}
}
下面的代码来给容器中加入我自己定义的ErrorAttributes
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
import java.util.Map;
/**
* @Classname MyErrorAttributes
* @Description TODO
* @Date 19-7-27 下午3:17
* @Created by xns
*/
//给容器中加入我们自己定义的ErrorAttributes
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
//看这里,重点
public MyErrorAttributes(ServerProperties serverProperties) {
super(serverProperties.getError().isIncludeException());
}
//返回值的map就是页面和json能获取的所有字段
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String,Object> map = super.getErrorAttributes(webRequest,includeStackTrace);
map.put("company","xns");
//我们异常处理器携带的数据
Map<String,Object> ext = (Map<String,Object>)webRequest.getAttribute("ext",0);
map.put("ext",ext);
return map;
}
}
解决办法:写一个构造器,这样,就可以正常获取异常信息了
第一步:
在resources/application.properties
下,即配置文件里,写上:
server.error.include-exception=true
第二步:
在自己定义的ErrorAttributes里添加如下代码
public MyErrorAttributes(ServerProperties serverProperties) {
super(serverProperties.getError().isIncludeException());
}
添加完成后,这个类的完整代码如下:
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
import java.util.Map;
/**
* @Classname MyErrorAttributes
* @Description TODO
* @Date 19-7-27 下午3:17
* @Created by xns
*/
//给容器中加入我们自己定义的ErrorAttributes
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
public MyErrorAttributes(ServerProperties serverProperties) {
super(serverProperties.getError().isIncludeException());
}
//返回值的map就是页面和json能获取的所有字段
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String,Object> map = super.getErrorAttributes(webRequest,includeStackTrace);
map.put("company","xns");
//我们异常处理器携带的数据
Map<String,Object> ext = (Map<String,Object>)webRequest.getAttribute("ext",0);
map.put("ext",ext);
return map;
}
}
正常显示:
原因:下面是相关源码ErrorMvcAutoConfiguration,一个自动配置类
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
// Load before the main WebMvcAutoConfiguration so that the error View is available
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class, WebMvcProperties.class })
public class ErrorMvcAutoConfiguration {
private final ServerProperties serverProperties;
private final DispatcherServletPath dispatcherServletPath;
private final List<ErrorViewResolver> errorViewResolvers;
public ErrorMvcAutoConfiguration(ServerProperties serverProperties, DispatcherServletPath dispatcherServletPath,
ObjectProvider<ErrorViewResolver> errorViewResolvers) {
this.serverProperties = serverProperties;
this.dispatcherServletPath = dispatcherServletPath;
this.errorViewResolvers = errorViewResolvers.orderedStream().collect(Collectors.toList());
}
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}
看这段代码:
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}
点进DefaultErrorAttributes
发现这段代码:
/**
* Create a new {@link DefaultErrorAttributes} instance.
* @param includeException whether to include the "exception" attribute
*/
public DefaultErrorAttributes(boolean includeException) {
this.includeException = includeException;
}
看注释,它说,参数includeException
说明是否包含exception
,所以这里应该传入的是true
看上一段代码,所以this.serverProperties.getError().isIncludeException()
的值应该为true,点进getError()
,来到了ServerProperties
类看到:
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
和方法
public ErrorProperties getError() {
return this.error;
}
它返回了一个ErrorProperties
类型的值,然后我们再点进isIncludeException()
,来到了ErrorProperties
类,看到:
public boolean isIncludeException() {
return this.includeException;
}
所以这里的数值应该返回true
又因为在ErrorMvcAutoConfiguration
这个类中有这样一个注解:
@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class, WebMvcProperties.class })
它开启了自动配置,并且将ServerProperties.class, ResourceProperties.class, WebMvcProperties.class
这几个类和我们的application.properties
进行了绑定
所以我们通过application.properties
来设置includeException
的值为true(即server.error.include-exception=true
)
添加完成这个,还是不能出现,(下面为我自己的推断,如有错误,请指正)
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}
看这段代码,注解@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
的意思是当ErrorAttributes
类型的Bean不存在于BeanFactory中时才创建这个Bean ,但是上面,我已经将自己定义的ErrorAttributes
加入了容器,所以这个组件不会被注册.所以我就想着自己实现一个,new一个DefaultErrorAttributes
所以,就添加了下面的代码:
public MyErrorAttributes(ServerProperties serverProperties) {
super(serverProperties.getError().isIncludeException());
}
这里的serverProperties直接从容器中获取值,因为由SpringBoot自动配置原理,可以知道,它已经被加入到容器中,用到的时候可直接获取.
初学SpringBoot,若有问题,请指正,谢谢!