精华内容
下载资源
问答
  • @RequestBody的使用

    万次阅读 多人点赞 2018-07-09 16:13:57
    提示:建议一定要看后面的@RequestBody的核心逻辑源码以及六个重要结论!本文前半部分的内容都是一些基 本知识常识,可选择性跳过。 声明:本文是基于SpringBoot,进行的演示说明。 基础知识介绍: @Request...

    提示:建议一定要看后面的@RequestBody的核心逻辑源码以及六个重要结论!本文前半部分的内容都是一些基
               本知识常识,可选择性跳过。

    声明本文是基于SpringBoot,进行的演示说明。


    基础知识介绍

            @RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);而最常用的使用请求体传参的无疑是POST请求了,所以使用@RequestBody接收数据时,一般都用POST方式进行提交。在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。

    注:一个请求,只有一个RequestBody;一个请求,可以有多个RequestParam。

    注:当同时使用@RequestParam()和@RequestBody时,@RequestParam()指定的参数可以是普通元素、
           数组、集合、对象等等(即:当,@RequestBody 与@RequestParam()可以同时使用时,原SpringMVC接收
           参数的机制不变,只不过RequestBody 接收的是请求体里面的数据;而RequestParam接收的是key-value
           里面的参数
    ,所以它会被切面进行处理从而可以用普通元素、数组、集合、对象等接收)。
           即:如果参数时放在请求体中,application/json传入后台的话,那么后台要用@RequestBody才能接收到;
                 如果不是放在请求体中的话,那么后台接收前台传过来的参数时,要用@RequestParam来接收,或
                 则形参前 什么也不写也能接收。

    注:如果参数前写了@RequestParam(xxx),那么前端必须有对应的xxx名字才行(不管其是否有值,当然可以通
           过设置该注解的required属性来调节是否必须传),如果没有xxx名的话,那么请求会出错,报400。

    注:如果参数前不写@RequestParam(xxx)的话,那么就前端可以有可以没有对应的xxx名字才行,如果有xxx名
           的话,那么就会自动匹配;没有的话,请求也能正确发送。
           追注:这里与feign消费服务时不同;feign消费服务时,如果参数前什么也不写,那么会被默认是
                      @RequestBody的。

    如果后端参数是一个对象,且该参数前是以@RequestBody修饰的,那么前端传递json参数时,必须满足以下要求:

    • 后端@RequestBody注解对应的类在将HTTP的输入流(含请求体)装配到目标类(即:@RequestBody后面的类)时,会根据json字符串中的key来匹配对应实体类的属性,如果匹配一致且json中的该key对应的值符合(或可转换为),这一条我会在下面详细分析,其他的都可简单略过,但是本文末的核心逻辑代码以及几个结论一定要看! 实体类的对应属性的类型要求时,会调用实体类的setter方法将值赋给该属性。

    • json字符串中,如果value为""的话,后端对应属性如果是String类型的,那么接受到的就是"",如果是后端属性的类型是Integer、Double等类型,那么接收到的就是null

    • json字符串中,如果value为null的话,后端对应收到的就是null。

    • 如果某个参数没有value的话,在传json字符串给后端时,要么干脆就不把该字段写到json字符串中;要么写value时, 必须有值,null  或""都行。千万不能有类似"stature":,这样的写法,如:

    注:关于@RequestParam()的用法,这里就不再一一说明了,可详见 《程序员成长笔记(一)》中的相关章节。


    示例详细说明

    先给出两个等下要用到的实体类

    User实体类:

    Team实体类:

    @RequestBody直接以String接收前端传过来的json数据:

    后端对应的Controller:

    使用PostMan测试:

    @RequestBody以简单对象接收前端传过来的json数据:

    后端对应的Controller:

    使用PostMan测试:

    @RequestBody以复杂对象接收前端传过来的json数据:

    后端对应的Controller:

    使用PostMan测试:

    @RequestBody与简单的@RequestParam()同时使用

    后端对应的Controller:

    使用PostMan测试:

    @RequestBody与复杂的@RequestParam()同时使用

    后端对应的Controller:

    使用PostMan测试:

    @RequestBody接收请求体中的json数据;不加注解接收URL中的数据并组装为对象

    后端对应的Controller:

    使用PostMan测试:

    注:如果在后端方法参数前,指定了@RequestParam()的话,那么前端必须要有对应字段才行(当然可以通过设置
           该注解的required属性来调节是否必须传),否者会报错;如果参数前没有任何该注解,那么前端可以传,也可
           以不传,如:

    上图中,如果我们传参中没有指定token,那么请求能正常进去,但是token为null;如果在String token前指定了@RequestParam(“token”),那么前端必须要有token这个键时,请求才能正常进去,否者报400错误。


    @RequestBody与前端传过来的json数据的匹配规则

    声明:根据不同的Content-Type等情况,Spring-MVC会采取不同的HttpMessageConverter实现来进行信息转换解析。
              下面介绍的是最常用的:前端以Content-Type 为application/json,传递json字符串数据;后端以@RequestBody
              模型接收数据的情况。

    解析json数据大体流程概述
            Http传递请求体信息,最终会被封装进com.fasterxml.jackson.core.json.UTF8StreamJsonParser中(提示:Spring采用CharacterEncodingFilter设置了默认编码为UTF-8),然后在public class BeanDeserializer extends BeanDeserializerBase implements java.io.Serializable中,通过 public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException方法进行解析

    核心逻辑分析示例

            假设前端传的json串是这样的: {"name1":"邓沙利文","age":123,"mot":"我是一只小小小小鸟~"} 后端的模型只有name和age属性,以及对应的setter/getter方法给出一般用到的deserializeFromObject(JsonParser p, DeserializationContext ctxt)方法的核心逻辑:


    小技巧之指定模型中的属性对应什么key

    这里简单介绍,更多的可参考:

               public class BeanPropertyMap implements Iterable<SettableBeanProperty>,java.io.Serializable

    给出Controller中的测试类:

    给出模型中的属性(setter/getter方法没截出来):

    使用postman测试一下,示例:

    上图简单测试了一下,但是测得并不全面,这里就不带大家一起测试了直接给出。


    全面的结论

    结论@JsonAlias注解,实现:json转模型时,使json中的特定key能转化为特定的模型属性;但是模型转json时,
                   对应的转换后的key仍然与属性名一致,见:上图示例中的name字段的请求与响应。
                   以下图进一步说明:

                      此时,json字符串转换为模型时,json中key为Name或为name123或为name的都能识别。

    结论@JsonProperty注解,实现:json转模型时,使json中的特定key能转化为指定的模型属性;同样的,模
                   型转json时,对应的转换后的key为指定的key,见:示例中的motto字段的请求与响应。
                   以下图进一步说明:

                   此时,json字符串转换为模型时,key为MOTTO的能识别,但key为motto的不能识别。

    结论@JsonAlias注解需要依赖于setter、getter,而@JsonProperty注解不需要。

    结论在不考虑上述两个注解的一般情况下,key与属性匹配时,默认大小写敏感。

    结论有多个相同的key的json字符串中,转换为模型时,会以相同的几个key中,排在最后的那个key的值给模
                   型属性复制,因为setter会覆盖原来的值。见示例中的gender属性。

    结论后端@RequestBody注解对应的类在将HTTP的输入流(含请求体)装配到目标类(即:@RequestBody后面
                   的类)时,会根据json字符串中的key来匹配对应实体类的属性,如果匹配一致且json中的该key对应的值
                   符合(或可转换为)实体类的对应属性的类型要求时,会调用实体类的setter方法将值赋给该属性。

    ^_^ 如有不当之处,欢迎指正

    ^_^ 代码托管链接
                   https://github.com/JustryDeng...RequestBody...

    ^_^ 本文已经被收录进《程序员成长笔记(二)》,笔者JustryDeng

    展开全文
  • POST、GET、@RequestBody和@RequestParam区别

    万次阅读 多人点赞 2019-08-15 21:26:43
    我们再来看下面这个例子: 在body中,我们还是输入上面的json数据,根据分析,上面的json数据是一个List数组内嵌套着map对象,那么在后台的接收形式可写为 List>,具体代码如下图所示: postman请求: 控制台输出: ...

    @RequestParam

    注解@RequestParam接收的参数是来自HTTP请求体或请求url的QueryString中。

    RequestParam可以接受简单类型的属性,也可以接受对象类型。

    @RequestParam有三个配置参数:

    • required 表示是否必须,默认为 true,必须。
    • defaultValue 可设置请求参数的默认值。
    • value 为接收url的参数名(相当于key值)。

    @RequestParam用来处理 Content-Typeapplication/x-www-form-urlencoded 编码的内容,Content-Type默认为该属性。@RequestParam也可用于其它类型的请求,例如:POST、DELETE等请求

    所以在postman中,要选择body的类型为 x-www-form-urlencoded,这样在headers中就自动变为了 Content-Type : application/x-www-form-urlencoded 编码格式。如下图所示:

    但是这样不支持批量插入数据啊,如果改用 json 字符串来传值的话,类型设置为 application/json,点击发送的话,会报错,后台接收不到值,为 null

    但可以入参后再转换,参考如下:

       @PostMapping("/ali-receive")
        public void aliReceive(@RequestParam("message") String message) {
                    ReceiveLog receiveLog = JSON.parseObject(message, ReceiveLog.class);
    
        }

    解决Spring/SpringBoot @RequestParam注解无法读取application/json格式数据:https://blog.csdn.net/weixin_42536015/article/details/106906055

    @RequestParam 接受JSON的字符串:https://blog.csdn.net/qq_40470612/article/details/104225419

     

    不推荐使用@RequestParam接收application/json,这时候就需要使用到@RequestBody。

     

    @RequestBody


    注解@RequestBody接收的参数是来自requestBody中,即请求体。一般用于处理非 Content-Type: application/x-www-form-urlencoded编码格式的数据,比如:application/jsonapplication/xml等类型的数据。

    application/json类型的数据而言,使用注解@RequestBody可以将body里面所有的json数据传到后端,后端再进行解析。

    GET请求中,因为没有HttpEntity,所以@RequestBody并不适用。

    POST请求中,通过HttpEntity传递的参数,必须要在请求头中声明数据的类型Content-Type,SpringMVC通过使用

    HandlerAdapter 配置的HttpMessageConverters来解析HttpEntity中的数据,然后绑定到相应的bean上。

     

    向表中批量插入数据

    举个批量插入数据的例子,Controller层的写法如下图所示:

    由于@RequestBody可用来处理 Content-Typeapplication/json 编码的内容,所以在postman中,选择body的类型为row -> JSON(application/json),这样在 Headers 中也会自动变为 Content-Type : application/json 编码格式。body内的数据如下图所示:

    批量向表中插入两条数据,这里的 saveBatchNovel()方法已经封装了 JPAsaveAll() 方法。body 里面的 json 语句的 key 值要与后端实体类的属性一一对应。

    注意:前端使用$.ajax的话,一定要指定 contentType: "application/json;charset=utf-8;",默认为 application/x-www-form-urlencoded

    后端解析json数据

    上述示例是传递到实体类中的具体写法,那么如果传递到非实体类中,body里面的json数据需要怎么解析呢?我们再来看下面这个例子:

    在body中,我们还是输入上面的json数据,根据分析,上面的json数据是一个List数组内嵌套着map对象,那么在后台的接收形式可写为 List<Map<String, String>>,具体代码如下图所示:

    postman请求:

    控制台输出:

    得出结论,通过@RequestBody可以解析Body中json格式的数据。

     

     

    POST请求时

    @RequestBody --> JSON字符串部分

    @RequestParam --> 请求参数部分

    application/json格局图

    form-data、x-www-form-urlencoded格局图

     

    1、从content-type方面总结:

    ① form-data、x-www-form-urlencoded:不可以用@RequestBody;可以用@RequestParam。见postman的格局,这两种方式的时候没有json字符串部分。

    ② application/json:json字符串部分可以用@RequestBody;url中的?后面参数可以用@RequestParam。见postman的格局

     

    2、从两种注解方式总结:

    @RequestBody

    (@RequestBody Map map)
    (@RequestBody Object object)
    application/json时候可用
    form-data、x-www-form-urlencoded时候不可用
    

    @RequestParam

    (@RequestParam Map map)
    application/json时候,json字符串部分不可用,url中的?后面添加参数即可用,form-data、x-www-form-urlencoded时候可用,但是要将Headers里的Content-Type删掉
    
    (@RequestParam String waterEleId,@RequestParam String enterpriseName)
    application/json时候,json字符串部分不可用,url中的?后面添加参数即可用
    form-data、x-www-form-urlencoded时候可用,且参数可以没有顺序(即前端传过来的参数或者url中的参数顺序不必和后台接口中的参数顺序一致,只要字段名相同就可以),但是要将Headers里的Content-Type删掉
    
    (@RequestParam Object object)
    不管application/json、form-data、x-www-form-urlencoded都不可用
    

    既不是@RequestBody也不是@RequestParam,没有指定参数哪种接收方式

    (Map map)
    (Object object)
    application/json时候:json字符串部分不可用,url中的?后面添加参数不可用。
    因为没有指定,它也不知道到底是用json字符串部分还是?后面添加参数部分,所以干脆都不可以用
    form-data、x-www-form-urlencoded时都不可用,见图二
    
    (HttpServletRequest request)
    application/json不可用
    form-data、x-www-form-urlencoded时可用
    

    GET请求

    @RequestBody

    RequestBody -- Map / Object
    GET请求中不可以使用@RequestBody
    

    @RequestParam

    (@RequestParam Map map)
    在url中的?后面添加参数即可使用
    
    (@RequestParam String waterEleId,@RequestParam String enterpriseName)
    在url中的?后面添加参数即可使用
    
    (@RequestParam Object object)
    GET请求中不可以使用
    

    当使用GET请求时,通过postman添加?后面的参数,不用在url中自己一个一个拼,点击Params,在下面key-value中输入就自动拼接到url中

     

     

    举栗子

    上传文件,包含了图中圈出来的两部分

    如果这样,没有@RequestParam,那么url?后的参数就拿不到

    @RequestMapping(value = "/leadingIn", method = RequestMethod.POST)
        public ResponseObj<Boolean> leadingIn(Map formData,
                                              HttpServletRequest request,
                                              Map<String, InputStream> files) {
    }
    

    如果control中这样接收,本来想formData只接收url?后的参数,结果将{ "retCode": null, "data": true }这部分内容也拿到了,真实意外之喜。字符串这部分内容还可以从request中取到,见下面完整方法。

    @RequestMapping(value = "/leadingIn", method = RequestMethod.POST)
        public ResponseObj<Boolean> leadingIn(@RequestParam Map formData,
                                              HttpServletRequest request,
                                              Map<String, InputStream> files) {
    }
    

    完整方法

        /**
         * 导入
         */
        @RequestMapping(value = "/leadingIn", method = RequestMethod.POST)
        public ResponseObj<Boolean> leadingIn(@RequestParam Map formData,
                                              HttpServletRequest request,
                                              Map<String, InputStream> files) {
            //测试
            try {
                MultipartHttpServletRequest mulRequest = (MultipartHttpServletRequest) request;
                Set<Map.Entry<String, MultipartFile>> set = mulRequest.getFileMap().entrySet();
                Map<String, InputStream> listFile = new LinkedHashMap<>();
                System.out.println("个数" + set.size());
                for (Map.Entry<String, MultipartFile> each : set) {
                    String fileName = each.getKey();
                    MultipartFile file = each.getValue();
                    //这里需要上传FTP
                    try {
                        listFile.put(fileName, file.getInputStream());
                    } catch (Exception ex) {
                        return new ResponseObj<>(false, null);
                    }
                }
    
                String formjson = mulRequest.getParameter("content");
                ObjectMapper mapper = new ObjectMapper();
                mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
                mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    
    //            boolean result = iInstallWorkOrder.upLoadFile(listFile);
                boolean result = true;
    
                return new ResponseObj<>(result, null);
            } catch (Exception ex) {
                System.out.println(ex.toString());
                return new ResponseObj<>(false, null);
            }
    
        }



    按F12看一下Network里对应请求:

    使用@RequestParam:Content-Type为application/x-www-form-urlencoded,参数在FormData中



    使用@RequestBody:Content-Type为application/json,参数在Request PayLoad中


    总结 •在GET请求中,不能使用@RequestBody。 •在POST请求,可以使用@RequestBody和@RequestParam,但是如果使用@RequestBody,对于参数转化的配置必须统一。
    可以使用多个@RequestParam获取数据,@RequestBody不可以 举个例子,在SpringMVC配置了HttpMessageConverters处理栈中,指定json转化的格式,如Date转成‘yyyy-MM-dd’,则参数接收对象包含的字段如果是Date类型,就只能让客户端传递年月日的格式,不能传时分秒。因为不同的接口,它的参数可能对时间参数有不同的格式要求,所以这样做会让客户端调用同事对参数的格式有点困惑,所以说扩展性不高。 如果使用@RequestParam来接受参数,可以在接受参数的model中设置@DateFormat指定所需要接受时间参数的格式。 另外,使用@RequestBody接受的参数是不会被Servlet转化统一放在request对象的Param参数集中,@RequestParam是可以的。

     

    另外,还有一种应用场景,接口规范为resultful风格时,举个例子:如果要获取某个id下此条问题答案的查询次数的话,则后台就需要动态获取参数,其注解为@PathVariable,并且requestMapping中的value应为value="/{id}/queryNum",截图如下:

     

    参考:

    @RequestParam @RequestBody @PathVariable 等参数绑定注解详解

    https://blog.csdn.net/walkerjong/article/details/7946109

    https://my.oschina.net/u/3372000/blog/906217

    https://www.jianshu.com/p/4981911d5e15

    https://cloud.tencent.com/developer/article/1414464

    展开全文
  • Body 的重试 Body 丢失 之前我们的配置里面,只对 Get 请求针对 500 响应码重试,但是针对像 Post 这样的请求,只对那种根本还没到发送 Body 的阶段的异常(例如连接异常)这种重试,那么如果我们要对带 Body 的 ...

    带 Body 的重试 Body 丢失

    之前我们的配置里面,只对 Get 请求针对 500 响应码重试,但是针对像 Post 这样的请求,只对那种根本还没到发送 Body 的阶段的异常(例如连接异常)这种重试,那么如果我们要对带 Body 的 Post 进行重试呢?或者就是用 Postman 构建一个带 Body 的 Get 请求,重试是否正常呢?

    我们启动之前第6节的 EurekaServer,修改/test-exception-thrown接口,增加 RequestBody 参数:

    @RequestMapping(value = "/test-exception-thrown", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})
    public String testExceptionThrown(HttpServletRequest httpServletRequest, @RequestBody Map<String, String> body) {
        log.info("testExceptionThrow called {}, {}", httpServletRequest.getMethod(), body);
        if (shouldThrowException) {
            throw new IllegalStateException();
        }
        return zone;
    }
    

    启动zone1-service-provider-instance1zone1-service-provider-instance2,其中,zone1-service-provider-instance1是接口访问会抛出异常的那个实例。启动网关,使用 Postman 调用接口,发现出现重试,请求先发送到了zone1-service-provider-instance1,之后重试到了zone1-service-provider-instance2,但是zone1-service-provider-instance2返回 400 错误,也就是没有收到 RequestBody,这是怎么回事呢?

    Api网关调用日志

    2020-07-28 01:55:29.781  INFO [service-api-gateway,fc71e34f22e1bd17,fc71e34f22e1bd17]
                [7860] [boundedElastic-1][com.github.hashjang.hoxton.api.gateway.filter.InstanceCircuitBreakerFilter:54]: try to send request to: http://192.168.0.142:8001/test-exception-thrown: stats: {"numberOfNotPermittedCalls":0,"numberOfSlowCalls":0,"numberOfBufferedCalls":0,"slowCallRate":-1.0,"failureRate":-1.0,"numberOfSuccessfulCalls":0,"numberOfFailedCalls":0,"numberOfSlowSuccessfulCalls":0,"numberOfSlowFailedCalls":0}
    2020-07-28 01:55:30.115  INFO [service-api-gateway,fc71e34f22e1bd17,fc71e34f22e1bd17]
                [7860] [boundedElastic-1][com.github.hashjang.hoxton.api.gateway.filter.InstanceCircuitBreakerFilter:54]: try to send request to: http://192.168.0.142:8001/test-exception-thrown: stats: {"numberOfNotPermittedCalls":0,"numberOfSlowCalls":0,"numberOfBufferedCalls":1,"slowCallRate":-1.0,"failureRate":-1.0,"numberOfSuccessfulCalls":1,"numberOfFailedCalls":0,"numberOfSlowSuccessfulCalls":0,"numberOfSlowFailedCalls":0}
    

    zone1-service-provider-instance1日志:

    2020-07-28 01:55:29.789 ERROR
                [service-provider,,] [24956]
                [XNIO-2 task-4][io.undertow.servlet.api.LoggingExceptionHandler:80]:UT005023: Exception handling request to /test-exception-thrown
    
    

    zone1-service-provider-instance2日志:

    2020-07-28 01:55:30.133  WARN
                [service-provider,fc71e34f22e1bd17,da6d3f91fcfc053f] [24956]
                [XNIO-2 task-5][org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver:199]:Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public java.lang.String com.github.hashjang.hoxton.service.provider.controller.TestServiceController.testExceptionThrown(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)]
    

    为了定位问题,我们添加一个放在最开头的 LogFilter,开启 Body 的追踪:

    @Component
    public class LogFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return chain.filter(exchange.mutate().request(new ServerHttpRequestDecorator(exchange.getRequest()) {
                @Override
                public Flux<DataBuffer> getBody() {
                    //body开启日志,记录操作body的filter
                    return exchange.getRequest().getBody().log();
                }
            }).build());
        }
    
        @Override
        public int getOrder() {
            //放在最开头
            return Ordered.HIGHEST_PRECEDENCE;
        }
    }
    
    

    重启网关,发送请求:

    2020-07-28 02:22:16.026  INFO [service-api-gateway,1ae80e0b643da3c7,1ae80e0b643da3c7]
                [4408] [boundedElastic-1][com.github.hashjang.hoxton.api.gateway.filter.InstanceCircuitBreakerFilter:54]: try to send request to: http://192.168.0.142:8001/test-exception-thrown: stats: {"numberOfNotPermittedCalls":0,"numberOfSlowSuccessfulCalls":0,"numberOfSuccessfulCalls":0,"numberOfSlowFailedCalls":0,"numberOfFailedCalls":0,"failureRate":-1.0,"slowCallRate":-1.0,"numberOfBufferedCalls":0,"numberOfSlowCalls":0}
    2020-07-28 02:22:16.034  INFO [service-api-gateway,,]
                [4408] [reactor-http-nio-4][reactor.util.Loggers$Slf4JLogger:274]: onContextUpdate(Context3{class brave.propagation.TraceContext=1ae80e0b643da3c7/1ae80e0b643da3c7, class org.springframework.cloud.sleuth.instrument.web.client.HttpClientBeanPostProcessor$CurrentClientSpan=NoopSpan(1ae80e0b643da3c7/38ecc8fd2b789c2e), reactor.onDiscard.local=reactor.core.publisher.Operators$$Lambda$1179/0x00000008019d1840@469db4af})
    2020-07-28 02:22:16.034  INFO [service-api-gateway,1ae80e0b643da3c7,1ae80e0b643da3c7]
                [4408] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: onSubscribe([Fuseable] ScopePassingSpanSubscriber)
    2020-07-28 02:22:16.034  INFO [service-api-gateway,1ae80e0b643da3c7,1ae80e0b643da3c7]
                [4408] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: request(unbounded)
    2020-07-28 02:22:16.035  INFO [service-api-gateway,1ae80e0b643da3c7,1ae80e0b643da3c7]
                [4408] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: onNext(PooledSlicedByteBuf(ridx: 0, widx: 10, cap: 10/10, unwrapped: PooledUnsafeDirectByteBuf(ridx: 326, widx: 326, cap: 1024)))
    2020-07-28 02:22:16.035  INFO [service-api-gateway,1ae80e0b643da3c7,1ae80e0b643da3c7]
                [4408] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: onComplete()
    2020-07-28 02:22:16.165  INFO [service-api-gateway,1ae80e0b643da3c7,1ae80e0b643da3c7]
                [4408] [boundedElastic-1][com.github.hashjang.hoxton.api.gateway.filter.InstanceCircuitBreakerFilter:54]: try to send request to: http://192.168.0.142:8002/test-exception-thrown: stats: {"numberOfNotPermittedCalls":0,"numberOfSlowSuccessfulCalls":0,"numberOfSuccessfulCalls":0,"numberOfSlowFailedCalls":0,"numberOfFailedCalls":0,"failureRate":-1.0,"slowCallRate":-1.0,"numberOfBufferedCalls":0,"numberOfSlowCalls":0}
    2020-07-28 02:22:16.169  INFO [service-api-gateway,,]
                [4408] [reactor-http-nio-3][reactor.util.Loggers$Slf4JLogger:274]: onContextUpdate(Context3{class brave.propagation.TraceContext=1ae80e0b643da3c7/1ae80e0b643da3c7, class org.springframework.cloud.sleuth.instrument.web.client.HttpClientBeanPostProcessor$CurrentClientSpan=NoopSpan(1ae80e0b643da3c7/d05978ee5d1cb64c), reactor.onDiscard.local=reactor.core.publisher.Operators$$Lambda$1179/0x00000008019d1840@3a55aa8})
    2020-07-28 02:22:16.170  INFO [service-api-gateway,1ae80e0b643da3c7,1ae80e0b643da3c7]
                [4408] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: onSubscribe([Fuseable] ScopePassingSpanSubscriber)
    2020-07-28 02:22:16.170  INFO [service-api-gateway,1ae80e0b643da3c7,1ae80e0b643da3c7]
                [4408] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: request(unbounded)
    2020-07-28 02:22:16.170  INFO [service-api-gateway,1ae80e0b643da3c7,1ae80e0b643da3c7]
                [4408] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: onComplete()
    

    我们发现,这个 Body 的 Flux 在重试的时候,使用的还是原来同样的 Flux,但是这个 Flux 已经被第一次调用消费过了,所以重试的时候,再去消费,直接返回消费完成,不会有:onNext(PooledSlicedByteBuf(ridx: 0, widx: 10, cap: 10/10, unwrapped: PooledUnsafeDirectByteBuf(ridx: 326, widx: 326, cap: 1024)))

    那么如何解决呢?有两种方式,一种是自己实现 Body 缓存,参考我提的 Issue + PR(https://github.com/spring-cloud/spring-cloud-gateway/pull/1863),但是这实际上是我的乌龙,我没注意到 Spring Cloud Gateway实际上已经实现了:

    Publisher<Void> publisher = chain.filter(exchange.mutate().request(
    	new ServerHttpRequestDecorator(request) {
    		@Override
    		public Flux<DataBuffer> getBody() {
    			int currentIteration = exchange
    					.getAttributeOrDefault(RETRY_ITERATION_KEY, -1);
    			//根据currentIteration判断是否是重试,如果不是,就返回原始Request 的 body Flux
    			//如果是,则返回缓存的String重新生成的Flux,保证重试也有正确的body
    			return currentIteration > -1 ? Flux.from(Mono.just(dataBufferFactory.wrap(((String) exchange.getAttributes().get(BODY)).getBytes()))) :
    					request.getBody().map(dataBuffer -> {
    						if (LEGAL_LOG_MEDIA_TYPES.contains(contentType)) {
    							try {
    								String body = (String) exchange.getAttributes().get(BODY);
    								if (body == null) {
    									byte[] content = new byte[dataBuffer.readableByteCount()];
    									try {
    										dataBuffer.read(content);
    									} finally {
    										DataBufferUtils.release(dataBuffer);
    									}
    									String s = new String(content, Charset.defaultCharset());
    									exchange.getAttributes().put(BODY, s);
    									dataBuffer = dataBufferFactory.wrap(s.getBytes());
    								} else {
    									dataBuffer = dataBufferFactory.wrap(body.getBytes());
    								}
    
    							} catch (Exception e) {
    								log.error("error read body in retry", e);
    							}
    						}
    						return dataBuffer;
    					});
    		}
    	}
    ).build())
    

    另一种是使用 Spring Cloud Gateway 已有的缓存机制AdaptCachedBodyGlobalFilter:
    AdaptCachedBodyGlobalFilter源码:

    public class AdaptCachedBodyGlobalFilter
    		implements GlobalFilter, Ordered, ApplicationListener<EnableBodyCachingEvent> {
    
        /**
    	 * 缓存RequestBody的Route
    	 */
    	private ConcurrentMap<String, Boolean> routesToCache = new ConcurrentHashMap<>();
    
    	/**
    	 * 缓存RequestBody的Attribute Key
    	 */
    	@Deprecated
    	public static final String CACHED_REQUEST_BODY_KEY = CACHED_REQUEST_BODY_ATTR;
    
        /**
    	 * 收到EnableBodyCachingEvent,则将EnableBodyCachingEvent中的RouteId加入到要缓存的Route的Map
    	 */
    	@Override
    	public void onApplicationEvent(EnableBodyCachingEvent event) {
    		this.routesToCache.putIfAbsent(event.getRouteId(), true);
    	}
        //。。。。略
    }
    

    由于我们是全局的重试,所以可以对每一个Route都加上缓存 Body 的机制,所以可以这么实现:

    ApiGatewayConfig

    @Configuration
    @EnableConfigurationProperties(ApiGatewayRetryConfig.class)
    @LoadBalancerClients(defaultConfiguration = CommonLoadBalancerConfig.class)
    public class ApiGatewayConfig {
        @Autowired
        private AdaptCachedBodyGlobalFilter adaptCachedBodyGlobalFilter;
        @Autowired
        private GatewayProperties gatewayProperties;
    
        @PostConstruct
        public void init() {
            //让每一个路径都做body Cache,这样重试有Body的请求的时候,重试的请求不会没有body,因为原始body是一次性的基于netty的FluxReceive
            gatewayProperties.getRoutes().forEach(routeDefinition -> {
                EnableBodyCachingEvent enableBodyCachingEvent = new EnableBodyCachingEvent(new Object(), routeDefinition.getId());
                adaptCachedBodyGlobalFilter.onApplicationEvent(enableBodyCachingEvent);
            });
        }
    }
    

    这样修改后,重启网关,我们再调用触发重试:

    2020-07-28 02:48:18.972  INFO [service-api-gateway,72eba79a3afc324f,72eba79a3afc324f]
                [6784] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: onContextUpdate(Context2{class brave.propagation.TraceContext=72eba79a3afc324f/72eba79a3afc324f, reactor.onDiscard.local=reactor.core.publisher.Operators$$Lambda$1041/0x0000000801979440@119927bc})
    2020-07-28 02:48:18.972  INFO [service-api-gateway,72eba79a3afc324f,72eba79a3afc324f]
                [6784] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: onSubscribe([Fuseable] ScopePassingSpanSubscriber)
    2020-07-28 02:48:18.973  INFO [service-api-gateway,72eba79a3afc324f,72eba79a3afc324f]
                [6784] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: request(unbounded)
    2020-07-28 02:48:18.973  INFO [service-api-gateway,72eba79a3afc324f,72eba79a3afc324f]
                [6784] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: onNext(PooledSlicedByteBuf(ridx: 0, widx: 10, cap: 10/10, unwrapped: PooledUnsafeDirectByteBuf(ridx: 326, widx: 326, cap: 512)))
    2020-07-28 02:48:18.974  INFO [service-api-gateway,72eba79a3afc324f,72eba79a3afc324f]
                [6784] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:274]: onComplete()
    2020-07-28 02:48:18.986  INFO [service-api-gateway,72eba79a3afc324f,72eba79a3afc324f]
                [6784] [boundedElastic-1][com.github.hashjang.hoxton.api.gateway.filter.InstanceCircuitBreakerFilter:54]: try to send request to: http://192.168.0.142:8001/test-exception-thrown: stats: {"numberOfNotPermittedCalls":0,"failureRate":-1.0,"slowCallRate":-1.0,"numberOfSlowFailedCalls":0,"numberOfFailedCalls":0,"numberOfSlowSuccessfulCalls":0,"numberOfSuccessfulCalls":0,"numberOfBufferedCalls":0,"numberOfSlowCalls":0}
    2020-07-28 02:48:19.138  INFO [service-api-gateway,72eba79a3afc324f,72eba79a3afc324f]
                [6784] [boundedElastic-1][com.github.hashjang.hoxton.api.gateway.filter.InstanceCircuitBreakerFilter:54]: try to send request to: http://192.168.0.142:8002/test-exception-thrown: stats: {"numberOfNotPermittedCalls":0,"failureRate":-1.0,"slowCallRate":-1.0,"numberOfSlowFailedCalls":0,"numberOfFailedCalls":0,"numberOfSlowSuccessfulCalls":0,"numberOfSuccessfulCalls":1,"numberOfBufferedCalls":1,"numberOfSlowCalls":0}
    

    发现重试调用,Body没有丢,重试成功了

    展开全文
  • 上一章简单介绍了拦截器的使用源码demo,拦截器中获取get参数不会出现任何异常,但是在拦截器中读取了输入流中body内容后,在controller中@RequestBody注解参数无法获取到内容 下面介绍解决这一问题的源码实现. 测试...

    上一章简单介绍了拦截器的使用源码demo,拦截器中获取get参数不会出现任何异常,但是在拦截器中读取了输入流中body内容后,在controller中@RequestBody注解参数无法获取到内容

    下面介绍解决这一问题的源码实现.

    测试源码

    springboot 项目interceptor_demo2 的目录结构如下:
    在这里插入图片描述
    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <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/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.6.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>interceptor_demo2</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>interceptor_demo2</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    

    配置文件application.properties,只配置web server 端口

    server.port=8090
    

    自定义拦截器 TestInterceptor

    package com.example.interceptor_demo2.interceptor;
    
    
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @Component
    public class TestInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("uri="+request.getRequestURI());
            try {
                RequestWrapper requestWrapper = new RequestWrapper(request);
                String body = requestWrapper.getBody();
                System.out.println("拦截器获取到body内容:"+body);
            }catch (Exception e){
                e.printStackTrace();
            }
            return true;
        }
    }
    

    注册拦截器 WebConfigurer

    package com.example.interceptor_demo2.interceptor;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class WebConfigurer implements WebMvcConfigurer {
    
        @Autowired
        private TestInterceptor loginInterceptor;
    
        /**
         * 这个方法是用来配置静态资源的,比如html,js,css,等等
         * @param registry
         */
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
        }
    
        /**
         * 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
         * @param registry
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // addPathPatterns("/**") 表示拦截所有的请求,
    		// addPathPatterns("/test/**") 表示拦截/test/ 下的所有路径请求,
    		// addPathPatterns("/test/*") 表示拦截/test/abc,拦截/test/aaa , 不拦截 /test/abc/def
            // addPathPatterns("/test/**").excludePathPatterns("/test/login", "/test/register") 表示拦截/test/ 下的所有路径请求,但不拦截 /test/login 和 /test/register
            registry.addInterceptor(loginInterceptor).addPathPatterns("/**");
        }
    }
    
    

    RequestWrapper

    package com.example.interceptor_demo2.interceptor;
    
    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.*;
    
    public class RequestWrapper extends HttpServletRequestWrapper {
        private final String body;
    
        public RequestWrapper(HttpServletRequest request) {
            super(request);
            StringBuilder stringBuilder = new StringBuilder();
            BufferedReader bufferedReader = null;
            InputStream inputStream = null;
            try {
                inputStream = request.getInputStream();
                if (inputStream != null) {
                    bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                    char[] charBuffer = new char[128];
                    int bytesRead = -1;
                    while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                        stringBuilder.append(charBuffer, 0, bytesRead);
                    }
                } else {
                    stringBuilder.append("");
                }
            } catch (IOException ex) {
    
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    }
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (bufferedReader != null) {
                    try {
                        bufferedReader.close();
                    }
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            body = stringBuilder.toString();
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
            ServletInputStream servletInputStream = new ServletInputStream() {
                @Override
                public boolean isFinished() {
                    return false;
                }
                @Override
                public boolean isReady() {
                    return false;
                }
                @Override
                public void setReadListener(ReadListener readListener) {
                }
                @Override
                public int read() throws IOException {
                    return byteArrayInputStream.read();
                }
            };
            return servletInputStream;
    
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(this.getInputStream()));
        }
    
        public String getBody() {
            return this.body;
        }
    
    }
    
    

    过滤器 ChannelFilter

    package com.example.interceptor_demo2.interceptor;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    @WebFilter(urlPatterns = "/*",filterName = "channelFilter")
    public class ChannelFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            ServletRequest requestWrapper = null;
            if(servletRequest instanceof HttpServletRequest) {
                requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
            }
            if(requestWrapper == null) {
                System.out.println("servletRequest");
                filterChain.doFilter(servletRequest, servletResponse);
            } else {
                System.out.println("requestWrapper");
                filterChain.doFilter(requestWrapper, servletResponse);
            }
        }
    
        @Override
        public void destroy() {
    
        }
    }
    

    测试类 TestController

    package com.example.interceptor_demo2.controller;
    
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestController {
    
        @RequestMapping("/test")
        public String test(@RequestBody String paramsJson){
            System.out.println("paramsJson"+paramsJson);
            return "controller@RequestBody获取到参数内容:" + paramsJson;
        }
    
    }
    
    

    启动类 InterceptorDemoApplication

    package com.example.interceptor_demo2;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    
    @SpringBootApplication
    //注册过滤器注解
    @ServletComponentScan
    public class InterceptorDemo2Application {
    
        public static void main(String[] args) {
            SpringApplication.run(InterceptorDemo2Application.class, args);
        }
    
    }
    
    

    测试

    使用postman 测试,发送post请求body为json内容 {“key”:“123”} ,如果没有安装postman且没其他测试post请求工具 点击这里下载
    在这里插入图片描述

    源码

    链接:https://pan.baidu.com/s/1knR0rWTR3c3dP7CDJjYG3A
    提取码:7x0j

    最后,需要注意的地方

    上面介绍的 通过 自定义Filter 和 HttpServletRequestWrapper 实现拦截器读取body 且在controller方法中也能读取到body内容,实现原理是通过 Filter 过滤所有接口 读取body内容存储到HttpServletRequestWrapper中字符串变量body中,后面n次读取body内容都是获取HttpServletRequestWrapper中的body内容。

    注意:上面这种方法在读取字符串参数body时 是没有问题的,但是如果客户端post请求的body内容不是字符串而是字节流话,上面方法就会出问题:controller方法读取到的字节流参数(HttpServletRequestWrapper的body) 的大小会比实际上传的字节流数据 大了接近一倍。
    我的解决方法是:在自定义的过滤器和拦截器中 判断request的URI,不要让接收字节流参数的接口 new RequestWrapper ,比如下面自定义Filter中的doFilter方法, /xxxxxx 是一个接收字节流的接口

        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            ServletRequest requestWrapper = null;
            if(servletRequest instanceof HttpServletRequest) {
                if(!((HttpServletRequest) servletRequest).getRequestURI().startsWith("/xxxxxx")){
                    requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
                }
            }
            if(requestWrapper == null) {
                filterChain.doFilter(servletRequest, servletResponse);
            } else {
                filterChain.doFilter(requestWrapper, servletResponse);
            }
        }
    

    同时在Interceptor中 也要像在自定义 Filter中一样 通过请求URI判断,如果是 接收字节流的接口 不要去读取body值,那么这样就不会 使用到HttpServletRequestWrapper, 在Controller 方法中参数读取到字节流就完全是来自客户端请求的字节流内容了。

    展开全文
  • @RequestBody原理

    千次阅读 2021-01-27 15:50:25
    通过Http传递参数一般有两种方式,一种是通过url解析参数,一种是通过body来解决,那么我们本次说的RequestBody就是去解析请求体然后映射到我们的参数,那 么它该如何解析body呢?这就是本篇博客诞生的目的。 这个...
  • GET请求里的body问题

    千次阅读 2020-07-25 11:58:56
    故事还得从一个bug说起。今天有人问我,为什么发到后端的请求400了,我说肯定是参数不对,你去检查检查GET、POST之类...不出所料,后端把GET请求里的参数当成body的内容了,把@RequestBody改成@RequestParam应该就没问
  • nginx收集request_body、response_body

    千次阅读 2020-07-08 21:10:18
    1、收集request_body: 对于get请求,request_body始终是空,对于post请求,request_body是参数信息。request_body的获取有两种方式: 使用nginx ngx_http_core模块的$request_body; openresty中使用lua脚本。 ...
  • 解决Asp.Net Core 3.1 中无法读取HttpContext.Request.Body的问题需求问题描述问题解决解决方案注意事项问题分析附录 需求 根据项目需要,要为WebApi实现一个ExceptionFilter,不仅要将WebApi执行过程中可能产生的...
  • nginx获取request_body

    千次阅读 2020-12-22 13:03:02
    需求与问题工作中经常接到在nginx日志中打印出post请求的body数据的需求,也就是打印出$request_body。而直接在log_format中添加$request_body,经常会得到空值。问题原因查看官网中对$request_body的描述,$request...
  • css里面怎么让body内容居中

    千次阅读 2021-06-13 14:09:27
    css里面让body内容居中的方法:1、使用margin设置边距“0 auto”让HTML页面中所有的元素水平居中;2、将div距离页面窗口左边框和上边框的距离设置为“50%”;3、通过jQuery实现水平和垂直居中。本教程操作环境:...
  • OKhttp FormBody和RequestBody的不同

    万次阅读 2019-01-12 14:29:00
    String post(String url, String json) throws IOException { RequestBody body = RequestBody.create(JSON, json); Request request = new Request.Builder() .url(url) .post(body) .build(); //同步 Response ...
  • 通过JavaScript更改body标签样式

    千次阅读 2021-06-28 08:01:24
    这件事情是这样的:通过JavaScript更改body标签样式function adjustWidth() {width = 0;if (window.innerHeight) {width = window.innerWidth;} else if (document.documentElement && docume...
  • 菜鸟学习nginx之HTTP body接收(2)

    千次阅读 2019-01-13 15:29:20
    上一篇介绍Nginx是如何丢弃body,虽然是丢弃body但是仍然需要乖乖的从socket缓冲区中读取报文才行。本篇将介绍实际接收流程。 一、概要 接收流程是比较复杂的,主要涉及到两个方面考虑:body过长如何保存以及一次...
  • 直接从HttpServletRequest的Reader流中获取请求body参数 @RequestMapping(value = "/nty=", method = RequestMethod.POST) public JSONObject ForwardNtyMsg(HttpServletRequest request) throws IOException { /...
  • 出现接口使用http请求体来传递参数的情况,所以研究了一下feign,发现@Body注解大致可以实现。 虽然说是使用请求体来传递参数,但实质上请求体还是放了json格式的数据。当然,如果不想只局限于json格式,可以将其...
  • print("日程创建成功,body:", body) if __name__ == '__main__': create_schedule(11, 22) 找报错原因,猜测是不是attendees结构复杂,是字典里面嵌套列表又嵌套字典,于是搜索python的post请求body嵌套列表报错,...
  • 2.3.1 继承HttpServletRequestWrapper包装类,每次读取body后,再将参数写会request 2.3.2 将包装类加入过滤器链 3. 参考资料 1. 概述 在使用springboot开发接口时,会将参数转化为Bean,用来进行参数的自动校验...
  • Nginx 记录HTTP请求的body数据

    千次阅读 2020-12-22 13:03:03
    一般我们在开发过程中,尤其是和另外一... 准备nginx 的变量$request_body 即为http请求的body数据只有在 location中 有 proxy_pass,fastcgi_pass,scgi_pass命令存在时,$request_body变量才会有值。反向代理使用 p...
  • 最近调用公司项目一个接口时,发现该接口是一个Get请求,入参在Body 中(json格式)。场景如下:A服务需发送http请求调用B服务的接口(该接口为Get方式,入参是一个json字符串在body中传递) 当我看到这个接口的时候...
  • 的方式获取body参数实现起来比较复杂,因为输入流只能被使用一次。通过使用Spring的aop中point.getArgs()方法可以获取body参数,这种对源代码的侵入性比较小,优先选择。 参考:spring boot aop 中获取requestbody...
  • 文章名称: 如何在ASP.NET Core自定义中间件读取Request.Body和Response.Body的内容? 作者: Lamond Lu 地址: https://www.cnblogs.com/lwqlun/p/10954936.html 源代码: ...
  • public RequestResult switchDefault(@RequestBody Integer id){ deliveryAddressManager.switchDefault(id); return RequestResult.success(); } ![图片说明]...
  • @RequestParam、@ModelAttribute、@RequestBody的区别

    千次阅读 热门讨论 2021-01-06 20:59:23
    当同时使用@RequestParam()和@RequestBody时,@RequestParam()指定的参数可以是普通元素、数组、集合、对象等等 四、@RequestBody 1、@RequestBody简介 @RequestBody主要用来接收前端传递给后端的json字符串中...
  • python 怎么获取request 请求中 body的内容? 谢谢 就是红框中的内容 ![图片说明](https://img-ask.csdn.net/upload/201905/10/1557479798_54096.png) 这是工具里模拟发送的,上图红框中的内容,是写在下面...
  • 解释之前我们先基于Http协议,根据...Body类请求包括但不限于POST、PUSH、PATCH、DELETE 等请求方法。在语义上来说,例如GET请求是查询或者获取服务端的资源的,POST请求是向服务器push数据或者创建资源的。其它同...
  • body设置背景图片

    千次阅读 2021-03-06 23:30:25
    效果 代码 <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> , initial-scale=1.0"> 换肤效果title> <style> body{ background:url(image/changeBack/04.jpg...
  • okhttp之RequestBody

    千次阅读 2020-04-10 15:45:28
    Okhttp里面RequestBody是一个抽象类 抽象方法有: 1)public abstract @Nullable MediaType contentType(); 2)public abstract void writeTo(BufferedSink sink) throws IOException; 非抽象方法: 1) ...
  • 1.使用@RequestBody Object 获取数据 1.1 代码 1.2 获取的请求体 可以看到获取的json串中,键值都没有双引号,在将json转成map的时候,无法实现转换 2.使用@RequestBody JSONObject 获取数据 2.1 代码 ...
  • 菜鸟学习nginx之HTTP body接收(1)

    千次阅读 2019-01-13 15:29:11
    上一篇介绍了Nginx是如何处理HTTP请求的,其实对于一...对于接收body面临比较大的挑战,因为body是不定长,我们无法预先分配内存来接收body。况且http body可能很大,例如都上传一个高清电影,可能就有几个G大小。直...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,579,294
精华内容 1,031,717
关键字:

body

友情链接: Mealy.rar