springboot返回值处理

上一次说到了springboot中对于参数处理的扩展,当然主要是说到如何针对特有的请求数据进行转换,并非站在spring的基础上看spring如何完成这一逻辑,而关于spring内部处理或者说内置的处理将在sping源码中详细去理解。

今天将要说到的是springboot中如何处理方法返回值,使用spring较多的人会看到,对于方法的返回一般会有两种模式,一种是页面,一种是数据,分别对应了ModelAndView和被@ResponseBody标注的方法。下面简单的看下这两种方式的实现过程,同时如何扩展自己的实现。

对于springmvc,所有的请求入口则是通过DispatcherServlet.doDispatch处理,不妨从这里开始看下处理过程,对于此次不关注的点回直接略过。

首先定义一个简单的Controller,然后在doDispatch打个断点

主要关注ha,在spring中有很多的HandlerAdapter,我们用的比较多的当然是RequestMappingHandlerAdapter,相应的进入到handleInternal方法中,

下一步则会调用invokeHandlerMethod,内容比较多,但是不用关注太多,

这里可以看到,使用了一个invocableMethod,看代码可以看到其类型为ServletInvocableHandlerMethod,

这里关注下mavContainer.setRequestHandled(true),这也就是表明返回的ModelAndView是否会被视图解析器解析。returnValueHandlers也就是HandlerMethodReturnValueHandlerComposite,

此时会找到对应的returnValueHandler去处理,调试会发现RequestResponseBodyMethodProcessor,

这里将结果交由MessageConverter处理,由于方法writeWithMessageConverters篇幅比较长,看下关键点

最终就会定位到我们熟悉的StringHttpMessageConverter、MappingJackson2HttpMessageConverter。

其实通过调试基本可以看到主要的逻辑流程。今天说到的返回值处理主要如何通过HandlerMethodReturnValueHandler接口进行扩展,通过上面的调试过程,我们可以看到,对改接口的使用时在RequestMappingHandlerAdapter出现 ,具体spring对该接口有哪些实现,这些实现是如何注入到RequestMappingHandlerAdapter中,这个不做深入。上面说到这里会选择 RequestResponseBodyMethodProcessor,可以看下大致的实现,方便之后的扩展。

这也解释了为什么通过@ResponseBody标记的类或方法能够被处理了。

上面看到的,所以我们在扩展的时候也是一个道理。

下面看一个示例,将请求结果写到文件中并返回:

定义一个注解,类似@ResponseBody,ResponseFile :

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ResponseFile {

    String type() default "txt";
}

定义HandlerMethodReturnValueHandler实现类FileHandlerMethodReturnValueHandler:

public class FileHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler,Ordered {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseFile.class) ||
                returnType.hasMethodAnnotation(ResponseFile.class));
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        mavContainer.setRequestHandled(true);

        ResponseFile methodResponseFile = returnType.getMethodAnnotation(ResponseFile.class);
        if(methodResponseFile == null){
            methodResponseFile = returnType.getContainingClass().getAnnotation(ResponseFile.class);
        }
        Assert.notNull(methodResponseFile,"no ResponseFile at method :"+returnType.getMethod().getName());

        String fileType = methodResponseFile.type();

        HttpServletResponse response = getServletResponse(webRequest);

        response.setHeader("content-type", "application/octet-stream");
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename="+UUID.randomUUID().toString()+"."+fileType);

        ServletOutputStream out = response.getOutputStream();

        if(returnValue  instanceof ResponseFileEntity){
            FileCopyUtils.copy(((ResponseFileEntity) returnValue).getIn(),out);
        }else{
            FileCopyUtils.copy(new ByteArrayInputStream("non".getBytes()),out);
        }

    }

    public HttpServletResponse getServletResponse(NativeWebRequest webRequest){
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        return response;
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE-10;
    }
}

将HandlerMethodReturnValueHandler注册到RequestMappingHandlerAdapter中:

@Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        super.addReturnValueHandlers(returnValueHandlers);
        returnValueHandlers.add(new FileHandlerMethodReturnValueHandler());
    }
}

,根据源码你会发现,这个会注册到RequestMappingHandlerAdapter的customReturnValueHandlers中,但是一定要注意,默认情况下会有很多HandlerMethodReturnValueHandler,而具体使用哪一个是根据supportsReturnType进行适配,一段发现有匹配的处理,则会调用,看下面的代码你会发现,自定义的处理器位置较后,如果默认的处理器有能够适配的,那么我们定义的就不会执行了,所有要注意。

最后就是测试了:

    @ResponseFile
    @GetMapping("/file")
    public ResponseFileEntity toFile(){
        ResponseFileEntity responseFileEntity = new ResponseFileEntity();
        responseFileEntity.setIn(new ByteArrayInputStream("这是一段测试文字".getBytes()));
        return responseFileEntity;
    }

当发送对应请求时,会返回一个文件。

 

前面说到,我们一般情况下的返回值主要都是通过@ResponseBody处理,最后将返回值转换成json类型并返回,所有处理逻辑在RequestResponseBodyMethodProcessor中完成,前面也说到,主要是通过HttpMessageConverter完成,这个东西比较眼熟,同样,在spring中默认有很多HttpMessageConverter,比如常见的StringHttpMessageConverter、MappingJackson2HttpMessageConverter等。

下面通过自定义一个HttpMessageConverter来针对@ResponseBody进行扩展:

public class XmlHttpMessageConverter extends AbstractHttpMessageConverter<Message> {
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    public XmlHttpMessageConverter(){
        this(DEFAULT_CHARSET);
    }

    public XmlHttpMessageConverter(Charset charset){
        super(charset,MediaType.ALL);
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return clazz == Message.class;
    }

    @Override
    protected Message readInternal(Class<? extends Message> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    protected void writeInternal(Message message, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        XStream xStream = new XStream();
        xStream.alias("message",Message.class);
//        StreamUtils.copy(xStream.toXML(message), DEFAULT_CHARSET, outputMessage.getBody());
        xStream.toXML(message,outputMessage.getBody());
    }
}

注册到容器:

@Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.extendMessageConverters(converters);
        converters.add(0,new XmlHttpMessageConverter());
    }
}

写个测试:

    @ResponseBody
    @GetMapping("/message")
    public Message message(){
        Message message = new Message();
        message.setId(UUID.randomUUID().toString());
        message.setDate(new Date());
        message.setInfo("消息");
        return message;
    }

 

总结:

1、主要了解了springboot处理返回值的简单逻辑,返回对象最后都转换为ModelAndView,但是是否由视图解析器处理需要根据

ModelAndViewContainer.RequestHandled判断

2、通过实现 HandlerMethodReturnValueHandler自定义返回,可以将返回值转换成指定的类型,但要注意与默认配置的顺序,防止自定义的被覆盖

3、在@ResponseBody的基础上进行扩展,通过实现自定义的HttpMessageConverter来处理,同样需要注意注入顺序。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中,可以通过以下步骤实现接口返回的加密: 1. 创建一个自定义的响应对象,用于封装加密后的返回数据。例如,可以创建一个名为`EncryptedResponse`的类,包含一个`data`字段用于存储加密后的数据。 ```java public class EncryptedResponse { private String data; public EncryptedResponse(String data) { this.data = data; } // getter and setter } ``` 2. 创建一个自定义的响应切面,在切面中对接口返回进行加密处理。可以使用Spring AOP来实现这一步骤。 ```java @Aspect @Component public class EncryptionAspect { @Autowired private EncryptionService encryptionService; @Pointcut("execution(* com.example.controllers.*.*(..))") public void controllerMethods() {} @Around("controllerMethods()") public Object encryptResponse(ProceedingJoinPoint joinPoint) throws Throwable { // 执行目标方法 Object result = joinPoint.proceed(); // 对返回进行加密处理 String encryptedData = encryptionService.encrypt(result.toString()); // 创建加密后的响应对象 EncryptedResponse encryptedResponse = new EncryptedResponse(encryptedData); return encryptedResponse; } } ``` 在上述代码中,`EncryptionService`是一个自定义的加密服务,用于对返回进行加密。你可以根据具体需求选择适合的加密算法和实现方式。 3. 在Spring Boot的配置文件中启用AOP。 ```java @EnableAspectJAutoProxy @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 通过以上步骤,你可以在Spring Boot中实现接口返回的加密处理。每次调用接口时,返回的数据将会被加密后封装在`EncryptedResponse`对象中返回给客户端。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值