Vapor4中排除指定字段返回数据:不返回location字段的实现方法
在Vapor 4中排除模型字段返回的解决方案
嘿,我来帮你搞定这个问题!你现在需要让接口返回Info模型时排除location字段,这里有几种靠谱的实现方式,我给你一一拆解:
先明确你的现有代码场景
你的接口目前直接返回查询到的[Info]数组,而Info模型包含了location字段,现在要把这个字段从响应里去掉。
现有接口代码:
func getperinfo(_ req: Request) throws -> EventLoopFuture<[Info]>{ let user = try req.auth.require(User.self) return Info.query(on: req.db).filter(\.$user.$id == user.id!).all() }
Info模型定义:
import Foundation import Fluent import Vapor import FluentPostgresDriver import FluentPostGIS final class Info:Model,Content{ static let schema = "info" @ID(key: .id) var id:UUID? @Field(key: "姓名") var name: String @Field(key: "IG帳號") var account: String @Field(key: "頭像") var picture: String @Field(key: "年紀") var age: String @Field(key: "生日") var birth: String @Field(key: "居住城市") var city: String @Field(key: "興趣") var hobby : String @Field(key:"位置") var location: GeometricPoint2D @Parent(key: "user_id") var user: User init(){} init(id:UUID?=nil, name:String, account:String, picture:String ,age:String, birth:String,location: GeometricPoint2D , city:String, hobby:String, userId:UUID){ self.id=id self.name=name self.account=account self.picture=picture self.age=age self.birth=birth self.location=location self.city=city self.hobby=hobby self.$user.id=userId } }
方法一:使用DTO(数据传输对象)最推荐
这种方式把数据库模型和接口响应结构彻底分开,灵活度最高,也符合代码设计的最佳实践,避免后续需求变更时的耦合问题。
步骤1:创建响应结构体
新建一个InfoResponse结构体,只包含你需要返回的字段,并实现Content协议:
struct InfoResponse: Content { var id: UUID? var name: String var account: String var picture: String var age: String var birth: String var city: String var hobby: String var userId: UUID // 对应当初模型里的user.parent.id }
步骤2:给Info模型加转换方法
在Info模型的扩展里添加一个方法,把模型实例转换成响应结构体:
extension Info { func toResponse() -> InfoResponse { InfoResponse( id: self.id, name: self.name, account: self.account, picture: self.picture, age: self.age, birth: self.birth, city: self.city, hobby: self.hobby, userId: self.$user.id ) } }
步骤3:修改接口代码
查询到Info数组后,把每个元素转换成响应结构体再返回:
func getperinfo(_ req: Request) throws -> EventLoopFuture<[InfoResponse]>{ let user = try req.auth.require(User.self) return Info.query(on: req.db) .filter(\.$user.$id == user.id!) .all() .map { infos in infos.map { $0.toResponse() } } }
方法二:修改Content协议实现(耦合性高,不推荐)
如果不想新建结构体,也可以直接修改Info模型的Content编码逻辑,让它跳过location字段的编码。但这种方式会让模型和响应格式绑定,后续如果有其他接口需要返回location就会冲突,所以只适合临时快速解决问题。
修改代码如下:
extension Info: Content { // 重新定义编码的Key,排除location enum CodingKeys: String, CodingKey { case id, name, account, picture, age, birth, city, hobby, userId = "user_id" } // 自定义编码逻辑,不处理location字段 func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(name, forKey: .name) try container.encode(account, forKey: .account) try container.encode(picture, forKey: .picture) try container.encode(age, forKey: .age) try container.encode(birth, forKey: .birth) try container.encode(city, forKey: .city) try container.encode(hobby, forKey: .hobby) try container.encode($user.id, forKey: .userId) // 这里故意不编码location字段 } }
修改后你的原接口代码不需要改动,但还是建议用第一种方法。
方法三:结合投影查询优化性能(进阶)
如果想进一步优化,不仅不在响应里返回location,还让数据库只查询你需要的字段(减少数据传输),可以用Fluent的field方法指定要查询的字段,配合DTO一起使用:
func getperinfo(_ req: Request) throws -> EventLoopFuture<[InfoResponse]>{ let user = try req.auth.require(User.self) return Info.query(on: req.db) .filter(\.$user.$id == user.id!) // 逐个指定要查询的字段,排除location .field(\.$id) .field(\.$name) .field(\.$account) .field(\.$picture) .field(\.$age) .field(\.$birth) .field(\.$city) .field(\.$hobby) .field(\.$user.$id) .all() .map { infos in infos.map { $0.toResponse() } } }
这种方式既控制了返回字段,又优化了数据库查询的性能,是比较理想的方案。
内容的提问来源于stack exchange,提问作者蔡濡安
相关产品推荐
相关产品推荐

