详解在Spring MVC或Spring Boot中使用Filter打印请求参数问题

使用Spring MVC或Spring Boot中打印或记录日志一般使用AOP记录Request请求和Response响应参数,在不使用AOP的前提下,如果在Filter中打印日志,在打印或消费请求类型为Content-Type:application/json的请求时,会出现严重的问题。

在Spring体系中,过滤器的定义我们一般采用继承OncePerRequestFilter的方式,当然也可以使用原始的Filter。

错误写法一:

如果不对request和response进行处理,使用伪代码采用如下写法打印请求和响应参数(注:此时request请求类型为Post,接收的是Json数据)

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
         response, FilterChain filterChain) throws ServletException, IOException {
    filterChain.doFilter(request, response);
    printRequestLog(request);
    printResonseLog(response);
  }

运行测试后你会发现抛出如下异常:

java.io.IOException: Stream closed
 at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:359) ~[tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:132) ~[tomcat-embed-core-9.0.31.jar:9.0.31]
 at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) ~[na:1.8.0_191]
 at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) ~[na:1.8.0_191]
 at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) ~[na:1.8.0_191]
 at java.io.InputStreamReader.read(InputStreamReader.java:184) ~[na:1.8.0_191]
 at java.io.BufferedReader.fill(BufferedReader.java:161) ~[na:1.8.0_191]
 at java.io.BufferedReader.readLine(BufferedReader.java:324) ~[na:1.8.0_191]
 at java.io.BufferedReader.readLine(BufferedReader.java:389) ~[na:1.8.0_191]
 at com.micro.backend.filter.LoggingFilter.getBodyString(LoggingFilter.java:60) [classes/:na]
 at com.micro.backend.filter.LoggingFilter.doFilterInternal(LoggingFilter.java:49) [classes/:na]
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.1.14.RELEASE.jar:5.1.14.RELEASE]
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) [spring-web-5.1.14.RELEASE.jar:5.1.14.RELEASE]
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.1.14.RELEASE.jar:5.1.14.RELEASE]
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) [spring-web-5.1.14.RELEASE.jar:5.1.14.RELEASE]
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.1.14.RELEASE.jar:5.1.14.RELEASE]
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:94) [spring-web-5.1.14.RELEASE.jar:5.1.14.RELEASE]
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.1.14.RELEASE.jar:5.1.14.RELEASE]
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) [spring-web-5.1.14.RELEASE.jar:5.1.14.RELEASE]
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.1.14.RELEASE.jar:5.1.14.RELEASE]
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1639) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_191]
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_191]
 at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.31.jar:9.0.31]
 at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191]

错误写法二:

如果不对request和response进行处理,使用伪代码采用如下写法打印请求和响应参数(注:此时request请求类型为Post,接收的是Json数据)

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
       response, FilterChain filterChain) throws ServletException, IOException {
    printRequestLog(request);
    printResonseLog(response);
    filterChain.doFilter(request, response);
  }

运行测试后你会发现抛出如下异常:

org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing

遇到这样的问题你是不是有坐立不安、心烦意乱、百爪挠心的痛楚,不要着急,下面我给出一个解决方案。

首先我们使用装饰器模式,创建request和response两个包装类,如下:

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
/**
* @Description:  请求包装器
* @Author:     liuliya
* @CreateDate:   2020/4/29 10:00
*/
public class RequestWrapper extends HttpServletRequestWrapper {

  private final byte[] body;

  public RequestWrapper(HttpServletRequest request) throws IOException {
    super(request);
    body = getRequestBodyString(request).getBytes(Charset.defaultCharset());
  }

  @Override
  public BufferedReader getReader() throws IOException {
    return new BufferedReader(new InputStreamReader(getInputStream()));
  }

  @Override
  public ServletInputStream getInputStream() throws IOException {

    final ByteArrayInputStream bais = new ByteArrayInputStream(body);

    return new ServletInputStream() {

      @Override
      public int read() throws IOException {
        return bais.read();
      }

      @Override
      public boolean isFinished() {
        return false;
      }

      @Override
      public boolean isReady() {
        return false;
      }

      @Override
      public void setReadListener(ReadListener listener) {

      }
    };
  }

  public String getRequestBodyString(ServletRequest request) {
    StringBuilder sb = new StringBuilder();
    InputStream inputStream = null;
    BufferedReader reader = null;
    try {
      inputStream = request.getInputStream();
      reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
      String line;
      while ((line = reader.readLine()) != null) {
        sb.append(line);
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if (inputStream != null) {
        try {
          inputStream.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (reader != null) {
        try {
          reader.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
    return sb.toString();
  }
}
package com.micro.backend.filter.support;

import org.apache.commons.io.output.TeeOutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Description: 响应包装器
 * @Author: liuliya
 * @CreateDate: 2020/4/29 10:00
 */
public class ResponseWrapper extends HttpServletResponseWrapper {

  private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
  private PrintWriter writer = new PrintWriter(bos);

  public ResponseWrapper(HttpServletResponse response) {
    super(response);
  }

  @Override
  public ServletResponse getResponse() {
    return this;
  }

  @Override
  public ServletOutputStream getOutputStream() throws IOException {
    return new ServletOutputStream() {
      @Override
      public boolean isReady() {
        return false;
      }

      @Override
      public void setWriteListener(WriteListener listener) {

      }

      private TeeOutputStream tee = new TeeOutputStream(ResponseWrapper.super.getOutputStream(), bos);

      @Override
      public void write(int b) throws IOException {
        tee.write(b);
      }
    };
  }

  @Override
  public PrintWriter getWriter() throws IOException {
    return new TeePrintWriter(super.getWriter(), writer);
  }

  public byte[] toByteArray() {
    return bos.toByteArray();
  }
}
package com.micro.backend.filter.support;
import java.io.PrintWriter;
//PrintWriter是一种写入字符的一种操作类,可以写入字符,TeePrintWriter继承了他,主要功能是把原始的字符流copy到branch里面。
public class TeePrintWriter extends PrintWriter {

  PrintWriter branch;

  public TeePrintWriter(PrintWriter main, PrintWriter branch) {
    super(main, true);
    this.branch = branch;
  }

  public void write(char buf[], int off, int len) {
    super.write(buf, off, len);
    super.flush();
    branch.write(buf, off, len);
    branch.flush();
  }

  public void write(String s, int off, int len) {
    super.write(s, off, len);
    super.flush();
    branch.write(s, off, len);
    branch.flush();
  }

  public void write(int c) {
    super.write(c);
    super.flush();
    branch.write(c);
    branch.flush();
  }

  public void flush() {
    super.flush();
    branch.flush();
  }
}

接下来创建最重要的LoggingFilter类,继承OncePerRequestFilter,或者直接继承Servlet中原始的Filter。

package com.micro.backend.filter;

import com.micro.backend.filter.support.RequestWrapper;
import com.micro.backend.filter.support.ResponseWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

/**
* @Author:     liuliya
* @CreateDate:   2020/4/28 23:30
*/
@Slf4j
@Configuration
public class LoggingFilter extends OncePerRequestFilter {

  private static final String REQUEST_PREFIX_NAME = "Request请求: ";
  private static final String RESPONSE_PREFIX_NAME = "Response请求: ";

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    request = new RequestWrapper(request);
    response = new ResponseWrapper(response);
    filterChain.doFilter(request, response);
    printRequestLog(request);
    printResponseLog((ResponseWrapper) response);
  }

  private void printRequestLog(final HttpServletRequest request) {
    StringBuilder msg = new StringBuilder();
    msg.append(REQUEST_PREFIX_NAME);
    HttpSession session = request.getSession(false);
    if (session != null) {
      msg.append("sessionId = ").append(session.getId()).append("; ");
    }
    if (request.getMethod() != null) {
      msg.append("method = ").append(request.getMethod()).append("; ");
    }
    if (request.getContentType() != null) {
      msg.append("contentType = ").append(request.getContentType()).append("; ");
    }
    msg.append("uri = ").append(request.getRequestURI());
    if (request.getQueryString() != null) {
      msg.append('?').append(request.getQueryString());
    }

    if (request instanceof RequestWrapper && !isMultipart(request) && !isBinaryContent(request)) {
      RequestWrapper requestWrapper = (RequestWrapper) request;
      msg.append("; payload = ").append(requestWrapper.getRequestBodyString(request));
    }
    log.info(msg.toString());
  }

  private boolean isBinaryContent(final HttpServletRequest request) {
    if (request.getContentType() == null) {
      return false;
    }
    return request.getContentType().startsWith("image")
        || request.getContentType().startsWith("video")
        || request.getContentType().startsWith("audio");
  }

  private boolean isMultipart(final HttpServletRequest request) {
    return request.getContentType() != null
        && request.getContentType().startsWith("multipart/form-data");
  }

  private void printResponseLog(final ResponseWrapper response) {
    StringBuilder msg = new StringBuilder();
    msg.append(RESPONSE_PREFIX_NAME);
    try {
      msg.append("; payload = ")
          .append(new String(response.toByteArray(), response.getCharacterEncoding()));
    } catch (UnsupportedEncodingException e) {
      log.warn("Failed to parse response payload", e);
    }
    log.info(msg.toString());
  }
}

参考以上我整理出的代码,你就会发现奇迹!!!

为什么要这么写呢,其本质是把请求流拷贝了一份,一个供filterChain向下传递,一个来做流的消费,再有一个就是运用装饰器模式的精髓所在。

到此这篇关于详解在Spring MVC或Spring Boot中使用Filter打印请求参数问题的文章就介绍到这了,更多相关SpringBoot Filter打印请求参数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot拦截器Filter的使用方法详解

    前言: 最新Servlet 3.0拦截器的使用 1.pom.xml添加需要使用的依赖 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/x

  • springboot中filter的用法详解

    一.在spring的应用中我们存在两种过滤的用法,一种是拦截器.另外一种当然是过滤器.我们这里介绍过滤器在springboot的用法,在springmvc中的用法基本上一样,只是配置上面有点区别. 二.filter功能,它使用户可以改变一个 request和修改一个response. Filter 不是一个servlet,它不能产生一个response,它能够在一个request到达servlet之前预处理request,也可以在离开 servlet时处理response.换种说法,filter

  • spring boot 配置Filter过滤器的方法

    Filter 过滤器是web开发中很重要的一个组件,下面以一个session登陆的例子介绍下spring boot中如何使用Filter 首先要准备一个实现了Filter的接口的类 SessionFilter: import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRespo

  • Spring Boot使用过滤器Filter过程解析

    这篇文章主要介绍了Spring Boot使用过滤器Filter过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 首先我们说说什么是过滤器,过滤器是对数据进行过滤,预处理过程,当我们访问网站时,有时候会发布一些敏感信息,发完以后有的会用*替代,还有就是登陆权限控制等,一个资源,没有经过授权,肯定是不能让用户随便访问的,这个时候,也可以用到过滤器.过滤器的功能还有很多,例如实现URL级别的权限控制.压缩响应信息.编码格式等等. 过滤器依赖se

  • SpringBoot 2 快速整合 Filter过程解析

    概述 SpringBoot 中没有 web.xml, 我们无法按照原来的方式在 web.xml 中配置 Filter .但是我们可以通过 JavaConfig(@Configuration +@Bean)方式进行配置.通过FilterRegistrationBean 将自定义 Filter 添加到 SpringBoot 的过滤链中. 实战操作 实战操作通过定义一个拦截所有访问项目的URL的 Filter来进行演示的. 首先定义一个统一访问 URL 拦截的 Filter.代码如下: public

  • Spring Boot的filter(过滤器)简单使用实例详解

    过滤器(Filter)的注册方法和 Servlet 一样,有两种方式:代码注册或者注解注册 1.代码注册方式 通过代码方式注入过滤器 @Bean public FilterRegistrationBean indexFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(new IndexFilter()); registration.addUrlPatterns("/&quo

  • spring-boot实现增加自定义filter(新)

    前言 传统的javaEE增加Filter是在web.xml中配置,如以下代码: <filter> <filter-name>TestFilter</filter-name> <filter-class>com.cppba.filter.TestFilter</filter-class> </filter> <filter-mapping> <filter-name>TestFilter</filter-n

  • springboot使用filter获取自定义请求头的实现代码

    有个钱包项目,本来用的是微服务这一套,后来感觉没必要,重构成了简单的springboot项目,但是token校验重构完之后出问题了,之前写filter走的是springgateway,基于GatewayFilter实现,重构了之后基于filter,然后当请求进入过滤器的时候,发现不能获取到请求的自定义请求头. String token = request.getHeader("token"); // null String id = request.getHeader("id

  • 详解在Spring MVC或Spring Boot中使用Filter打印请求参数问题

    使用Spring MVC或Spring Boot中打印或记录日志一般使用AOP记录Request请求和Response响应参数,在不使用AOP的前提下,如果在Filter中打印日志,在打印或消费请求类型为Content-Type:application/json的请求时,会出现严重的问题. 在Spring体系中,过滤器的定义我们一般采用继承OncePerRequestFilter的方式,当然也可以使用原始的Filter. 错误写法一: 如果不对request和response进行处理,使用伪代码

  • 详解ASP.NET MVC的整个生命周期

    目录 一.介绍 二.MVC生命周期详述 View的初始化和渲染呈现 三.结束 一.介绍 我们做开发的,尤其是做微软技术栈的,有一个方向是跳不过去的,那就是MVC开发.我相信大家,做ASP.NET MVC 开发有的有很长时间,当然,也有刚进入这个行业的.无论如何,如果有人问你,你知道ASP.NET MVC的生命周期吗?你知道它的来世今生吗?你知道它和 ASP.NET WEBFORM 有什么区别吗?估计,这些问题,有很多人会答不上来,或者说不清楚.今天,我就把我的理解写出来,也是对我自己学习的一次回

  • SpringBoot之自定义Filter获取请求参数与响应结果案例详解

    一个系统上线,肯定会或多或少的存在异常情况.为了更快更好的排雷,记录请求参数和响应结果是非常必要的.所以,Nginx 和 Tomcat 之类的 web 服务器,都提供了访问日志,可以帮助我们记录一些请求信息. 本文是在我们的应用中,定义一个Filter来实现记录请求参数和响应结果的功能. 有一定经验的都知道,如果我们在Filter中读取了HttpServletRequest或者HttpServletResponse的流,就没有办法再次读取了,这样就会造成请求异常.所以,我们需要借助 Spring

  • 详解SpringBoot如何删除引用jar包中的无用bean

    目录 前言 代码示例 实现代码示例 前言 公司有个项目,时间比较赶,而且项目的部分需求,和之前做的项目部分功能一样,为了赶速度和直接将之前多模块的maven项目中的部分模块,直接以jar包的形式引入到新项目中了,虽然省去了不少开发时间,但是造成项目需要导入引入项目jar的相关依赖,导致项目臃肿,启动很慢.有没有办法让项目只加载自己需要的bean呢? 当然我们可以直接修改源代码重新打包引入去解决,但是这个办法太多麻烦. 通过百度的手段,查询可以在springboot启动类上用@ComponentS

  • 详解 Mysql查询结果顺序按 in() 中ID 的顺序排列

    详解 Mysql查询结果顺序按 in() 中ID 的顺序排列 实例代码: <select id="queryGBStyleByIDs" resultMap="styleMap"> select style_num_id ,style_id,style_title,style_pic FROM gb_style where online = 1 AND is_hide = 0 and style_num_id in <foreach collecti

  • 详解ES6 CLASS在微信小程序中的应用实例

    ES6 CLASS基本用法 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } 1.1 constructor方法 constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法.一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加.

  • 详解如何在vue+element-ui的项目中封装dialog组件

    1.问题起源 由于 Vue 基于组件化的设计,得益于这个思想,我们在 Vue 的项目中可以通过封装组件提高代码的复用性.根据我目前的使用心得,知道 Vue 拆分组件至少有两个优点: 1.代码复用. 2.代码拆分 在基于 element-ui 开发的项目中,可能我们要写出一个类似的调度弹窗功能,很容易编写出以下代码: <template> <div> <el-dialog :visible.sync="cnMapVisible">我是中国地图的弹窗&l

  • 详解UDP协议格式及在java中的使用

    UDP是面向无连接的通讯协议,由于通讯不需要连接,所以可以实现广播发送.UDP通讯时不需要接收方确认,属于不可靠的传输,可能会出现丢包现象,实际应用中要求程序员编程验证. UDP适用于DNS.视频音频等多媒体通信.广播通信(广播.多播).例如我们常用的QQ,就是一个以UDP为主,TCP为辅的通讯协议. UDP报文格式如下: UDP首部有8个字节,由4个字段构成,每个字段都是两个字节, 源端口:数据发送方的端口号. 目的端口:数据接收方的端口号. 长度:UDP数据报的整个长度(包括首部和数据),其

  • 详解Java枚举类在生产环境中的使用方式

    目录 前言 使用 1.确定业务场景状态 2.定义枚举类 3.自定义查询方法 4.测试效果 总结 前言   Java枚举在项目中使用非常普遍,许多人在做项目时,一定会遇到要维护某些业务场景状态的时候,往往会定义一个常量类,然后添加业务场景相关的状态常量.但实际上,生产环境的项目中业务状态的定义大部分是由枚举类来完成的,因为更加清晰明确,还能自定义不同的方法来获取对应的业务状态值,十分方便. 以下代码均为生产环境已上线项目的代码片段,仅供参考. 使用 大体分为确定业务场景状态.定义枚举类.自定义查询

  • 详解如何在Vue3+TS的项目中使用NProgress进度条

    目录 写在前面 在项目中安装 简单的封装 在Vue切换路由时展示进度条 写在前面 NProgress是一个轻量级的进度条组件,在Github上已经2.4万star数了,虽然这个组件已经好久没有更新了,最近一次更新是20年4月份,改了jQuery的版本,但是该组件的使用频率还是高的. 在项目中安装 这里的包管理工具使用的npm,如果你使用的是yarn或者pnpm,请自行更改安装命令,安装命令如下: npm i nprogress -S 因为我们是TS的项目,还需要安装其类型声明文件,命令如下: n

随机推荐