Springboot Controller多实体接收参数之HandlerMethodArgumentResolver应用与扩展

首先定义两个参数类

1、TestDTO

package com.xx.log.common.pojo.dto;

import com.xx.log.common.pojo.group.GroupV1;
import com.xx.log.common.pojo.group.GroupV2;
import lombok.Data;
import lombok.ToString;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Data
@ToString
public class TestDTO {

    @NotBlank(message = "info 不能为空")
    @NotNull(message = "info 不能为null")
    @Size(min = 1, max = 4, groups = GroupV1.class)
    @Size(min = 6, max = 10, groups = GroupV2.class)
    private String info;

    private String usp;
}

2、Test2DTO

package com.xx.log.common.pojo.dto;

import com.xx.log.common.pojo.group.GroupV1;
import com.xx.log.common.pojo.group.GroupV2;
import lombok.Data;
import lombok.ToString;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Data
@ToString
public class Test2DTO {

    @NotBlank(message = "info 不能为空")
    @NotNull(message = "info 不能为null")
    @Size(min = 1, max = 4, groups = GroupV1.class)
    @Size(min = 6, max = 10, groups = GroupV2.class)
    private String info;

    private String usp;
    private String ip;
}

GET请求

1、controller接收多个参数情况:
直接使用实体作为controller参数,spring会自动处理

    @RequestMapping("/test-get")
    public void testGet(TestDTO testDTO, Test2DTO test2DTO) {
        log.info("testDTO:{} test2DTO:{}", testDTO, test2DTO);
    }

请求地址

http://localhost:10086/test-get?info=tt&ip=kk

打印结果

testDTO:TestDTO(info=tt, usp=null) test2DTO:Test2DTO(info=tt, usp=null, ip=kk)

从结果上看,两个参数都有了值的映射

2、使用 @ModelAttribute 注解

    public void testGet(@ModelAttribute TestDTO testDTO, @ModelAttribute Test2DTO test2DTO) {
        log.info("testDTO:{} test2DTO:{}", testDTO, test2DTO);
    }

效果跟第一种方式是一致的

POST请求

post方式和get方式是有区别的,controller如下:

    @PostMapping("/test2")
    public TestVO test2(@RequestBody TestDTO testDTO, @RequestBody Test2DTO test2DTO) {
        log.info("testDTO:{} test2DTO:{}", testDTO, test2DTO);
        return TestVO.builder().code(0).msg(MixUtil.toJsonString(testDTO)).build();
    }

请求体
在这里插入图片描述
控制台
在这里插入图片描述
看到将requestbody里的值映射到两个参数会报错的,主因就是框架处理带有@RequestBody这种注解的参数时会主动从输入流中读取一次数据,当读取之后会关闭流;第二个参数读取时流已经关闭,所以会抛异常;
通过实验,第二个参数不加 @RequestBody 不会报错,但也获取不到页面传过来的值
所以在设计的时候,设计成一个对象接收 前端请求 是正确的;

spring mvc 除了上面讲的参数映射还有多种其他方式获取参数方式,这里省略了

那如果我硬要把post的controller方法设计成两个参数可以吗(面试的时候会这样问)?答案是可以的,需要借助HandlerMethodArgumentResolver

HandlerMethodArgumentResolver
package org.springframework.messaging.handler.invocation;

import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;

/**
 * Strategy interface for resolving method parameters into argument values
 * in the context of a given {@link Message}.
 *
 * @author Rossen Stoyanchev
 * @since 4.0
 */
public interface HandlerMethodArgumentResolver {

	boolean supportsParameter(MethodParameter parameter);

	@Nullable
	Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception;
}

场景:我们的post方法第一个参数(@RequestBody注解标记)接收前端请求,
第二个参数我们想接收HttpServletRequest 对象,还比如第二个参数我们想用redis通过一个key去拿到值,这个时候就需要HandlerMethodArgumentResolver了,这个对象可以根据我们自定义逻辑识别我们需要的类,进而构建类进行注入。

Spring web 的 HandlerMethodArgumentResolver默认提供有如下若干个实现,这是我们能在controller方法里直接使用其支持对象的原因,我们用的比较多的比如 HttpServletRequest,HttpServletResponse就是下面两个Resolver的功能
在这里插入图片描述

上面说的redis场景我们可以自定义扩展HandlerMethodArgumentResolver即可:

1、自定义一个Redis注解

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Redis {
    
    /**
     * redis中的Key
     */
    String key();    
}

2、扩展 HandlerMethodArgumentResolver

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.util.Random;

/**
 * 从redis中获取值放入到参数中
 *
 */
public class RedisMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
    private static final Logger log = LoggerFactory.getLogger(RedisMethodArgumentResolver.class);
    
    /**
     * 处理参数上存在@Redis注解的
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(Redis.class);
    }
    
    /**
     * 解析参数  伪代码
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) throws Exception {

        // 获取到这个注解
        Redis redis = parameter.getParameterAnnotation(Redis.class);
        // 获取在redis中的key       
        // 从redis中获取值       
        // 返回值
        return “xxxx”;
    }
}

3、注册 RedisMethodArgumentResolver

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    /**
     * 这个地方加载的顺序是在默认的HandlerMethodArgumentResolver之后的
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new RedisMethodArgumentResolver());
    }
}

4、在controller里使用 @Redis 注解参数即可

@GetMapping("redisArgumentResolver")
    public void redisArgumentResolver(@RequestParam("hello") String hello,
                                      @Redis(key = "redisKey") String redisValue) {
        log.info("控制层获取到的参数值: hello:[{}],redisValue:[{}]", hello, redisValue);
    }

over~~

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,下面是一个Java SpringBoot Controller接收GeoJSON和多个字符串参数的示例代码: ```java import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class GeoJsonController { @PostMapping("/geojson") public ResponseEntity<String> receiveGeoJsonAndParams( @RequestBody String geoJson, @RequestParam("param1") String param1, @RequestParam("param2") String param2) { // 在这里处理GeoJSON数据和其他参数 System.out.println("GeoJSON: " + geoJson); System.out.println("Param1: " + param1); System.out.println("Param2: " + param2); return new ResponseEntity<>("GeoJSON and params received", HttpStatus.OK); } } ``` 在上面的示例中,我们使用了SpringBoot的@RestController注解来标记这个类是一个Controller,使用@PostMapping注解来指定接收POST请求,路径为"/geojson"。 我们的接收方法receiveGeoJsonAndParams接收三个参数:一个RequestBody为String类型的参数geoJson,两个@RequestParam注解的String类型参数param1和param2。其中@RequestParam注解指定了参数的名称,以匹配请求中的参数。 在这个方法中,我们可以同时处理GeoJSON数据和其他参数。在本例中,我们只是简单地打印了接收到的数据,并返回了一个简单的响应。 希望这个示例代码对你有所帮助!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值