ModelAttribute用法详解

news2024/11/24 1:27:31

目录

官方解释

例子

使用场景1

使用场景2

场景3


官方解释

首先看一下官方文档上该注解的解释:

可以看到ModelAttribute可以用在参数上,也可以用在方法上:

  1. Can be used to expose command objects to a web view, using specific attribute names, through annotating corresponding parameters of an @RequestMapping method.
  2. Can also be used to expose reference data to a web view through annotating accessor methods in a controller class with @RequestMapping methods. Such accessor methods are allowed to have any arguments that @RequestMapping methods support, returning the model attribute value to expose.

大致的意思是说,结合@RequestMapping,使用特定的属性名,可以将对象暴露给web view;或者也可以在controller类中将该注解加到方法上,暴露相关的数据。

说的太抽象了,结合具体场景更好理解,请继续往下看⬇️。

例子

我写了一个controller类,里面有几个接口,为了安全,我希望每次调用这些接口的时候都要验证用户的登录状态,如果验证通过,才能正常使用接口:

package com.useless.matrix.test.controller;

import com.useless.matrix.common.exception.ApiException;
import com.useless.matrix.data.dto.AccountDetailDto;
import com.useless.matrix.data.mbg.model.Account;
import com.useless.matrix.test.service.AccountService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;

import java.util.List;

@RestController
@Api(tags = "TestController", description = "测试controller")
public class TestController {
    @Autowired
    AccountService accountService;

    @ApiOperation("测试抛异常")
    @GetMapping(value = "/testThrowExcep")
    //@ResponseBody
    public void throwExp() {
        throw new ApiException("api error");
    }

    @ApiOperation("测试-获取账户详细信息")
    @GetMapping(value = "/userInfo")
    public List<AccountDetailDto> userInfo(@RequestParam("id") String userId) {
        return accountService.getAccountDetail(userId);
    }

    @ApiOperation("测试-获取所有账户信息")
    @GetMapping(value = "/allAccount")
    public List<Account> allAccount() {
        return accountService.getAllAccount();
    }
}

最简单的方式,在每个方法中都加入验证token的代码:

虽然能满足需求,但是每个方法都包含重复的代码,且都增加了入参,那么简化下,将登录验证封装成一个方法:

将验证登录状态的代码封装在一个getUserInfo方法中,哪里需要就拿来用。这是个不错的办法,也比较灵活,但是对于当下的场景,controller类下的每一个方法还是要重复写代码,且如果新增了接口,接口里还要写重复代码,比较麻烦。还能再优化下吗?当然可以!下面有请主角ModelAttribute出场!

使用场景1

我们写一个基本类,其中包含了一个验证登录状态的接口getUserInfo,接口上有@ModelAttribute注解⬇️

import org.springframework.web.bind.annotation.ModelAttribute;

import javax.servlet.http.HttpServletRequest;

public class BaseController {

    @ModelAttribute
    public void getUserInfo(HttpServletRequest httpServletRequest) {
        System.out.println("从httpRequest中获取token,验证登录状态");
    }
}

原来的controller继承基本类⬇️

import com.useless.matrix.common.exception.ApiException;
import com.useless.matrix.data.dto.AccountDetailDto;
import com.useless.matrix.data.mbg.model.Account;
import com.useless.matrix.test.service.AccountService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@Api(tags = "TestController", description = "测试controller")
public class TestController extends BaseController {
    @Autowired
    AccountService accountService;

    @ApiOperation("测试抛异常")
    @GetMapping(value = "/testThrowExcep")
    //@ResponseBody
    public void throwExp() {
        throw new ApiException("api error");
    }

    @ApiOperation("测试-获取账户详细信息")
    @GetMapping(value = "/userInfo")
    public List<AccountDetailDto> userInfo(@RequestParam("id") String userId) {
        return accountService.getAccountDetail(userId);
    }

    @ApiOperation("测试-获取所有账户信息")
    @GetMapping(value = "/allAccount")
    public List<Account> allAccount() {
        System.out.println("我才是真正的方法");
        return null;
        //return accountService.getAllAccount();
    }

}

尝试调用其中的一个接口/allAccount:

观察log日志或者打断点,会发现程序会先去调getUserInfo接口,再去调真正的接口:

这样一来,所有写在该controller下的方法都会先去调getUserInfo验证登录状态,这就是官方文档中说的:

 Can also be used to expose reference data to a web view through annotating accessor methods in a controller class with @RequestMapping methods. Such accessor methods are allowed to have any arguments that @RequestMapping methods support, returning the model attribute value to expose.

这样写的好处是我们不需要在每个方法中都放入登录校验的代码,写在controller类下面的每个方法调用之前都会去调用getUserInfo方法验证登录状态,即便再写新的接口,也无需特殊处理。并且其他需要验证登录状态的controller也可以继承该基本类。

使用场景2

到这儿还没完,如果我不仅想验证登录状态,还想使用返回的userInfo该怎么办?ModelAttribute也可以支持,请继续往下看⬇️。

首先定义一个简单的用户信息类:

import lombok.Data;

@Data
public class UserDto {
    String userId;
    String userName;
    String phone;
}

改造下getUserInfo方法,现在它会返回一个伪造的UserDto用户信息,注意ModelAttribute上加了name属性:

import com.useless.matrix.data.dto.UserDto;
import org.springframework.web.bind.annotation.ModelAttribute;

import javax.servlet.http.HttpServletRequest;

public class BaseController {

    @ModelAttribute(name = "userInfo")
    public UserDto getUserInfo(HttpServletRequest httpServletRequest) {
        System.out.println("从httpRequest中获取token,验证登录状态");
        UserDto userDto = new UserDto();
        userDto.setUserName("Tom");
        userDto.setUserId("1");
        userDto.setPhone("123");
        return userDto;
    }
}

 改造下实际调用的方法,入参中加入了一个userInfo,且同样使用了ModelAttribute注解。注意modelAttribute中的name需要和getUserInfo的name保持一致。

@ApiOperation("测试-获取所有账户信息")
    @GetMapping(value = "/allAccount")
    public List<Account> allAccount(@ModelAttribute("userInfo") UserDto userInfo) {
        System.out.println("我才是真正的方法");
        System.out.println(userInfo.getUserId() + " " + userInfo.getUserName() + " " + userInfo.getPhone());
        return null;
        //return accountService.getAllAccount();
    }

再次调用接口,可以看到正常获取到了用户信息:

 这也就是官方文档中说的:

Can be used to expose command objects to a web view, using specific attribute names, through annotating corresponding parameters of an @RequestMapping method.

场景3

再进一步,我不仅想要获取用户信息,还想知道用户所在的组织id,也就是说我想获得多个参数怎么办?

继续改造getUserInfo方法,去除了@ModelAttribute的name属性,增加了一个ModelMap参数,现在我们可以使用addAttribute方法往里面放多个参数:

@ModelAttribute
    public void getUserInfo(ModelMap modelMap, HttpServletRequest httpServletRequest) {
        System.out.println("从httpRequest中获取token,验证登录状态");
        UserDto userDto = new UserDto();
        userDto.setUserName("Tom");
        userDto.setUserId("1");
        userDto.setPhone("123");
        modelMap.addAttribute("userInfo", userDto);
        modelMap.addAttribute("orgId", 110);
    }

真实方法现在可以获取到多个参数了:

@ApiOperation("测试-获取所有账户信息")
    @GetMapping(value = "/allAccount")
    public List<Account> allAccount(@ModelAttribute("userInfo") UserDto userInfo,
                                    @ModelAttribute("orgId") Integer orgId) {
        System.out.println("我才是真正的方法");
        System.out.println(userInfo.getUserId() + " " + userInfo.getUserName() + " " + userInfo.getPhone());
        System.out.println("orgId: " + orgId);
        return null;
        //return accountService.getAllAccount();
    }

使用postman调用后可以看到日志:

好的,以上就是ModelAttribute的使用方法。

ps:特别注意的是,如果使用了swagger,则需要在入参上加入@ApiIgnore注解忽略参数,不然swagger上会显示该参数(@ApiParam( hidden = true)不支持复杂类型,所以还是用@ApiIgnore比较稳妥)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/749584.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ceph--cephFS的使用

ceph分布式存储—cephFS的使用 1、cephfs的概念 ceph FS 即 ceph filesystem&#xff0c;可以实现文件系统共享功能,客户端通过 ceph 协议挂载并使 用 ceph 集群作为数据存储服务器。 Ceph FS 需要运行 Meta Data Services(MDS)服务&#xff0c;其守护进程为 ceph-mds&#x…

【Unity面试篇】Unity 面试题总结甄选 |Unity渲染Shader相关 | ❤️持续更新❤️

前言 关于Unity面试题相关的所有知识点&#xff1a;&#x1f431;‍&#x1f3cd;2023年Unity面试题大全&#xff0c;共十万字面试题总结【收藏一篇足够面试&#xff0c;持续更新】为了方便大家可以重点复习某个模块&#xff0c;所以将各方面的知识点进行了拆分并更新整理了新…

怎样优雅地增删查改(六):按任意字段关键字查询

文章目录 实现应用测试 实现 定义按任意字段关键字查询过滤器&#xff08;IKeywordOrientedFilter&#xff09;接口&#xff0c;查询实体列表Dto若实现该接口&#xff0c;将筛选指定的目标字段&#xff08;TargetFields&#xff09;包含指定的关键字&#xff08;Keyword&#…

软件开发的六大设计原则

我们常说软件开发要尽量具有良好的可扩展性&#xff0c;做到高内聚低耦合。那么究竟该如何实现呢&#xff1f;在面向对象软件设计领域有一系列大家所认可的设计原则&#xff0c;依据这些原则来设计软件&#xff0c;就可以让软件有很好的可扩展性&#xff0c;其中最重要的一条原…

发起投票平台投票吧网络投票平台网络投票平台

小程序投票活动如何做&#xff1f;很多企业在运营当中&#xff0c;都会通过投票活动来进行推广&#xff0c;从而达到吸粉、增加用户粘度等效果。而此类投票活动&#xff0c;通过小程序就可以实现&#xff0c;操作简单。 我们现在要以“青春大不同”为主题进行一次投票活动&…

扩展欧几里得 证明及应用代码(超详细,附带例题)

应用方面&#xff1a; 1.求解乘法逆元 2.求解&#xff08;ax&#xff09;%bc 即 a个x 模上b后得到c&#xff0c;其中满足条件的x的最小整数。[也可表示为axc(mod b)] 3.求解直线上的整点数 模板代码&#xff1a; 代码1&#xff1a; ll exgcd(ll a,ll b,ll &x,ll &a…

jeecgboot:vue3版本打包失败的解决过程

根据jeecgboot vue3的文档&#xff0c;把本地node升级到16&#xff0c;在本地运行都正常&#xff0c;打包后一直提示内存不足。 首先怀疑是代码配置问题&#xff0c;找到提示对应的地方&#xff0c;修改了package.json&#xff0c;把默认的NODE_OPTIONS--max-old-space-size81…

【批量将视频转为图像序列】

批量将视频转为图像序列 代码如下&#xff0c;代码中带有解释&#xff1a; # 导入所需要的库 import cv2 import os import numpy as np# 多个视频所在的路径 datasets_path ["/home/y/Code/数据集/1/007f.mp4","/home/y/Code/数据集/1/05f.mp4","/…

Python 打印文件执行路径和行号

文章目录 前言代码部分演示意外惊喜&#xff0c;文件位置跳转 前言 我最近在学Python&#xff0c;但是我感觉动态语言如果不打印文件路径和行号&#xff0c;到时候如果出问题Debug&#xff0c;除非你对业务特别熟悉&#xff0c;不然找不到问题的位置。 反正打印了也不亏 代码…

合宙Air001开发板系列教程—01环境搭建与点灯(基于Keil-MDK的开发)

近日合宙出品了一款&#xff1a; TSSOP20封装、ARMCortex-M0内核&#xff0c;内置32K Flash4K RAM、集成多路USART、IIC、SPI等通讯外设&#xff0c;5个16bit定时器以及1路12bit ADC和2路比较器的国产MCU10块钱一个开发板10个芯片&#xff0c;性价比还是很高的&#xff0c;加其…

基于ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升技术

空间数据获取与制图 1.1 软件安装与应用讲解 1.2 空间数据介绍 1.3海量空间数据下载 1.4 ArcGIS软件快速入门 1.5 Geodatabase地理数据库 ArcGIS专题地图制作 2.1专题地图制作规范 2.2 空间数据的准备与处理 2.3 空间数据可视化&#xff1a;地图符号与注记 2.4 研究区…

ndk编译jni错误LOCAL_MAKEFILE is not defined,分析解决

概述 我们用ndk编译jni的时候&#xff0c;通常会写一个Android.mk脚本文件。但是有些情况&#xff0c;我们脚本文件名字不叫Android.mk&#xff0c;比如我的分别改成AndroidSo.mk&#xff0c;AndroidA.mk 这时候就会报错&#xff1a;LOCAL_MAKEFILE is not defined 软件环境 …

单片机数码管

LED数码管&#xff08;LED Segment Displays&#xff09;是由8个发光二极管构成&#xff0c;并按照一定的图形及排列封转在一起的显示器件。其中7个LED构成7笔字形&#xff0c;1个LED构成小数点&#xff08;固有时成为八段数码管&#xff09;。 LED数码管有两大类&#xff0c…

从新手到高阶,企业培训直播玩法全攻略

首先&#xff0c;把握培训直播的整体规划。 管理端&#xff1a;直播内容管理纳入企业内部学习资源的建设&#xff0c;让企业内部的知识积累、沉淀形成体系。学员端&#xff1a;方便学员精准、快速定位到课程资源&#xff0c;方便快捷检索内容&#xff0c;学习体验简单易用。 其…

CP AUTOSAR中的EThTrcv

环境 EthTrcv驱动实际上是要实现EthIf指出的接口,包括如下API函数,描述在一个结构体里面 /** \brief type used in EthIf_EthTrcvDrvApi */ typedef struct sEthIf_EthTrcvDrvApiType { EthIf_EthTrcvCheckWakeupFctPtrType CheckWakeupOfEthTrcvDrvApi; /**< Et…

2023 JAVA 面试太难, 吃透这份 JAVA 架构面试笔记后, 成功涨到 30K

前阵子跟一位高级架构师的前辈聊天时&#xff0c;聊到今年的面试。有两个感受&#xff0c;一个是今年面邀的次数比往年要低不少&#xff0c;再一个就是很多面试者准备明显不足。不少候选人能力其实不差&#xff0c;进入团队干活后达到期望不难&#xff0c;但由于没准备或不会表…

Python从入门到精通:一步步掌握Python编程

&#x1f482; 个人网站:【海拥】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言Python入门条件和循…

使用Django数据库模型中的ForeignKey()形成数据表记录的父子层次结构

可以把ForeignKey()的第1个参数设置为值 “self” 实际形成数据表记录的父子层次结构。 下面是一个简单的实例&#xff1a; 在文件 E:\Python_project\P_001\myshop-test\myshop\app1\models.py 中写入下面的代码&#xff1a; from django.db import models# Create your mod…

如何保证消息的可靠性+延迟队列(TTL+死信队列+延迟队列)

目录 1.如何保证消息的可靠性 1.1.消息的可靠投递 confirm机制 return机制 1.2.如何保证消息在队列中不丢失 1.3.确保消息能可靠的被消费掉 2.延迟队列 2.1.TTL 2.2.死信队列 2.3.延迟队列 3.如何防止消费者重复消费消息 1.如何保证消息的可靠性 1.1.消息的可靠投递…

element-ui 使用 el-descriptions

<el-descriptions :column"2" border size"mini" style"margin-top: 10px;" :labelStyle"{width: 123px}" :contentStyle"{width:42%}"><el-descriptions-item label"选择项目"><el-select size&…