第四阶段04- spring-web,@RequestMapping 关于Knife4j框架

18. 添加Web开发的依赖项

在Spring Boot项目中,当需要实现Web开发(例如使用控制器接收客户端的请求并响应)时,需要添加spring-boot-starter-web依赖项(在创建项目时,勾选Web本质上也是添加此依赖项),此依赖项中包含了spring-boot-starter这个基础依赖项,所以,在现有的项目中,只需要将spring-boot-starter改为spring-boot-starter-web即可:

<!-- Spring Boot Web依赖项 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

当添加了以上依赖项,你的项目会有以下明显的变化:

  • 启用项目时,会自动启用Tomcat(你的项目也会部署到这个Tomcat上),默认占用8080端口
  • src/main/resources下的static文件夹会是默认的静态资源文件夹,在此文件夹下的资源(例如网页、图片等)可以直接通过HTTP协议的URL直接访问
    • 如果创建项目时勾选了Web依赖项,会自动创建此文件夹,如果创建项目时未勾选Web依赖项,而是手动添加spring-boot-starter-web,不会自动创建此文件夹,但可以手动创建,是等效的

在配置文件中,可以通过server.port属性指定Tomcat占用的端口号。

**提示:**端口号的最大值是65535,并且,一般不推荐使用低位端口,因为一些低位端口是默认用于特定的服务的,例如21端口用于FTP服务,110端口用于邮件服务,80端口用于HTTP服务,等等。

例如,在application-dev.yml中添加配置:

# 服务器配置
server:
  # 服务端口
  port: 9080

19. 使用控制器接收并处理“添加相册”的请求

在项目的根包下创建controller.AlbumController类,在类上添加@RestController注解,在类中自动装配IAlbumService的对象,并且,自定义方法处理“添加相册”的请求:

@RestController
public class AlbumController {
    
    @Autowired
    private IAlbumService albumService;
    
    // http://localhost:9080/album/add-new?name=TestAlbum001&description=TestDescription001&sort=88
    @RequestMapping("/album/add-new")
    public String xxx(AlbumAddNewDTO albumAddNewDTO) {
        try {
            albumService.addNew(albumAddNewDTO);
            return "添加相册成功!";
        } catch (ServiceException e) {
            return e.getMessage();
        }
    }
    
}

完成后,启动项目,可以通过 http://localhost:9080/album/add-new?name=TestAlbum001&description=TestDescription001&sort=88 测试访问,也可以修改此URL中的参数值多次尝试访问,当成功添加相册数据时,在浏览器中可以看到添加相册成功!的字样,如果相册名称被占用,可以看到添加相册失败,相册名称已经被占用!的字样。

关于以上自动装配的IAlbumService:不建议声明为具体的实现类,那样不利于代码“解耦”!

20. 关于@RequestMapping注解

在Spring MVC框架中,@RequestMapping注解的主要作用是配置“请求路径”与“处理请求的方法”的映射关系。

@RequestMapping的源代码中,可以看到它的声明:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    // 暂不关心内部代码
}

声明上的@Target注解表示当前注解(@RequestMapping)可以添加在哪里,@Target注解参数为{ElementType.TYPE, ElementType.METHOD},表示可以添加在“类型”上,也可以添加在“方法”上。

通常,建议在每个控制器类上都使用@RequestMapping配置当前类中多个处理的请求的路径的共有部分,例如:

@RestController
@RequestMapping("/album") // 【重要】
public class AlbumController {

    // http://localhost:9080/album/add-new
    @RequestMapping("/add-new")
    public String addNew() {
        // 暂不关心方法的实现
        return null;
    }

    // http://localhost:9080/album/delete
    @RequestMapping("/delete")
    public String delete() {
        // 暂不关心方法的实现
        return null;
    }
    
    // http://localhost:9080/album/update
    @RequestMapping("/update")
    public String update() {
        // 暂不关心方法的实现
        return null;
    }
    
    // http://localhost:9080/album/list
    @RequestMapping("/list")
    public String list() {
        // 暂不关心方法的实现
        return null;
    }

}

在配置路径时,Spring MVC会自动处理必要的/(如果缺少,则添加,如果多余,则去除),例如,以下几种配置是完全等效的:

类上的配置值方法上的配置值
/album/add-new
/albumadd-new
/album//add-new
/album/add-new
album//add-new
album/add-new
album/add-new
albumadd-new

具体使用哪组配置,并没有明确的要求,只需要保证同一个项目中的风格统一即可,例如,推荐使用第1组,或最后一组,不推荐偶尔使用某一组,在其它类中又使用其它组,另外,第1组或最后一组在阅读理解上通常没有歧义,但其它组可能会产生歧义,不太推荐。

@RequestMapping的源代码中还有:

String[] value() default {};

以上value()表示此注解中可以配置名为value的属性,例如:@RequestMapping(value = ???)

以上String[]表示value属性的值类型,例如:@RequestMapping(value = {"a", "b"})

以上default {}表示value属性的默认值是空数组。

在Java语言中,value是注解的默认属性,当配置注解参数时,只配置value这1个属性时,可以不必显式的写出属性名称,例如以下2种配置是完全等效的:

@RequestMapping(value = {"a", "b"})
@RequestMapping({"a", "b"})

在Java语言中,如果需要配置的注解属性的值是某种数组类型的,但是当前只配置1个值(数组只有1个元素)时,可以不必使用大括号将值框住,例如以下2种配置是完全等效的:

@RequestMapping(value = {"a"})
@RequestMapping(value = "a")

所以,在以上的控制器类中,配置的@RequestMapping("/add-new")其实配置的就是注解的value属性!

@RequestMapping的源代码中,value属性的声明上还有@AliasFor注解的配置:

@AliasFor("path")
String[] value() default {};

以上@AliasFor表示当前属性(value)等效于同注解内的path属性!

@RequestMapping的源代码中,path属性的源代码为:

@AliasFor("value")
String[] path() default {};

@RequestMapping的源代码中,还有method属性:

RequestMethod[] method() default {};

method属性的作用是限制请求方式,当没有配置此属性时,所有请求方式都是允许的!

method属性的值类型是RequestMethod(是一种枚举类型)的数组。

例如,可以配置为:

@RequestMapping(value = "/add-new", method = RequestMethod.POST)

如果要允许多种请求方式,可以将method属性值配置为数组,例如:

@RequestMapping(value = "/add-new", method = {RequestMethod.POST, RequestMethod.GET})

在同一个项目中,允许存在多个请求方式不同,但请求路径相同的配置,例如:

@RequestMapping(value = "/add-new", method = RequestMethod.GET)
public String xxx() {
    // 暂不关心方法的实现
}
@RequestMapping(value = "/add-new", method = RequestMethod.POST)
public String xxx() {
    // 暂不关心方法的实现
}

以上2段代码是允许共存的!

为了简化限制请求方式的配置语法,Spring MVC框架还提供了基于@RequestMapping的衍生注解,例如:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
    // 内部的各属性都是等效于@RequestMapping的同名属性的,并且,没有声明method属性
}

以上@GetMapping就相当于是将请求方式限制为GET@RequestMapping,并且,其源代码中的@Target的参数值决定了它只能添加在方法上。

@GetMapping类似的注解还有:@PostMapping@PutMapping@DeleteMapping@PatchMapping

21. 关于Knife4j框架

Knife4j是一款在线API文档框架,可以基于当前项目的控制器类中的配置生成文档,并自带调试功能。

在项目中使用Knife4j需要3个步骤:

  • 添加依赖
    • **注意:**本次使用的Knife4j的版本,要求项目中的Spring Boot版本必须是2.6以下版本(不包含2.6)
  • 在主配置文件(application.properties / application.yml)中开启增强模式
    • 配置knife4j.enable属性,取值为true
  • 添加配置类
    • 是相对固定的代码,复制粘贴即可
    • **注意:**需要检查配置类中所配置的包名,必须是当前项目中控制器类所在的包

先在pom.xml中添加依赖:

<!-- Knife4j Spring Boot:在线API -->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>2.0.9</version>
</dependency>

然后,在application.yml中添加配置:

# Knife4j配置
knife4j:
  # 开启增强模式
  enable: true

最后,在项目的根包下的config包中,创建Knife4jConfiguration配置类,代码如下:

package cn.tedu.csmall.product.config;

import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

/**
 * Knife4j配置类
 *
 * @author java@tedu.cn
 * @version 0.0.1
 */
@Slf4j
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {

    /**
     * 【重要】指定Controller包路径
     */
    private String basePackage = "cn.tedu.csmall.product.controller";
    /**
     * 分组名称
     */
    private String groupName = "product";
    /**
     * 主机名
     */
    private String host = "http://java.tedu.cn";
    /**
     * 标题
     */
    private String title = "酷鲨商城在线API文档--商品管理";
    /**
     * 简介
     */
    private String description = "酷鲨商城在线API文档--商品管理";
    /**
     * 服务条款URL
     */
    private String termsOfServiceUrl = "http://www.apache.org/licenses/LICENSE-2.0";
    /**
     * 联系人
     */
    private String contactName = "Java教学研发部";
    /**
     * 联系网址
     */
    private String contactUrl = "http://java.tedu.cn";
    /**
     * 联系邮箱
     */
    private String contactEmail = "java@tedu.cn";
    /**
     * 版本号
     */
    private String version = "1.0.0";

    @Autowired
    private OpenApiExtensionResolver openApiExtensionResolver;

    public Knife4jConfiguration() {
        log.debug("创建配置类对象:Knife4jConfiguration");
    }

    @Bean
    public Docket docket() {
        String groupName = "1.0.0";
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .host(host)
                .apiInfo(apiInfo())
                .groupName(groupName)
                .select()
                .apis(RequestHandlerSelectors.basePackage(basePackage))
                .paths(PathSelectors.any())
                .build()
                .extensions(openApiExtensionResolver.buildExtensions(groupName));
        return docket;
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(title)
                .description(description)
                .termsOfServiceUrl(termsOfServiceUrl)
                .contact(new Contact(contactName, contactUrl, contactEmail))
                .version(version)
                .build();
    }
}

完成后,启用项目,通过 doc.html 即可访问在线API文档,即:http://localhost:9080/doc.html

为了更好的显示API文档,应该通过相关注解进行配置:

  • @Api:添加在控制器类上,通过此注解的tags属性,可配置管理模块的名称,在配置管理模块名称时,可以在各名称前面添加数字或字母进行序号的排列,使得各管理模块的显示顺序是自定义的顺序

    • 例如:@Api(tags = "1. 相册管理模块")
  • @ApiOperation:添加在控制器类中处理请求的方法上,通过此注解的value属性,可配置业务的名称

    • 例如:@ApiOperation("添加相册")
  • @ApiOperationSupport:添加在控制器类中处理请求的方法上,通过此注解的order属性(int类型),可配置业务的排序序号,各业务将按照此序号升序排列

    • 例如:@ApiOperationSupport(order = 400)
    • 提示:不建议将序号排列的过于紧凑,否则,不利于在已有的顺序中插入新的内容
  • @ApiModelProperty:添加在POJO类型的属性上,通过此注解的value属性,可配置属性的名称,通过此注解的required属性,可配置是否必须提交此参数(但不具备检查功能),通过此注解的example属性,可配置示例值(需要注意数据类型的问题,例如数值型的属性不要配置字母或符号作为示例值),通过此注解的dataType属性,可配置数据类型,例如配置为dataType = "long",但此属性通常不需要配置此属性

    • public class AlbumAddNewDTO implements Serializable {
          @ApiModelProperty(value = "相册名称", required = true, example = "小米手机的相册")
          private String name;
      }
      
  • @ApiImplicitParam:添加在控制器类中处理请求的方法上,用于对未封装的请求参数进行配置,必须通过此注解的name属性,指定配置哪个参数,其它属性的配置可参考@ApiModelProperty中的属性,需要注意的是,一旦通过此注解进行配置,在API文档上显示的数据类型默认是string,对于非字符串类型的属性,应该显式的配置dataType属性

    • 例如:@ApiImplicitParam(name = "id", value = "相册ID", required = true, example = "9527", dataType = "long")
  • @ApiImplicitParams:添加在控制器类中处理请求的方法上,当方法的参数超过1个,且都需要配置API文档说明时,必须将多个@ApiImplicitParam注解配置作为此注解(@ApiImplicitParams)的参数(此注解参数是@ApiImplicitParam数组类型)

    • @ApiImplicitParams({
              @ApiImplicitParam(name = "id", value = "相册ID", required = true, example = "9527", dataType = "long"),
              @ApiImplicitParam(name = "userId", value = "用户ID", required = true, example = "9527", dataType = "long")
      })
      public String delete(Long id, Long userId) {
          return null;
      }
      

22. 关于处理请求的方法的返回值

在Spring MVC框架中,处理请求的方法的返回值,默认表示“视图组件的名称”,即“处理完请求后,由哪个视图来负责显示”,这不是前后端分离的做法。

在前后端分离的做法中,服务器端是不处理视图的,而是将处理请求后得到的数据响应到客户端,由客户端决定如何处理此数据。

在处理请求的方法上,使用@ResponseBody注解,可以使得此方法是响应正文的,即:方法的返回值会直接响应到客户端去!

还可以将@ResponseBody注解添加在控制器类上,则当前控制器类中所有处理请求的方法都是响应正文的。

添加了@Controller注解的类会被作为控制器类,也可以改为使用@RestController,其源代码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

	@AliasFor(annotation = Controller.class)
	String value() default "";

}

所以,在类上添加@RestController,可以将此类标记为控制器类,并且,表示此类中所有处理请求的方法都是响应正文的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值