尚庭公寓项目,即房屋租赁项目,是b站尚硅发布的一套全新的视频,对于java基础较薄弱的同学有一定帮助。我在这个项目基础上做出一些大量的改动,采用了比较流行的若依框架。目前只是完成了管理员部分的。在完成这些部分的基础上对我所遇到的问题进行如下记录。
该原有项目原本是采用Knife4j方法测试,但若依框架所提供的测试技术是Swagger,因此在Swagger的基础上进行修改
(1)首先是注解的修改。在controller中,需要把每个@Tag标签换成@Api,把每个@Operation换成@ApiOperation。例如
@Tag(name = "房间属性管理")
换成
@Api(tags = "房间属性管理")
,然后
@Operation(summary = "新增或更新属性名称")
换成
@ApiOperation("新增或更新属性名称")
,然后对于vo类和实体类,@Schema换成@ApiModel,例如:
@Schema(description = "房间基本属性表")
换成
@ApiModel(description = "房间基本属性表")
,这样用swagger测试的每个api或者实体vo类的名字描述才能正常显示出来。
(2)原有的swagger配置代码:
/**
* Swagger2的接口配置
*
* @author ruoyi
*/
@Configuration
public class SwaggerConfig
{
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
/** 是否开启swagger */
@Value("${swagger.enabled}")
private boolean enabled;
/** 设置请求的统一前缀 */
@Value("${swagger.pathMapping}")
private String pathMapping;
/**
* 创建API
*/
@Bean
public Docket createRestApi()
{
return new Docket(DocumentationType.OAS_30)
// 是否启用Swagger
.enable(enabled)
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描所有有注解的api,用这种方式更灵活
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描指定包中的swagger注解
// .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
/* 设置安全模式,swagger可以设置访问token */
.securitySchemes(securitySchemes())
.securityContexts(securityContexts())
.pathMapping(pathMapping);
}
/**
* 安全模式,这里指定token通过Authorization头请求头传递
*/
private List<SecurityScheme> securitySchemes()
{
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
return apiKeyList;
}
/**
* 安全上下文
*/
private List<SecurityContext> securityContexts()
{
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
.build());
return securityContexts;
}
/**
* 默认的安全上引用
*/
private List<SecurityReference> defaultAuth()
{
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
return securityReferences;
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo()
{
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("标题:房屋易租系统_接口文档")
// 描述
.description("描述:用于管理房屋的信息.")
// 作者信息
.contact(new Contact(ruoyiConfig.getName(), null, null))
// 版本
.version("版本号:" + ruoyiConfig.getVersion())
.build();
}
}
但是对于登录后,因为要携带一个jwt生成的token值,而原有的这个配置并不能满足我的需求,所以需要:
A.在securitySchemes方法添加access-token配置:
private List<SecurityScheme> securitySchemes()
{
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
apiKeyList.add(new ApiKey("access-token", "access-token", In.HEADER.toValue()));
return apiKeyList;
}
B.在defaultAuth()方法添加 access-token 的安全引用:
private List<SecurityReference> defaultAuth()
{
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
// 添加 access-token 的安全引用
securityReferences.add(new SecurityReference("access-token", authorizationScopes));
return securityReferences;
}
浏览器输入,然后右边会有个Authorize,点击后:
下面刚好多出了一个access-token,这样当登录成功后拿到了token值填入到下面点击Authorize即可,否则测试每一个接口都会提示未登录错误。
这里再提一下上面的Authorization,我在ruoyi-framework\src\main\java\com\ruoyi\framework\config\SecurityConfig.java中第117行进行修改:
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**","/admin/**","/app/**").permitAll()
添加"/admin/**","/app/**上去,这样就不会报401错。
在项目提供的源代码上,用swagger调试含有枚举类型的api方法时,类型转换有问题,这里我举一个有关变迁类型的列表查看功能,代码如下:
@ApiOperation("(根据类型)查询标签列表")
@GetMapping("list")
public Result<List<LabelInfo>> labelList(@RequestParam(required = false) ItemType type) {
LambdaQueryWrapper<LabelInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(type != null, LabelInfo::getType, type);
List<LabelInfo> list = service.list(queryWrapper);
return Result.ok(list);
}
其对应的枚举类型有房间和公寓:
public enum ItemType implements BaseEnum {
APARTMENT(1, "公寓"),
ROOM(2, "房间");
@EnumValue
@JsonValue
private Integer code;
private String name;
ItemType(Integer code, String name) {
this.code = code;
this.name = name;
}
@Override
public Integer getCode() {
return this.code;
}
@Override
public String getName() {
return name;
}
}
测试的结果:
这里面不难看出,这里面传的值就有问题,应该是1或2,而不是APARTMENT或ROOM,可能是Jackson 默认会优先使用枚举的 name() 方法(即 APARTMENT 或 ROOM)。当你将 @JsonValue 移到 getCode() 方法上时,就明确告诉 Jackson 使用 getCode() 方法的返回值(1 或 2)作为枚举的 JSON 表现形式。解决方法是将@JsonValue放在public Integer getCode()上面:
@JsonValue
@Override
public Integer getCode() {
return this.code;
}
但这样还没结束,虽然传的值变为了1或2,但测试的时候data还是为空:
再看控制台,传入的却依然是name值:
为了解决这一问题,对于这个问题,为了确保枚举对象的解析正确,需要自己另写fromCode方法,来获取枚举实例,该枚举类所有代码如下:
public enum ItemType implements BaseEnum {
APARTMENT(1, "公寓"),
ROOM(2, "房间");
@EnumValue
private Integer code;
private String name;
ItemType(Integer code, String name) {
this.code = code;
this.name = name;
}
@JsonValue
@Override
public Integer getCode() {
return this.code;
}
@Override
public String getName() {
return name;
}
public static ItemType fromCode(Integer code) {
for (ItemType itemType : ItemType.values()) {
if (itemType.getCode().equals(code)) {
return itemType;
}
}
throw new IllegalArgumentException("Unknown enum code: " + code);
}
}
这个fromCode方法根据给定的 code 值找到对应的 ItemType 枚举实例。如果找到匹配的枚举,则返回相应的枚举对象,否则抛出异常。接下来就要自定义 MyBatis 类型处理器 (TypeHandler) 和配置:
public class BaseStatusTypeHandler extends BaseTypeHandler<BaseStatus> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, BaseStatus parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.getCode());
}
@Override
public BaseStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {
int code = rs.getInt(columnName);
return BaseStatus.fromCode(code);
}
@Override
public BaseStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int code = rs.getInt(columnIndex);
return BaseStatus.fromCode(code);
}
@Override
public BaseStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int code = cs.getInt(columnIndex);
return BaseStatus.fromCode(code);
}
}
最后注册自定义的类型处理器即可:
@Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.getTypeHandlerRegistry().register(ItemType.class, ItemTypeHandler.class);
}
}
再重新测试,然后便可查到相关数据,接下来对于其他的枚举类型也是如此方法。
在后端代码无误的情况下,当前后端联调后,在前端中的公寓添加或修改信息中,省市区这三个字段传值后点击确认,所显示的数据为空且数据库对应的省市区信息也为空,对应的vue文件是\src\views\apartmentManagement\apartmentManagement\components\addOrEditApartment.vue可以看到省市区对应的@change方法写的有问题,修改如下:
// 省份改变回调
const provinceChangeCallback = async () => {
let provinceId = formData.value.provinceId
if (provinceId) {
resetCity()
resetDistrict()
await getCityListHandle(provinceId)
const province = areaInfo.provinceList.find(item => item.id === provinceId);
formData.value.provinceName = province ? province.name : '';
}
}
// 省份清除回调
const provinceClearCallback = () => {
formData.value.provinceId = ''
resetCity()
resetDistrict()
}
// 城市改变回调
const cityChangeCallback = async () => {
let cityId = formData.value.cityId
if (cityId) {
resetDistrict()
await getDistrictListHandle(cityId)
// 更新城市名称
const city = areaInfo.cityList.find(item => item.id === cityId);
formData.value.cityName = city ? city.name : '';
}
}
// 城市清除回调
const cityClearCallback = () => {
console.log('清空城市')
formData.value.cityId = ''
resetDistrict()
}
// 区域改变回调
const districtChangeCallback = async () => {
console.log('区域改变')
// 更新区域名称
let districtId = formData.value.districtId;
const district = areaInfo.districtList.find(item => item.id === districtId);
formData.value.districtName = district ? district.name : '';
console.log("区名:",formData.value.districtName);
}
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- huatuo9.cn 版权所有 赣ICP备2023008801号-1
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务