欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot Web篇笔记(一)

程序员文章站 2022-06-13 08:09:54
摘要 文章是根据江南一点雨(松哥)的视频进行总结 "江南一点雨博客" 全局异常处理 通常情况下,我们都需要对自己定义的异常进行相应的处理。捕获指定的异常方式如下: 自定义错误页面 若服务器抛出404错误码(页面找不到)时,通常会返回如下页面: 而我们需要指定在服务器抛出相应的错误码时,跳转到指定的动 ......

摘要

文章是根据江南一点雨(松哥)的视频进行总结


全局异常处理

通常情况下,我们都需要对自己定义的异常进行相应的处理。捕获指定的异常方式如下:

@controlleradvice
public class exceptionhandlers {

    // 捕获自定义异常类进行处理
    @exceptionhandler(customexception.class)
    public modelandview handler(customexception e) {
        modelandview modelandview = new modelandview("customexception"); //自定义异常错误页面
        modelandview.addobject("msg", e.getmessage());
        // ...
        return modelandview;
    }
}


自定义错误页面

若服务器抛出404错误码(页面找不到)时,通常会返回如下页面:
SpringBoot Web篇笔记(一)

而我们需要指定在服务器抛出相应的错误码时,跳转到指定的动态或静态页面。

源码阅读

参考默认的视图解析器org.springframework.boot.autoconfigure.web.servlet.error.defaulterrorviewresolver源码,取出部分代码片段如下:

public class defaulterrorviewresolver implements errorviewresolver, ordered {
   private static final map<series, string> series_views; // 存放不同错误码对应的视图

   // 添加默认的视图
   static {
      map<series, string> views = new enummap<>(series.class);
      views.put(series.client_error, "4xx");
      views.put(series.server_error, "5xx");
      series_views = collections.unmodifiablemap(views);
   }
   ...

   // 开始解析错误视图
   @override
   public modelandview resolveerrorview(httpservletrequest request, httpstatus status, map<string, object> model) {
      // status.value() 得到的是错误码
      // 寻找错误码指定的页面,如404就找名为404的页面
      modelandview modelandview = resolve(string.valueof(status.value()), model); 

      // 若找不到错误码指定的页面,则400,401,403,404...都会去找4xx的页面
      if (modelandview == null && series_views.containskey(status.series())) {
         modelandview = resolve(series_views.get(status.series()), model);
      }
      // 若modelandview还是null,那么就返回上面的那个图片了
      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);
   }

   //获取静态页面资源
   private modelandview resolveresource(string viewname, map<string, object> model) {
      // 遍历静态资源,查找是否有对应的页面
      for (string location : this.resourceproperties.getstaticlocations()) {
         try {
            resource resource = this.applicationcontext.getresource(location);
            resource = resource.createrelative(viewname + ".html");
            if (resource.exists()) {
               return new modelandview(new htmlresourceview(resource), model);
            }
         }
         catch (exception ex) {
         }
      }
      return null;
   }
   ...
}

阅读源码总结

1.首先会去找指定错误码的页面,若指定页面找不到则找4xx、5xx页面,(400、401...都会找4xx)
2.先到动态资源下的error目录寻找,再到静态资源中的error目录寻找
SpringBoot Web篇笔记(一)

实现

如果为动态资源的页面,返回的modelattribute可以查看org.springframework.boot.web.servlet.error.defaulterrorattributes, 返回的数据如下:

timestamp
status
error
message
...

thymeleaf下页面使用如下:

<table>
    <tr>
        <td th:text="${status}"></td>
    </tr>
    <tr>
        <td th:text="${message}"></td>
    </tr>
</table>

若需要扩展,则继承defaulterrorattributes,对扩展类加@component注释:

@component
public class customerrorattribute extends defaulterrorattributes {
   // 扩展
}


cors跨域

在前后端分离进行开发的情况下,一般都需要设置跨域访问,springboot提供cors跨域设置如下:

@configuration
public class webmvcconfig implements webmvcconfigurer {
    @override
    public void addcorsmappings(corsregistry registry) {
        registry.addmapping("/**") //所有前缀
                .allowedorigins("http://localhost:8081") //跨域地址(前端地址)
                .allowedheaders("*") //允许所有请求头
                .allowedmethods("*") //允许通过所有方法
                .maxage(30 * 1000); //探测请求的有效期
    }
}


注册拦截器

拦截器可以拦截request请求,若自定义权限认证的功能,就可以使用拦截器去进行实现。

public class myinterceptor implements handlerinterceptor {
    @override
    public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
        return false;
    }
    public void posthandle ...
    public void aftercompletion ...
}

prehandler执行方法前调用,posthandler在返回视图前调用,aftercompletion在方法执行完后调用。
添加拦截器到配置中,重写addinterceptors方法

@configuration
public class webmvcconfig implements webmvcconfigurer {

    @override
    public void addinterceptors(interceptorregistry registry) {
        registry.addinterceptor(myinterceptor())
                .addpathpatterns("/**"); //拦截所有路径
    }

    @bean
    myinterceptor myinterceptor() {
        return new myinterceptor();
    }
}


整合servlet

首先自定义的servelt继承javax.servlet.http.httpservlet;使用@webservlet进行url映射

@webservlet(urlpatterns = "/myservlet")
public class myservlet extends httpservlet {

    @override
    protected void doget(httpservletrequest req, httpservletresponse resp) throws servletexception, ioexception {
        system.out.println("doget");
    }
}

在启动类xxxapplication对自定义的servlet的目录进行扫描

@servletcomponentscan(basepackages = "org.java.servlet")

这就可以成功访问到啦!localhost:8080/myservlet

扩展(怕忘记了,记一下):
request监听实现接口javax.servlet.servletrequestlistener, 然后对request监听类使用javax.servlet.annotation.weblistener注解;
request拦截器实现接口javax.servlet.filter,然后对拦截器使用javax.servlet.annotation.webfilter注解,如:

@weblistener
public class myrequestlistener implements servletrequestlistener {
    @override
    public void requestdestroyed(servletrequestevent sre) {system.out.println("requestdestroyed");}
    @override
    public void requestinitialized(servletrequestevent sre) {system.out.println("requestinitialized");}
}

@webfilter(urlpatterns = "/*") //对所有目录进行拦截
public class myfilter implements filter {
    @override
    public void dofilter(servletrequest request, servletresponse response, filterchain chain) throws ioexception, servletexception {
        system.out.println("dofilter");
        chain.dofilter(request,response);
    }
}

上述的监听器和拦截器一定要在@servletcomponentscan的扫描目录下或子目录。

若文章有错误或疑问,可在下方评论,thanks♪(・ω・)ノ。