精华内容
下载资源
问答
  • 优雅的实现spring-cloud-gateway修改请求和响应体
    千次阅读
    2020-04-02 22:12:47

    # 优雅的实现spring-cloud-gateway修改请求和响应体
     
    ## 引言
        最近公司的微服务越来越多,管理起来比较麻烦,决定引入微服务网关。咨询了百度老师发现Spring Cloud gateway功能已经比较完善,性能也还很不错。决定就是它了。
        建工程,加依赖,写配置,一切看起来都那么的简单加顺利。
        只是某天某人突然提了一个需求,为了方便调试让我增加1个可以查看每个post+json+restApi请求的请求头,请求体,响应头,响应体的功能。
        我想这还不简单嘛,直接加个全局过滤器就搞定了。
        赶紧加班加点写了一个过滤器实现GlobalFilter和Ordered接口,在接口中接收获取请求和响应的信息通过日志打印出来就OK了吧。
        
        理想是美好的,现实是残酷的。才发现遇到了不好处理的问题。
    ## 遇到的问题

    * 1.ServerHttpRequest请求对象的请求体只能获取一次,一旦获取了就不能继续往下传递。
        Flux<DataBuffer> getBody();

    * 2.请求体方法返回的是基于webFlux响应式的函数结构,特别难处理。
         
    * 3.ServerHttpResponse响应体根本就没有获取响应体的方法。

    * 4.系统提供的修改请求体和响应体的类只实现了GatewayFilter过滤器,无法对全局进行监控而且还只能通过Java DSL进行配置.不可能每增加一个服务都修改代码。我需要的是全局过滤器。
        ModifyRequestBodyGatewayFilterFactory  系统提供的修改请求体的类
        ModifyResponseBodyGatewayFilterFactory 系统提供的修改响应体的类
    ## 解决方案
     
     查询了网上很多的实现方法始终都不能完美的处理。不是丢包就是根本获取不到数据。
     
     基本思想:代理实现ServerHttpRequest和ServerHttpResponse类,自己先处理请求和响应体,然后重新生成1个新的数据返回出去。网上大多数都是这种方案。
     
     想起组件本身提供的有ModifyRequestBodyGatewayFilterFactory和ModifyResponseBodyGatewayFilterFactory来对请求和响应体进行修改。
     
     不过研究之后发现。这两个类生成的是GatewayFilter路由过滤器,而我们需要GlobalFilter(全局过滤器)
     
     要么把这个类拷贝出来重新自己实现1个基于全局过滤器的版本。不过这样代码改动比较大,而且重复严重,哪怕是跟组件本身的代码重复这也是不允许的。不提倡重复造轮子。

     想到 GatewayFilter和GlobalFilter过滤器除了实现的接口不同,它要实现的方法签名都是相同的。既然官方已经实现了GatewayFilter版本的,我应该可以代理直接使用。
     
     设计思路:提供一个类专门用来处理请求和响应。可以实现接口RewriteFunction,为了统一处理,输入输出参数都用byte[].代码如下:
     
     ```
      
     import java.util.stream.Collectors;
     import org.reactivestreams.Publisher;
     import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction;
     import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
     import org.springframework.http.HttpHeaders;
     import org.springframework.http.MediaType;
     import org.springframework.http.server.reactive.ServerHttpRequest;
     import org.springframework.http.server.reactive.ServerHttpResponse;
     import org.springframework.web.server.ServerWebExchange;
     
     import lombok.extern.slf4j.Slf4j;
     import reactor.core.publisher.Mono;
     /**
      * 泛型参数1:源请求体类,源响应体
      * 泛型参数2:新请求体类,新响应体
      * @author cqyhm
      *
      */
     @Slf4j
     public class BodyRewrite implements RewriteFunction<byte[], byte[]> {
        
        /**
         * 在执行全局请求或响应的过滤器的时候会执行该方法,并把请求体或响应体传递进来。
         * @param exchange 网关处理上下文
         * @param body 源请求或响应体
         * @return 返回处理过的请求体或响应体
         */
         @Override
         public Publisher<byte[]> apply(ServerWebExchange exchange, byte[] body) {
            //如果路由没有完成应该是请求过滤器执行
             if(!ServerWebExchangeUtils.isAlreadyRouted(exchange)) {            
                 exchange.getAttributes().put("request_key", new String(body));  //保存请求体到全局上下文中
                 exchange.getAttributes().put("startTime", System.currentTimeMillis()); //保存启动时间到上下中
                //TODO 可以在这里对请求体进行修改
             } else { //已经路由应该是响应过滤器执行
                //TODO 可以在这里对响应体进行修改
                 response(exchange, body);
             }
             return Mono.just(body);
         }
        /**
         *  打印输出响应的参数,请求体,响应体,请求头部,响应头部,请求地址,请求方法等。
         *  @param exchange 网关处理上下文
         *  @param body 源请求或响应体
         *  @return 返回处理过的请求体或响应体
         */
         public byte[] response(ServerWebExchange exchange,  byte[] responseBody) {
             try {
                 ServerHttpRequest request=exchange.getRequest();
                 ServerHttpResponse response=exchange.getResponse();
                 String requestbody=exchange.getAttribute("request_key");
                 Long startTime=exchange.getAttributeOrDefault("startTime", 0L);
                 Long time=System.currentTimeMillis()-startTime;
                 boolean flag=MediaType.APPLICATION_JSON.isCompatibleWith(response.getHeaders().getContentType());
                 //responseBody=objectMapper.writeValueAsString(MessageBox.ok());
                 log.info("\n[{}]请求地址:\n\t{} {}\n[{}]请求头部:\n{}\n[{}]路径参数:\n{}\n[{}]请求参数:\n{}"
                          + "\n[{}]响应头部:\n{}\n[{}]响应内容:\n\t{}\n[{}]执行时间[{}]毫秒", 
                         request.getId(), request.getMethod(), request.getPath(),
                         request.getId(), headers(request.getHeaders()), 
                         request.getId(), request(request),
                         request.getId(), requestbody,
                         request.getId(), headers(response.getHeaders()),
                         request.getId(), flag ? new String(responseBody) : "非JSON字符串不显示", 
                         request.getId(),    time);
                 //TODO 可以对响应体进行修改
                 return responseBody;        
             } catch (Exception e) {
                 throw new RuntimeException("响应转换错误");
             } finally {
                 exchange.getAttributes().remove("request_key");
                 exchange.getAttributes().remove("startTime");
             }
         }
         
         public String headers(HttpHeaders headers) {
             return headers.entrySet().stream()
                     .map(entry -> "\t" + entry.getKey() + ": [" + String.join(";", entry.getValue()) + "]")
                     .collect(Collectors.joining("\n"));
         }
         /**
          * 处理其它get请求参数
          */
         public String request(ServerHttpRequest request) {
             String params=request.getQueryParams().entrySet().stream()
                     .map(entry -> "\t" + entry.getKey() + ": [" + String.join(";", entry.getValue()) + "]")
                     .collect(Collectors.joining("\n"));
             return params;
         }
     }
     ```
        业务处理逻辑写好之后,剩下的就是配置了。我这里只是把参数打印日志没有做处理,你甚至可以在这里修改请求体和响应体。
        代理ModifyRequestBodyGatewayFilterFactory和ModifyResponseBodyGatewayFilterFactory类中的GatewayFilter来定义GlobalFilter过滤器。

          这里是使用内部类直接实现的,但是指定GlobalFilter的顺序有问题导致无法监控,最好的办法还是单独设计两个GlobalFilter过滤器类。
        
        查看代码:
    ```
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.cloud.gateway.filter.GatewayFilter;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
    import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.annotation.Order;
    import org.springframework.web.server.ServerWebExchange;

    import reactor.core.publisher.Mono;

    @Configuration
    @ConditionalOnProperty(prefix = "logging.filter", name = "enabled", havingValue = "true", matchIfMissing = false)
    public class CustomGatewayConfig {
        
        @Bean
        public BodyRewrite bodyRewrite() {
            return new BodyRewrite();
        }
        /**
         * 定义全局拦截器拦截请求体
         */
        @Bean
        @Order(NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 2)   //指定顺序必须在之前
        public GlobalFilter requestFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBody) {        
            GatewayFilter delegate=modifyRequestBody.apply(new ModifyRequestBodyGatewayFilterFactory.Config()
                                                     .setRewriteFunction(byte[].class, byte[].class, bodyRewrite()));
            return new GlobalFilter() {
                @Override
                public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                    return delegate.filter(exchange, chain); 
                }
            };
        }
        /**
         * 定义全局拦截器拦截响应体
         */
        @Bean
        @Order(NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 2) //指定顺序必须在之前
        public GlobalFilter responseFilter(ModifyResponseBodyGatewayFilterFactory modifyResponseBody) {
            GatewayFilter delegate=modifyResponseBody.apply(new ModifyResponseBodyGatewayFilterFactory.Config()
                                                     .setRewriteFunction(byte[].class, byte[].class, bodyRewrite()));
            return new GlobalFilter() {
                @Override
                public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                    return delegate.filter(exchange, chain);
                }
            };
        }
    }
    ```
        关键的地方就在于官方提供的类ModifyResponseBodyGatewayFilterFactory提供的方法apply
    ```    
        public GatewayFilter apply(Config config);
    ```    
        该方法返回的是GatewayFilter我们正好可以利用它的方法filter方法
    ```    
        public Mono<Void> filter(ServerWebExchange exchange,GatewayFilterChain chain) 
    ```
       我们只需要构造1个它的参数config就好了。分析源代码发现它的参数有个setRewriteFunction函数
       
    ```
       /**
        * @param inClass   请求体的原始数据类型,我们统一使用byte[]
        * @param outClass 请求体变更过后的数据类型,我们统一使用byte[]
        * @param rewriteFunction 对请求体进行处理的类。就是我们前面定义的BodyRewrite的对象
        */
       public <T, R> Config setRewriteFunction(Class<T> inClass, Class<R> outClass, RewriteFunction<T, R> rewriteFunction)   
       
    ```   
      执行了ModifyResponseBodyGatewayFilterFactory的apply方法传入config参数能够得到1个唯1个GatewayFilter对象
    ```  
      GatewayFilter delegate=modifyResponseBody.apply(new ModifyResponseBodyGatewayFilterFactory.Config()
                                               .setRewriteFunction(byte[].class, byte[].class, bodyRewrite()));
       
    ```
      只需要在我们自定义的 GlobalFilter过滤器中调用delegate.filter(exchange, chain)方法
    ```  
      return new GlobalFilter() {
          @Override
          public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
              return delegate.filter(exchange, chain);
          }
      };
    ```

          以上简化的方法设置可能存在在某些版本上无法监控的问题,只需要把请求过滤器和响应过滤器完全独立出来开发就可以了。具体为什么,我还没来得及分析。以下是请求过滤器。响应过滤器类似。

    ```

    import org.springframework.cloud.gateway.filter.GatewayFilter;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
    import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory;
    import org.springframework.core.Ordered;
    import org.springframework.web.server.ServerWebExchange;

    import reactor.core.publisher.Mono;

    /**
     * 全局请求体过滤器
     * @author cqyhm
     *
     */
    public class RequestBodyFilter implements GlobalFilter, Ordered {
        
        private GatewayFilter delegate;
        public RequestBodyFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBody, BodyRewrite bodyRewrite) {
            this.delegate=modifyRequestBody.apply(new ModifyRequestBodyGatewayFilterFactory.Config()
                                                                 .setRewriteFunction(byte[].class, byte[].class, bodyRewrite));
        }

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return delegate.filter(exchange, chain);
        }
        
        @Override
        public int getOrder() {
            return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 2;
        }
    }
    ```

    至于响应过滤器跟这个差不多,一葫芦画瓢很快就能写1个出来。

    ```

    /**
     * 全局响应体过滤器
     */
    public class ResponseBodyFilter implements GlobalFilter, Ordered  {
        
        private GatewayFilter delegate;
        public ResponseBodyFilter(ModifyResponseBodyGatewayFilterFactory modifyResponseBody, BodyRewrite bodyRewrite) {
            this.delegate=modifyResponseBody.apply(new ModifyResponseBodyGatewayFilterFactory.Config()
                                                                    .setRewriteFunction(byte[].class, byte[].class, bodyRewrite));
        }

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return delegate.filter(exchange, chain);
        }
        
        @Override
        public int getOrder() {
            return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 2;
        }
    }

    ```

     然后注册这两个Bean基本上都可以了。
      这种方案应该是最将简便的,最优雅的,只需要实现我们的业务类BodyRewrite其它都直接使用Java config配置,对系统改动小,侵入性低。
      
      这种方案目前正在验证当中,有什么问题欢迎交流。
      好了,这就大功告成了。
     

     

    更多相关内容
  • http请求体和响应体图解

    千次阅读 2018-08-20 21:19:21
  • @ApiModel("请求响应结果VO") public class ResponseVo { /** * 姓名 */ @JsonProperty @ApiModelProperty(value = "姓名") private String CName; /** * 年龄 */ @JsonProperty @ApiModelProperty("年龄") ...

    一、简单的方法

    1、直接定义请求DTO

    package com.xiaoduye0814.t3.pojo;
    
    import com.fasterxml.jackson.annotation.JsonIgnore;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    
    @ApiModel("請求DTO")
    public class RequestDTO {
    
            /**
             * 姓名
             */
            @JsonProperty
            @ApiModelProperty(value = "用戶名",example = "xiaoduye")
            private String CName;
    
            /**
             * 年龄
             */
            @JsonProperty
            @ApiModelProperty(value = "年龄",example = "18")
            private Integer NAge;
    
            @JsonIgnore
            public String getCName() {
                return CName;
            }
    
            @JsonIgnore
            public void setCName(String CName) {
                this.CName = CName;
            }
    
            @JsonIgnore
            public Integer getNAge() {
                return NAge;
            }
    
            @JsonIgnore
            public void setNAge(Integer NAge) {
                this.NAge = NAge;
            }
        }
    

    2、定义返回VO

    package com.xiaoduye0814.t3.pojo;
    
    import java.util.Date;
    
    import com.fasterxml.jackson.annotation.JsonIgnore;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    
    /**
     * @Description 响应返回值
     * @Author xiaoduye
     * @Date 2019-03-29
     *
     */
    @ApiModel(value = "响应返回值", description = "用户接口的响应返回数据")
    public class ResponseVo {
    
        /**
         * 姓名
         */
        @JsonProperty
        @ApiModelProperty(value = "姓名")
        private String CName;
    
        /**
         * 年龄
         */
        @JsonProperty
        @ApiModelProperty(value = "年龄")
        private Integer NAge;
    
        @JsonIgnore
        public String getCName() {
            return CName;
        }
    
        @JsonIgnore
        public void setCName(String CName) {
            this.CName = CName;
        }
    
        @JsonIgnore
        public Integer getNAge() {
            return NAge;
        }
    
        @JsonIgnore
        public void setNAge(Integer NAge) {
            this.NAge = NAge;
        }
    }
    

    二、麻烦的方法

    1、创建ApiJsonObject 类

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ApiJsonObject {
    
        ApiJsonProperty[] value(); //对象属性值
    
        String name();  //对象名称
    
    }
    

    2、创建接口ApiJsonProperty

        import java.lang.annotation.ElementType;
        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;
        import java.lang.annotation.Target;
        
        @Target(ElementType.ANNOTATION_TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface ApiJsonProperty {
        
            String key();  //key
        
            String example() default "";
        
            String type() default "string";  //支持string 和 int
        
            String description() default "";
        }
    

    3、创建MapApiReader类

    import java.util.Map;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import com.fasterxml.classmate.TypeResolver;
    import com.google.common.base.Optional;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtField;
    import javassist.Modifier;
    import javassist.NotFoundException;
    import javassist.bytecode.AnnotationsAttribute;
    import javassist.bytecode.ConstPool;
    import javassist.bytecode.annotation.Annotation;
    import javassist.bytecode.annotation.IntegerMemberValue;
    import javassist.bytecode.annotation.StringMemberValue;
    import springfox.documentation.schema.ModelRef;
    import springfox.documentation.service.ResolvedMethodParameter;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spi.service.ParameterBuilderPlugin;
    import springfox.documentation.spi.service.contexts.ParameterContext;
    
    @Component
    @Order   //plugin加载顺序,默认是最后加载
    public class MapApiReader implements ParameterBuilderPlugin {
        @Autowired
        private TypeResolver typeResolver;
    
        @Override
        public void apply(ParameterContext parameterContext) {
            ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter();
    
            if (methodParameter.getParameterType().canCreateSubtype(Map.class) || methodParameter.getParameterType().canCreateSubtype(String.class)) { //判断是否需要修改对象ModelRef,这里我判断的是Map类型和String类型需要重新修改ModelRef对象
                Optional<ApiJsonObject> optional = methodParameter.findAnnotation(ApiJsonObject.class);  //根据参数上的ApiJsonObject注解中的参数动态生成Class
                if (optional.isPresent()) {
                    String name = optional.get().name();  //model 名称
                    ApiJsonProperty[] properties = optional.get().value();
    
                    parameterContext.getDocumentationContext().getAdditionalModels().add(typeResolver.resolve(createRefModel(properties, name)));  //像documentContext的Models中添加我们新生成的Class
    
                    parameterContext.parameterBuilder()  //修改Map参数的ModelRef为我们动态生成的class
                            .parameterType("body")
                            .modelRef(new ModelRef(name))
                            .name(name);
                }
            }
    
        }
    
        private final static String basePackage = "com.xx.xxx.in.swagger.model.";  //动态生成的Class名
    
        /**
         * 根据propertys中的值动态生成含有Swagger注解的javaBeen
         */
        private Class createRefModel(ApiJsonProperty[] propertys, String name) {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass = pool.makeClass(basePackage + name);
    
            try {
                for (ApiJsonProperty property : propertys) {
                    ctClass.addField(createField(property, ctClass));
                }
                return ctClass.toClass();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 根据property的值生成含有swagger apiModelProperty注解的属性
         */
        private CtField createField(ApiJsonProperty property, CtClass ctClass) throws NotFoundException, CannotCompileException {
            CtField ctField = new CtField(getFieldType(property.type()), property.key(), ctClass);
            ctField.setModifiers(Modifier.PUBLIC);
    
            ConstPool constPool = ctClass.getClassFile().getConstPool();
    
            AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
            Annotation ann = new Annotation("io.swagger.annotations.ApiModelProperty", constPool);
            ann.addMemberValue("value", new StringMemberValue(property.description(), constPool));
            if (ctField.getType().subclassOf(ClassPool.getDefault().get(String.class.getName())))
                ann.addMemberValue("example", new StringMemberValue(property.example(), constPool));
            if (ctField.getType().subclassOf(ClassPool.getDefault().get(Integer.class.getName())))
                ann.addMemberValue("example", new IntegerMemberValue(Integer.parseInt(property.example()), constPool));
    
            attr.addAnnotation(ann);
            ctField.getFieldInfo().addAttribute(attr);
    
            return ctField;
        }
    
        private CtClass getFieldType(String type) throws NotFoundException {
            CtClass fileType = null;
            switch (type) {
                case "string":
                    fileType = ClassPool.getDefault().get(String.class.getName());
                    break;
                case "int":
                    fileType = ClassPool.getDefault().get(Integer.class.getName());
                    break;
            }
            return fileType;
        }
    
        @Override
        public boolean supports(DocumentationType delimiter) {
            return true;
        }
    }
    

    4、对应controller层:
    (1) 请求注释

        @PostMapping("/请求地址")
        @ApiOperation(value = "接口名称", notes = "接口描述")
       实体类接收pojo【返回值类型】  getSomething【方法名称】(
              		    @ApiJsonObject(name = "请求参数的总名称", value = {
                        @ApiJsonProperty(key = "CZjhaoma", example = "211381122245154541", description = "证件号码【String类型】"),
                        @ApiJsonProperty(key = "Size", example = "100", description = "大小【int类型】"),
                        @ApiJsonProperty(key = "StartTime", example = "2019-04-16 00:00:00.000", description = "开始时间"),
                        @ApiJsonProperty(key = "Hobby", example = "[' ']", description = "爱好【list类型】")
        
                }) @RequestBody Map<String, Object> 请求参数的总名称);
    

    (2) 响应注释
    实体类:

    @Data
    @ApiModel("请求响应结果VO")
    public class ResponseVo {
    
    /**
     * 姓名
     */
    @JsonProperty
    @ApiModelProperty(value = "姓名")
    private String CName;
    
    /**
     * 年龄
     */
    @JsonProperty
    @ApiModelProperty("年龄")
    private Integer NAge;
    
    @JsonIgnore
    public String getCName() {
        return CName;
    }
    
    @JsonIgnore
    public void setCName(String CName) {
        this.CName = CName;
    }
    
    @JsonIgnore
    public Integer getNAge() {
        return NAge;
    }
    
    @JsonIgnore
    public void setNAge(Integer NAge) {
        this.NAge = NAge;
    }
    

    5、所需架包 javassist.jar 请自行提取
    链接:https://pan.baidu.com/s/1zrwwxO1M6CWDuWsufqYtUg
    提取码:vvje

    6、下面是我自己做的一个swagger接口的model,大家可以直接拿去使用
    链接:https://pan.baidu.com/s/1ltwjw2AL7yMN0Elf2FtWEA
    提取码:pc0e

    接口截图如下:
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    展开全文
  • Request Body:请求体 HTTP请求报文由3部分组成(请求行+请求头+请求体): 下面是一个实际的请求报文: ①是请求方法,HTTP/1.1 定义的请求方法有8种:GET、POST、...

    HTTP请求报文解剖
    HTTP Request :HTTP请求
    Request Line:请求行
    Header:请求头
    Request Body:请求体

    HTTP请求报文由3部分组成(请求行+请求头+请求体):

     

    下面是一个实际的请求报文:

     

     

    ①是请求方法,HTTP/1.1 定义的请求方法有8种:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE,最常的两种GET和POST,如果是RESTful接口的话一般会用到GET、POST、DELETE、PUT。
    ②为请求对应的URL地址,它和报文头的Host属性组成完整的请求URL
    ③是协议名称及版本号。
    ④是HTTP的报文头,报文头包含若干个属性,格式为“属性名:属性值”,服务端据此获取客户端的信息。
    ⑤是报文体,它将一个页面表单中的组件值通过param1=value1&param2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。不但报文体可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1&param2=value2”的方式传递请求参数。
    对照上面的请求报文,我们把它进一步分解,你可以看到一幅更详细的结构图:

     
     
    HTTP响应报文解剖
    HTTP的响应报文也由三部分组成(响应行+响应头+响应体):
     
    以下是一个实际的HTTP响应报文:
     
    ①报文协议及版本;
    ②状态码及状态描述;
    ③响应报文头,也是由多个属性组成;
    ④响应报文体,即我们真正要的“干货”。

     

    响应状态码
    和请求报文相比,响应报文多了一个“响应状态码”,它以“清晰明确”的语言告诉客户端本次请求的处理结果。
    HTTP的响应状态码由5段组成:

    1xx 消息,一般是告诉客户端,请求已经收到了,正在处理,别急...
    2xx 处理成功,一般表示:请求收悉、我明白你要的、请求已受理、已经处理完成等信息.
    3xx 重定向到其它地方。它让客户端再发起一个请求以完成整个处理。
    4xx 处理发生错误,责任在客户端,如客户端的请求一个不存在的资源,客户端未被授权,禁止访问等。
    5xx 处理发生错误,责任在服务端,如服务端抛出异常,路由出错,HTTP版本不支持等。

    200 OK
    你最希望看到的,即处理成功!
    303 See Other
    我把你redirect到其它的页面,目标的URL通过响应报文头的Location告诉你。

    悟空:师傅给个桃吧,走了一天了[图片上传失败...(image-3001d7-1513152011799)]
    唐僧:我哪有桃啊!去王母娘娘那找吧[图片上传失败...(image-a39592-1513152011799)]
    

    304 Not Modified
    告诉客户端,你请求的这个资源至你上次取得后,并没有更改,你直接用你本地的缓存吧,我很忙哦,你能不能少来烦我啊!
    404 Not Found
    你最不希望看到的,即找不到页面。如你在google上找到一个页面,点击这个链接返回404,表示这个页面已经被网站删除了,google那边的记录只是美好的回忆。
    500 Internal Server Error
    看到这个错误,你就应该查查服务端的日志了,肯定抛出了一堆异常,别睡了,起来改BUG去吧!

     
    转载自:https://www.jianshu.com/p/eb3e5ec98a66
     

    转载于:https://www.cnblogs.com/heyjia/p/11163620.html

    展开全文
  • 一旦收到请求,服务器会向客户端返回一个状态,比如“HTTP/1.1 200 OK”,以及返回的内容,如请求的文件、错误消息、或者其他信息,这就是服务器端的响应。 常见的请求头 1、 GET或POST:请求类型,后接请求资源、...
  • http请求,get请求post请求体以及响应体

    万次阅读 多人点赞 2016-11-20 16:02:20
    该请求的内容格式如下所示: 请求首行 ...请求正文,也称请求体 2.使用HttpWatch抓包工具 请求信息详细内容如下: GET /Example03/ HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/g
  • response对象request对象 response对象(响应对象) 想要获取客户端的内容,使用request对象。对客户端做出响应使用response对象 响应 响应行 状态码: void setStatus(int sc) 设置状态码 响应头(key:value...
  • Request Body:请求体 HTTP请求报文由3部分组成(请求行+请求头+请求体): 下面是一个实际的请求报文: 其中,①,②③属于请求行;④属于请求头;⑤属于报文体 ① 是请求方法,HTTP/1.1 定义的请求方法有8种:...
  • SpringBoot控制器统一的响应体加密与请求体解密的注解处理方式,支持MD5/SHA/AES/DES/RSA
  • 谷歌浏览器查看http请求,请求头请求体响应数据状态
  • HTTP请求报文由3部分组成(请求行+请求头+请求体): 请求行: ①是请求方法,GETPOST是最常见的HTTP方法,除此以外还包括DELETE、HEAD、OPTIONS、PUT、TRACE。 ②为请求对应的URL地址,它报文头的Host属性...
  • HTTP中的请求和响应

    千次阅读 2021-11-18 11:14:30
    请求头 1.Accept 告诉服务器,客户端支持的数据类型 2.Accept-Encoding 告诉服务器,客户机支持的数据压缩格式。 3.Accept-Language 告诉服务器,客户机的语言环境。 4.Connection 客户机通过这个头告诉服务器,...
  • 请求到了Gateway后,可以被Gateway做了修改,再转发给真正的服务提供者,这些小把戏,有时候也能发挥重要的作用
  • 什么是请求报文和响应报文?

    千次阅读 2022-04-28 22:15:23
    在了解请求报文和响应报文之前,咱们先了解下什么是HTTP协议? http协议: 网络传输协议 协议:规定网络数据传输格式 --- http协议组成: 请求报文+响应报文 浏览器发请求 必须是 : 请求报文... (3)请求体:浏览器发送...
  • 1. 响应编码  * 当使用response.getWriter()来向客户端发送字符数据时,如果在之前没有设置编码,那么默认使用iso,因为iso不支持中文,一定乱码  * 在使用response.getWriter()之前可以使用response....
  • HTTP请求响应体报文(行,头,体)

    万次阅读 多人点赞 2018-11-28 00:08:14
    响应体(4):是服务器返回给客户端的文本信息。   HTTP响应报文属性 Cache-Control  :响应输出到客户端后,服务端通过该报文头属告诉客户端如何控制响应内容的缓存。常见的有:(默认 为private) ...
  • flask 请求数据和响应

    千次阅读 2021-10-25 16:18:52
    请求带参 1、路径中带参 固定参数是指在URL中固定的, 是不可获取的一部分, 区别与查询字符串, 查询字符串是可有可无的. 标准格式 /path/<参数> 默认 参数 格式是字符串 但是我们还有其他的格式,可以...
  • 请求报文与响应报文的组成请求报文组成部分一、请求行1、 请求方法2、统一资源定位符3、http版本号二、请求头三、空一行四、请求体响应报文组成部分一、响应行1、http的协议以及版本(同上)2、状态码3、是否被成功...
  • Get请求是把参数放在URL中(无请求体),会将数据暴露在请求地址上,而Post请求是通过请求体requestBody来传递参数的 GetPost是HTTP协议中的两种发送请求方法 那么HTTP又是什么呢? HTTP是基于TCP/IP中关于...
  • python http的请求和响应

    千次阅读 2022-03-28 11:05:51
    python http的请求和响应
  • 一般情况下,controller中入参很少需要HttpServletRequestHttpServletResponse参数,但是有时我们需要对访问参数进行日志打印,查看请求ip或入参等信息时,就需要在入参加上requestresponse,这样每个...
  • Python爬虫之HTTP请求和响应

    千次阅读 2021-11-15 17:41:51
    请求,由客户端向服务端发出,可以分为4个部分内容:请求方法(Request Method),请求的网址(Request URL)、请求头(Request Header)、请求体(Request Body)。 常见的请求方法有两种:GETPOST。在浏览器中...
  • 【网络】HTTP请求报文和响应报文

    千次阅读 2022-02-25 10:22:42
    请求体 请求行 请求方法:GETPOST是最常见的HTTP方法,除此之外还包括DELETE、HEAD、OPTIONS、PUT、TRANCE,不过当前的大多数浏览器都只支持GETPOST URL:为请求对应的URL 版本号:既HTTP协议的版本 请求头 ...
  • 1. 获取post请求的下游微服务返回的响应内容: import java.nio.charset.Charset; import org.reactivestreams.Publisher; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org....
  • Wireshark的HTTP请求和响应包如何对应

    万次阅读 多人点赞 2018-10-02 13:33:34
    以Wireshark2.6.3版本为例,如下图所示,红框中的803是一次HTTP的GET请求包,绿框中的809、810两条记录都是响应包,究竟哪个是803的响应包呢?接下来介绍两种方式识别; 通过传输控制协议信息识别 如下图,点击...
  • HTTP概述—请求头和请求体

    千次阅读 2020-03-03 17:28:53
    基于请求/响应模型的:一次请求对应一次响应 无状态的:每次请求之间相互独立,不能交互数据 历史版本: 1.0:每一次请求响应都会建立新的连接 1.1:复用连接 请求消息数据格式: 1. 请求行 ...
  • 一、通信 1. 通信是信息的传递交换。 2. 通信三要素 通信主体、通信内容、通信方式。...采用【请求-响应】的交互模式。 2. HTTP发展 版本 产生时间 内容 HTTP/0.9 1991年 不涉及数据包传输;规定客
  • HTTP 请求和响应包 (网络篇)

    万次阅读 多人点赞 2018-07-17 18:10:12
    请求包:请求行,请求头,【空行】,请求体 http://www.gov.cn/xinwen/2018-07/17/content_5307156.htm 【请求行】组成规则:方法 /url HTTP/版本号 例:GET /xinwen/2018-07/17/content_5307156.htm HTTP/1.1 ...
  • 请求和响应详解(request&response)

    千次阅读 多人点赞 2020-11-11 14:03:48
    文件下载记忆方式: 两头流:  两个响应头 Content-Type: 告知响应体的数据类型(mime类型) Content-Disposition: 告知浏览器不要渲染响应体,以附件形式处理。  输入流输出流的对接: 输入流: 关联文件的输入流 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 216,920
精华内容 86,768
关键字:

请求体和响应体