Swagger使用简介

一、Swagger的作用

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。
作用:
1. 接口的文档在线自动生成。
2. 功能测试。

二、Swagger的主要项目

Swagger是一组开源项目,其中主要项目如下:

  1. Swagger-tools:提供各种与Swagger进行集成和交互的工具。例如模式检验、Swagger 1.2文档转换成Swagger 2.0文档等功能。
  2. Swagger-core: 用于Java/Scala的的Swagger实现。与JAX-RS(Jersey、Resteasy、CXF…)、Servlets和Play框架进行集成。
  3. Swagger-js: 用于JavaScript的Swagger实现。
  4. Swagger-node-express: Swagger模块,用于node.js的Express web应用框架。
  5. Swagger-ui:一个无依赖的HTML、JS和CSS集合,可以为Swagger兼容API动态生成优雅文档。
  6. Swagger-codegen:一个模板驱动引擎,通过分析用户Swagger资源声明以各种语言生成客户端代码。

三、引入Swagger依赖

在pom.xml中加入swagger2的依赖

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

四、创建Swagger2配置类

在Application.java同级目录下创建Swagger2的配置类Swagger2.java
@Configuration:表明它是一个配置类。
@EnableSwagger2:开启swagger2。
createRestApi():创建Docket的Bean。
apiInfo():创建该API的基本信息,这些基本信息会展现在文档页面中。
apis():指定扫描的包会生成文档。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * Swagger2配置类
 * 在与spring boot集成时,放在与Application.java同级的目录下。
 * 通过@Configuration注解,让Spring来加载该类配置。
 * 再通过@EnableSwagger2注解来启用Swagger2。
 */
@Configuration
@EnableSwagger2
public class Swagger2 {

    /**
     * 创建API应用
     * apiInfo() 增加API相关信息
     * 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现,
     * 本例采用指定扫描的包路径来定义指定要建立API的目录。
     *
     * @return
     */
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.test.helloworld.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    /**
     * 创建该API的基本信息(这些基本信息会展现在文档页面中)
     * 访问地址:http://项目实际地址/swagger-ui.html
     *
     * @return
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Spring Boot中使用Swagger2构建RESTful APIs")
                .description("更多请关注http://www.baidu.com")
                .termsOfServiceUrl("http://www.baidu.com")
                .version("1.0")
                .build();
    }
}

五、添加文档内容

swagger通过注解表明该接口会生成文档,包括接口名、请求方法、参数、返回信息的等等。
Swagger使用的注解及其说明:

swagger通过注解表明该接口会生成文档,包括接口名、请求方法、参数、返回信息的等等。
Swagger使用的注解及其说明:
1@Api:用在请求的类上,表示对类的说明
   	tags="说明该类的作用,可以在UI界面上看到的注解"
	value="该参数没什么意义,在UI界面上也看不到,所以不需要配置"
	示例:
		@Api(tags="APP用户注册Controller")
2@ApiOperation:用在请求的方法上,说明方法的用途、作用
    value="说明方法的用途、作用"
	notes="方法的备注说明"
	示例:
		@ApiOperation(value="用户注册",notes="手机号、密码都是必输项,年龄随边填,但必须是数字")
3@ApiImplicitParams:用在请求的方法上,表示一组参数说明
	@ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面
        name:参数名
        value:参数的汉字说明、解释
        required:参数是否必须传(true | false)
        paramType:参数放在哪个地方
            · header --> 请求参数的获取:@RequestHeader
            · query --> 请求参数的获取:@RequestParam
            · path(用于restful接口)--> 请求参数的获取:@PathVariable
            · body(不常用)
            · form(不常用)    
        dataType:参数类型,默认String,其它值dataType="Integer"       
        defaultValue:参数的默认值
	示例:
		@ApiImplicitParams({
    		@ApiImplicitParam(name="mobile",value="手机号",required=true,paramType="form"),
    		@ApiImplicitParam(name="password",value="密码",required=true,paramType="form"),
    		@ApiImplicitParam(name="age",value="年龄",required=true,paramType="form",dataType="Integer")
		})
4@ApiResponses:用在请求的方法上,表示一组响应
    @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
        code:数字,例如400
        message:信息,例如"请求参数没填好"
        response:抛出异常的类
	示例:
		@ApiOperation(value = "select1请求",notes = "多个参数,多种的查询参数类型")
		@ApiResponses({
    		@ApiResponse(code=400,message="请求参数没填好"),
    		@ApiResponse(code=404,message="请求路径没有或页面跳转路径不对")
		})
5@ApiModel:用于响应类上,表示一个返回响应数据的信息
	(这种一般用在post创建的时候,使用@RequestBody这样的场景,请求参数无法使用@ApiImplicitParam注解进行描述的时候)
    @ApiModelProperty:用在属性上,描述响应类的属性
	示例:
		import io.swagger.annotations.ApiModel;
		import io.swagger.annotations.ApiModelProperty;
		import java.io.Serializable;
		
		@ApiModel(description= "返回响应数据")
		public class RestMessage implements Serializable{
    		@ApiModelProperty(value = "是否成功")
    		private boolean success=true;
    		@ApiModelProperty(value = "返回对象")
    		private Object data;
    		@ApiModelProperty(value = "错误编号")
    		private Integer errCode;
    		@ApiModelProperty(value = "错误信息")
    		private String message;
    		/* getter/setter */
		}

六、示例说明

import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;

/**
 * 一个用来测试swagger注解的控制器
 * 注意@ApiImplicitParam的使用会影响程序运行,如果使用不当可能造成控制器收不到消息
 *
 * @author
 */
@Controller
@RequestMapping("/say")
@Api(value = "SayController|一个用来测试swagger注解的控制器")
public class SayController {

    @ResponseBody
    @RequestMapping(value = "/getUserName", method = RequestMethod.GET)
    @ApiOperation(value = "根据用户编号获取用户姓名", notes = "test: 仅1和2有正确返回")
    @ApiImplicitParam(paramType = "query", name = "userNumber", value = "用户编号", required = true, dataType = "Integer")
    public String getUserName(@RequestParam Integer userNumber) {
        if (userNumber == 1) {
            return "小浦";
        } else if (userNumber == 2) {
            return "小招";
        } else {
            return "未知";
        }
    }

    @ResponseBody
    @RequestMapping("/updatePassword")
    @ApiOperation(value = "修改用户密码", notes = "根据用户id修改密码")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "userId", value = "用户ID", required = true, dataType = "Integer"),
            @ApiImplicitParam(paramType = "query", name = "password", value = "旧密码", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "newPassword", value = "新密码", required = true, dataType = "String")
    })
    public String updatePassword(@RequestParam(value = "userId") Integer userId,
                                 @RequestParam(value = "password") String password,
                                 @RequestParam(value = "newPassword") String newPassword) {
        if (userId <= 0 || userId > 2) {
            return "未知的用户";
        }
        if (StringUtils.isEmpty(password) || StringUtils.isEmpty(newPassword)) {
            return "密码不能为空";
        }
        if (password.equals(newPassword)) {
            return "新旧密码不能相同";
        }
        return "密码修改成功!";
    }
}

七、查看接口信息

完成上述代码添加后,启动Spring Boot程序,访问:http://localhost:8080/swagger-ui.html
控制器信息
如上图,可以看到接口的控制器信息,点击进入可以看到详细信息。
接口详细信息

八、注意事项

  1. paramType会直接影响程序的运行,如果paramType与方法参数获取使用的注解不一致,会影响参数的接收。
    例如:
    注意paramType的设置
    在使用Sawgger UI进行测试时,接收不到参数。
    无法接收到userNumber的值
  2. Conntroller中定义的方法必须在@RequestMapper中显示的指定RequestMethod类型,否则SawggerUI会默认为全类型皆可访问, API列表中会生成多条项目。
    在这里插入图片描述
    如上图updatePassword()未指定requestMethod,结果生成了7条API信息(如下图)。所以如果没有特殊需求,建议根据实际情况加上requestMethod。
    在这里插入图片描述

九、Swagger UI面板说明

在这里插入图片描述

十、接收对象传参

在POJO上增加@ApiModel注解

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * 医生对象模型
 *
 * @author
 */
@ApiModel(value = "医生对象模型")
@Data
public class DemoDoctor {
    @ApiModelProperty(value = "id", required = true)
    private Integer id;
    @ApiModelProperty(value = "医生姓名", required = true)
    private String name;
}

注意: 在后台采用对象接收参数时,Swagger自带的工具采用的是JSON传参,测试时需要在参数上加上@RequestBody注解,正常运行采用form或URL提交时候请删除。

import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import com.forezp.helloworld.javaBean.DemoDoctor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.github.pagehelper.PageInfo;
import com.forezp.api.exception.HttpStatus401Exception;
import com.forezp.api.model.base.DemoDoctor;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;

/**
 * 医生类
 *
 * @author
 */
@RequestMapping("/api/v1")
@Controller
@Api(value = "DoctorTestController-医生信息接口")
public class DoctorTestController {

    /**
     * 添加医生
     * <p>
     * 在使用对象封装参数进行传参时,需要在该对象添加注解,将其注册到swagger中
     *
     * @param doctor 医生类对象
     * @return
     * @throws Exception
     * @link com.zhongying.api.model.base.DemoDoctor
     * <p>
     * 注意: 在后台采用对象接收参数时,Swagger自带的工具采用的是JSON传参,
     * 测试时需要在参数上加入@RequestBody,正常运行采用form或URL提交时候请删除。
     */
    @ResponseBody
    @RequestMapping(value = "/doctor", method = RequestMethod.POST)
    @ApiOperation(value = "添加医生信息", notes = "")
    public String addDoctor(@RequestBody DemoDoctor doctor) throws Exception {
        if (null == doctor || doctor.getId() == null) {
            throw new HttpStatus401Exception("添加医生失败", "DT3388", "未知原因", "请联系管理员");
        }
        try {
            System.out.println("成功----------->" + doctor.getName());
        } catch (Exception e) {
            throw new HttpStatus401Exception("添加医生失败", "DT3388", "未知原因", "请联系管理员");
        }
        return doctor.getId().toString();
    }

    /**
     * 删除医生
     *
     * @param doctorId 医生ID
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/doctor/{doctorId}", method = RequestMethod.DELETE)
    @ApiOperation(value = "删除医生信息", notes = "")
    @ApiImplicitParam(paramType = "query", name = "doctorId", value = "医生ID", required = true, dataType = "Integer")
    public String deleteDoctor(@RequestParam Integer doctorId) {
        if (doctorId > 2) {
            return "删除失败";
        }
        return "删除成功";
    }

    /**
     * 修改医生信息
     *
     * @param doctorId 医生ID
     * @param doctor   医生信息
     * @return
     * @throws HttpStatus401Exception
     */
    @ResponseBody
    @RequestMapping(value = "/doctor/{doctorId}", method = RequestMethod.POST)
    @ApiOperation(value = "修改医生信息", notes = "")
    @ApiImplicitParam(paramType = "query", name = "doctorId", value = "医生ID", required = true, dataType = "Integer")
    public String updateDoctor(@RequestParam Integer doctorId, @RequestBody DemoDoctor doctor) throws HttpStatus401Exception {
        if (null == doctorId || null == doctor) {
            throw new HttpStatus401Exception("修改医生信息失败", "DT3391", "id不能为空", "请修改");
        }
        if (doctorId > 5) {
            throw new HttpStatus401Exception("医生不存在", "DT3392", "错误的ID", "请更换ID");
        }
        System.out.println(doctorId);
        System.out.println(doctor);
        return "修改成功";
    }

    /**
     * 获取医生详细信息
     *
     * @param doctorId 医生ID
     * @return
     * @throws HttpStatus401Exception
     */
    @ResponseBody
    @RequestMapping(value = "/doctor/{doctorId}", method = RequestMethod.GET)
    @ApiOperation(value = "获取医生详细信息", notes = "仅返回姓名..")
    @ApiImplicitParam(paramType = "query", name = "doctorId", value = "医生ID", required = true, dataType = "Integer")
    public DemoDoctor getDoctorDetail(@RequestParam Integer doctorId) throws HttpStatus401Exception {
        System.out.println(doctorId);
        if (null == doctorId) {
            throw new HttpStatus401Exception("查看医生信息失败", "DT3390", "未知原因", "请联系管理员");
        }
        if (doctorId > 3) {
            throw new HttpStatus401Exception("医生不存在", "DT3392", "错误的ID", "请更换ID");
        }
        DemoDoctor doctor = new DemoDoctor();
        doctor.setId(1);
        doctor.setName("测试员");
        return doctor;
    }

    /**
     * 获取医生列表
     *
     * @param pageIndex 当前页数
     * @param pageSize  每页记录数
     * @param request
     * @return
     * @throws HttpStatus401Exception
     */
    @ResponseBody
    @RequestMapping(value = "/doctor", method = RequestMethod.GET)
    @ApiOperation(value = "获取医生列表", notes = "目前一次全部取,不分页")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", name = "token", value = "token", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "pageIndex", value = "当前页数", required = false, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "pageSize", value = "每页记录数", required = true, dataType = "String"),
    })
    public PageInfo<DemoDoctor> getDoctorList(@RequestParam(value = "pageIndex", required = false, defaultValue = "1") Integer pageIndex,
                                              @RequestParam(value = "pageSize", required = false) Integer pageSize,
                                              HttpServletRequest request) throws HttpStatus401Exception {
        String token = request.getHeader("token");
        if (null == token) {
            throw new HttpStatus401Exception("没有权限", "SS8888", "没有权限", "请查看操作文档");
        }
        if (null == pageSize) {
            throw new HttpStatus401Exception("每页记录数不粗安在", "DT3399", "不存在pageSize", "请查看操作文档");
        }

        DemoDoctor doctor1 = new DemoDoctor();
        doctor1.setId(1);
        doctor1.setName("测试员1");
        DemoDoctor doctor2 = new DemoDoctor();
        doctor2.setId(2);
        doctor2.setName("测试员2");

        List<DemoDoctor> doctorList = new ArrayList<DemoDoctor>();
        doctorList.add(doctor1);
        doctorList.add(doctor2);
        return new PageInfo<DemoDoctor>(doctorList);
    }
}

如请求需要在header增加额外参数,可以参考getDoctorList()的做法,使用request接收。
@ApiImplicitParam(paramType = “header”, name = “token”, value = “token”, required = true, dataType = “String”)
String token = request.getHeader(“token”);

十一、参考资料

https://blog.csdn.net/sanyaoxu_2/article/details/80555328
https://blog.csdn.net/jia20003/article/details/50700736
http://blog.didispace.com/springbootswagger2/
Swagger官网 :http://swagger.io/
Spring Boot & Swagger UI : http://fruzenshtein.com/spring-boot-swagger-ui/
Github:https://github.com/swagger-api/swagger-core/wiki/Annotations

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据来源:中经数据库 主要指标110多个(全部都是纯粹的 市辖区 指标),大致是: GDP GDP增速 第一产业增加值占GDP比重 第二产业增加值占GDP比重 第三产业增加值占GDP比重 人均GDP 社会消费品零售总额 固定资产投资(不含农户) 新设外商投资企业数_外商直接投资 实际利用外资金额(美元) 一般公共预算收入 一般公共预算支出 一般公共预算支出_教育 一般公共预算支出_科学技术 金融机构人民币各项存款余额_个人储蓄存款 金融机构人民币各项存款余额 金融机构人民币各项贷款余额 规模以上工业企业单位数 规模以上工业企业单位数_内资企业 规模以上工业企业单位数_港澳台商投资企业 规模以上工业企业单位数_外商投资企业 规模以上工业总产值 规模以上工业总产值_内资企业 规模以上工业总产值_港澳台商投资企业 规模以上工业总产值_外商投资企业 规模以上工业企业流动资产合计 规模以上工业企业固定资产合计 规模以上工业企业利润总额 规模以上工业企业应交增值税 规模以上工业企业主营业务税金及附加 户籍人口数 年均户籍人口数 户籍人口自然增长率 第一产业就业人员占全部城镇单位就业人员比重 第二产业就业人员占全部城镇单位就业人员比重 第三产业就业人员占全部城镇单位就业人员比重 城镇非私营单位就业人员数 城镇非私营单位就业人员数_第一产业 城镇非私营单位就业人员数_第二产业 城镇非私营单位就业人员数_第三产业 城镇非私营单位就业人员数_农、林、牧、渔业 城镇非私营单位就业人员数_采矿业 城镇非私营单位就业人员数_制造业 城镇非私营单位就业人员数_电力、热力、燃气及水生产和供应业 城镇非私营单位就业人员数_建筑业 城镇非私营单位就业人员数_批发和零售业 城镇非私营单位就业人员数_交通运输、仓储和邮政业 城镇非私营单位就业人员数_住宿和餐饮业 城镇非私营单位就业人员数_信息传输、软件和信息技术服务业 城镇非私营单位就业人员数_金融业 城镇非私营单位就业人员数_房地产业 城镇非私营单位就业人员数_租赁和商务服务业 城镇非私营单位就业人员数_科学研究和技术服务业 城镇非私营单位就业人员数_水利、环境和公共设施管理业 城镇非私营单位就业人员数_居民服务、修理和其他服务业 城镇非私营单位就业人员数_教育 城镇非私营单位就业人员数_卫生和社会工作 城镇非私营单位就业人员数_文化、体育和娱乐业 城镇非私营单位就业人员数_公共管理、社会保障和社会组织 城镇非私营单位在岗职工平均人数 城镇就业人员数_私营企业和个体 城镇非私营单位在岗职工工资总额 城镇非私营单位在岗职工平均工资 城镇登记失业人员数 建成区面积 建设用地面积 建设用地面积_居住用地 液化石油气供气总量 液化石油气供气总量_居民家庭 人工煤气、天然气供气总量 人工煤气、天然气供气总量_居民家庭 液化石油气用气人口 人工煤气、天然气用气人口 城市公共汽电车运营车辆数 城市出租汽车运营车辆数 城市公共汽电车客运总量 道路面积 排水管道长度 建成区绿化覆盖面积 建成区绿化覆盖率 绿地面积 公园绿地面积 维护建设资金支出 土地面积 生活用水供水量 供水总量 全社会用电量 城乡居民生活用电量 工业生产用电量 房地产开发投资 房地产开发投资_住宅 限额以上批发和零售业法人单位数 限额以上批发和零售业商品销售总额 普通中学学校数 中等职业教育学校数 普通小学学校数 普通高等学校专任教师数 普通中学专任教师数 中等职业教育专任教师数 普通小学专任教师数 普通高等学校在校生数 普通中学在校生数 中等职业教育在校生数 普通小学在校生数 电视节目综合人口覆盖率 公共图书馆总藏量_图书 医疗卫生机构数_医院和卫生院 卫生人员数_执业(助理)医师 医疗卫生机构床位数_医院和卫生院 城镇职工基本养老保险参保人数 职工基本医疗保险参保人数 失业保险参保人数
Swagger是一个用于设计、构建和文档化RESTful Web服务的开源工具集。下面是一个简单的Swagger使用教程: 1. 安装Swagger:可以通过npm、pip等包管理工具安装Swagger相关的库和工具。例如,对于Node.js项目,可以使用以下命令安装swagger-jsdoc和swagger-ui-express: ```bash npm install swagger-jsdoc swagger-ui-express ``` 2. 编写Swagger注解:在你的API代码中,使用Swagger注解来描述API的信息、请求和响应参数等。以下是一个示例: ```javascript /** * @swagger * /api/users: * get: * summary: 获取所有用户 * description: 获取所有用户列表 * responses: * 200: * description: 成功获取用户列表 * schema: * type: array * items: * $ref: '#/definitions/User' */ ``` 在这个示例中,我们使用Swagger注解来描述一个GET请求,它可以获取所有用户的列表。 3. 生成Swagger文档使用Swagger注解编写完API代码后,可以使用相应的工具将这些注解转换为Swagger文档。例如,对于Node.js项目,我们可以使用swagger-jsdoc库生成Swagger文档。在项目的入口文件中添加以下代码: ```javascript const swaggerJSDoc = require('swagger-jsdoc'); const swaggerUi = require('swagger-ui-express'); const options = { definition: { openapi: '3.0.0', info: { title: 'API文档', version: '1.0.0', }, }, apis: ['./path/to/api/controllers/*.js'], // API代码文件的路径 }; const swaggerSpec = swaggerJSDoc(options); app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); ``` 这段代码将会在`/api-docs`路径下提供Swagger文档。 4. 查看Swagger文档:运行项目并访问`/api-docs`路径,你将会看到生成的Swagger文档Swagger提供了一个交互式的UI界面,可以方便地查看API的信息、请求和响应参数等。 这只是一个简单的Swagger使用教程,你可以根据自己的项目需求进一步深入学习和使用Swagger

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值