处理映射 API 对象到 UI 模型对象的模式
Pattern for dealing with mapping API objects to UI model objects
我在 angular 中切换到使用新的 HttpClient。
API 正在以一种格式调用 returns json 数据。我想获取此数据并将其转换为打字稿模型 class sui 表供 UI 使用。
我之前这样做的方法是使用地图功能,例如
return this.httpClient.get(url, { headers: headers })
.map(mapFunction)
.catch(errorFunction);
map 函数负责将 api 响应转换为模型对象,例如
const mapFunction =
(response: Response) => {
const data = response.json();
const contacts: Contact[] = [];
let i = 0;
for (const result of data.resourceResults) {
i = i + 1;
const contact: Contact = new Contact();
contact.role = result.r
对我来说,这似乎 quite 很麻烦,我基本上是在寻找一种方法来将对象从 api 响应类型映射到 ui 模型类型,而不必使用每个请求的自定义地图功能。
没有明确指定需要映射的内容,就无法进行自定义映射。要么你告诉服务器端 return 一个 UI 友好的响应,要么你需要在客户端自己做映射。
如果你想在客户端映射响应,你可以利用 Typescript 类,并使用它的 constructor
来快速生成你想要的项目:
export class Contact {
public name:string;
public roles:any;
constructor(name:string, roles: any) {
//specify your own constructor logic
this.name=name;
this.roles=roles
}
}
现在您可以在 mapFunction
中写入以将响应显式转换为 Contact
列表。此外,您可以使用数组的 .map()
来遍历对象,而无需编写 for 循环:
public mapFunction = (response: Response) => {
const data = response.json();
const resource = data.resourceResults;
//map it
return resource.map(result => new Contact(result.n, result.r))
}
累不累,我觉得是主观的。但绝对可以用更优雅的方式编写代码。
目前我完全不熟悉 angular,但是由于打字稿,这应该仍然适用。对不起,我的 伪 angular 代码。最简单的方法之一是将对象映射到接口。因此,如果我有 JSON 响应:
{
Id: 1,
Name: "Bob",
Age: 21
}
因为 TypeScript 我可以创建一个匹配的界面:
export interface IPerson
{
Id: number,
Name: string,
Age: number
}
而且我可以将值作为接口传递,而不必 map 值到 类。
return (this.httpClient.get(url, { headers: headers })) as IPerson;
当然,如果您必须扩展 JSON 这不是正确的解决方案。
要根据@CozyAzure 的回复添加更多示例,这就是我所做的。
带界面的 Typescript class(为了通过应用程序获得更好的可读性):
interface MessageConfig {
MessageId?: Guid;
SentAt?: Date;
Status?: string;
Text?: string;
}
export class Message {
MessageId: Guid;
SentAt: Date;
Status: string;
Text: string;
constructor(config: MessageConfig) {
this.MessageId = config.MessageId;
this.SentAt = config.SentAt || new Date();
this.Status = config.Status || "Unread";
this.Text = config.Text;
}
}
将数据映射到消息的实用函数class:
export function mapMessage(message: any) {
return new Message({
MessageId: message.messageId,
SentAt: new Date(message.sentAt),
Status: message.status,
Text: message.text,
});
}
将服务器响应映射到单个消息的服务函数:
addMessage = (message: Message) => {
return this.http.post(API, message).pipe(
map((response: any) => {
if (response && response.success) {
const m = mapMessage(response.message);
return m;
}
throw new Error(response.errorMessage);
}),
catchError(error => {
return throwError(error);
}),
share()
);
};
地图服务器响应多条消息的服务函数:
getMessages = (): Observable<Array<Message>> => {
return this.http
.get(API)
.pipe(
map((response: any) => {
if (response && response.success) {
let messages: Array<Messages> = [];
if (response.count > 0) {
messages = response.messages.map(
message => mapMessage(message)
);
}
return messages;
}
throw new Error(response.errorMessage);
}),
catchError(error => {
return throwError(error);
}),
share()
);
};
所以,是的,发生了很多事情,但这是我在对这个想法进行多次迭代后得出的结论。它是最容易复制和维护的,而且清晰易懂,而且相当干。我通常有几个 API 调用映射到同一个 class,因此 5 分钟的设置为我每次额外的 API 调用节省了很多时间。
我在 angular 中切换到使用新的 HttpClient。
API 正在以一种格式调用 returns json 数据。我想获取此数据并将其转换为打字稿模型 class sui 表供 UI 使用。
我之前这样做的方法是使用地图功能,例如
return this.httpClient.get(url, { headers: headers })
.map(mapFunction)
.catch(errorFunction);
map 函数负责将 api 响应转换为模型对象,例如
const mapFunction =
(response: Response) => {
const data = response.json();
const contacts: Contact[] = [];
let i = 0;
for (const result of data.resourceResults) {
i = i + 1;
const contact: Contact = new Contact();
contact.role = result.r
对我来说,这似乎 quite 很麻烦,我基本上是在寻找一种方法来将对象从 api 响应类型映射到 ui 模型类型,而不必使用每个请求的自定义地图功能。
没有明确指定需要映射的内容,就无法进行自定义映射。要么你告诉服务器端 return 一个 UI 友好的响应,要么你需要在客户端自己做映射。
如果你想在客户端映射响应,你可以利用 Typescript 类,并使用它的 constructor
来快速生成你想要的项目:
export class Contact {
public name:string;
public roles:any;
constructor(name:string, roles: any) {
//specify your own constructor logic
this.name=name;
this.roles=roles
}
}
现在您可以在 mapFunction
中写入以将响应显式转换为 Contact
列表。此外,您可以使用数组的 .map()
来遍历对象,而无需编写 for 循环:
public mapFunction = (response: Response) => {
const data = response.json();
const resource = data.resourceResults;
//map it
return resource.map(result => new Contact(result.n, result.r))
}
累不累,我觉得是主观的。但绝对可以用更优雅的方式编写代码。
目前我完全不熟悉 angular,但是由于打字稿,这应该仍然适用。对不起,我的 伪 angular 代码。最简单的方法之一是将对象映射到接口。因此,如果我有 JSON 响应:
{
Id: 1,
Name: "Bob",
Age: 21
}
因为 TypeScript 我可以创建一个匹配的界面:
export interface IPerson
{
Id: number,
Name: string,
Age: number
}
而且我可以将值作为接口传递,而不必 map 值到 类。
return (this.httpClient.get(url, { headers: headers })) as IPerson;
当然,如果您必须扩展 JSON 这不是正确的解决方案。
要根据@CozyAzure 的回复添加更多示例,这就是我所做的。
带界面的 Typescript class(为了通过应用程序获得更好的可读性):
interface MessageConfig {
MessageId?: Guid;
SentAt?: Date;
Status?: string;
Text?: string;
}
export class Message {
MessageId: Guid;
SentAt: Date;
Status: string;
Text: string;
constructor(config: MessageConfig) {
this.MessageId = config.MessageId;
this.SentAt = config.SentAt || new Date();
this.Status = config.Status || "Unread";
this.Text = config.Text;
}
}
将数据映射到消息的实用函数class:
export function mapMessage(message: any) {
return new Message({
MessageId: message.messageId,
SentAt: new Date(message.sentAt),
Status: message.status,
Text: message.text,
});
}
将服务器响应映射到单个消息的服务函数:
addMessage = (message: Message) => {
return this.http.post(API, message).pipe(
map((response: any) => {
if (response && response.success) {
const m = mapMessage(response.message);
return m;
}
throw new Error(response.errorMessage);
}),
catchError(error => {
return throwError(error);
}),
share()
);
};
地图服务器响应多条消息的服务函数:
getMessages = (): Observable<Array<Message>> => {
return this.http
.get(API)
.pipe(
map((response: any) => {
if (response && response.success) {
let messages: Array<Messages> = [];
if (response.count > 0) {
messages = response.messages.map(
message => mapMessage(message)
);
}
return messages;
}
throw new Error(response.errorMessage);
}),
catchError(error => {
return throwError(error);
}),
share()
);
};
所以,是的,发生了很多事情,但这是我在对这个想法进行多次迭代后得出的结论。它是最容易复制和维护的,而且清晰易懂,而且相当干。我通常有几个 API 调用映射到同一个 class,因此 5 分钟的设置为我每次额外的 API 调用节省了很多时间。