纹理和基元

最近,我们为您提供了新的HTTP框架HttpMate。 在介绍性文章中 ,我们将请求和响应映射到域对象称为“最复杂的技术细节”,以及如何通过另一个伴侣MapMate帮助我们。

实际上,当将请求属性映射到您的域对象时,MapMate减轻了HttpMate的负担。 它负责将响应转换为适当的格式(JSON,XML,YAML等),本质上执行反序列化和序列化,但还有很多其他工作。

在本文中,我们将重点介绍MapMate如何以受控和可预测的方式帮助我们处理(反序列化)请求/响应对象。

自定义基元

让我们回顾一下上一篇文章中的示例; 我们有一个简单的UseCase发送电子邮件。 为此,我们需要一个Email对象,该对象应具有:

  • 发件人
  • 接收者
  • 学科
  • 身体

所有这些字段都可以表示为字符串,甚至可以表示为字节数组。 选择用来表示数据的通用类型越多,以后解释数据的可能性就越大。 想象一下以下方法:

public Object sendEmail(final Object sender, final Object receiver, final Object subject, final Object body) {...
}

这给我们留下了很多未解决的问题:

  • 是发件人instanceOf是字符串还是字节[]?
  • 编码是什么?
  • 拉链被压缩了吗?

清单继续。 尽管在某些情况下这可能是适当的,但我敢打赌,您会更满意:

public String sendEmail(final String sender, final String receiver, final String subject, final String body) {...
}

后者留出了较少的解释空间:例如,我们不再需要假设编码或完全质疑参数的类型。

但是,它仍然是模棱两可的,发件人字段是否带有用户名或她的电子邮件地址? 同样的歧义是编写单元测试时产生无限不确定性的原因……在某种程度上,使用随机字符串生成器来测试一种人们只能接受电子邮件地址的方法。

对于人员和编译器,以下方法签名在歧义方面做得更好:

public Receipt sendEmail(final EmailAddress sender, final EmailAddress receiver, final Subject subject, final Body body) {...
}

我们可以以相同的方式相信字符串是字符串,整数是整数,我们现在可以相信EmailAddress是电子邮件地址,主题实际上是主题–它们成为了send email方法的自定义原语。

发件人和接收者不是面面俱到的“字符串”,它们与“主题”和“正文”有很大不同。 它们是电子邮件地址,我们可以通过使用一些理智的正则表达式来验证其值来表示它们。 (谨防ReDoS )

使用工厂方法作为创建“始终有效”对象的方法的合理性已得到广泛讨论和验证。 考虑到这一点,我们将为示例用例创建一个EmailAddress类,然后将其用作Sender和Receiver字段的自定义原始类型。

public final class EmailAddress {private final String value;private EmailAddress(final String value) {this.value = value;}public static EmailAddress fromStringValue(final String value) {final String validated = EmailAddressValidator.ensureEmailAddress(value, "emailAddress");return new EmailAddress(validated);}
}

由于–唯一的实例变量是私有的且是最终变量,因此只能使用私有的构造函数进行分配,只有在将其传递给构造函数之前,可以使用验证输入的公共工厂方法从类外部调用该私有构造函数–我们可以请确保每当我们收到EmailAddress实例时,它都是有效的。

如果您现在对EmailAddressValidator实现感到好奇,请确保签出此示例项目的源代码 。

现在,我们的域对象不仅可以使用默认原语(例如String,Double,Integer等),还可以使用自定义原语(例如EmailAddress和Body,Subject等)。通常,尽管我们需要能够将域对象存储在数据库中或将其传达给其他服务或UI。 尽管没有其他方知道名为EmailAddress的自定义基元。 因此,我们需要它的“表示形式”,即HTTP,持久性和人性化的东西–字符串。

public final class EmailAddress {private final String value;public static EmailAddress fromStringValue(final String value) {final String validated = EmailAddressValidator.ensureEmailAddress(value, "emailAddress");return new EmailAddress(validated);}public String stringValue() {return this.value;}
}

我们添加的方法“ stringValue”是自定义基元的字符串表示形式。 现在,我们可以发送EmailAddress的“ stringValue”,然后根据接收到的值对其进行重构。 本质上,“ fromString”和“ stringValue”方法分别是EmailAddress的“反序列化”和“序列化”机制。

按照这种方法,我们还可以为电子邮件的正文和主题创建自定义基元:

public final class Body {private final String value;public static Body fromStringValue(final String value) {final String emailAddress = LengthValidator.ensureLength(value, 1, 1000, "body");return new Body(emailAddress);}public String stringValue() {return this.value;}
}public final class Subject {private final String value;public static Subject fromStringValue(final String value) {final String validated = LengthValidator.ensureLength(value, 1, 256, "subject");return new Subject(validated);}public String stringValue() {return this.value;}
}

数据传输对象

有了我们的自定义基元,我们现在可以创建适当的数据传输对象–电子邮件,这是一个非常简单的任务,因为它基本上是一个不变的结构:

public final class Email {public final EmailAddress sender;public final EmailAddress receiver;public final Subject subject;public final Body body;
}

相同的“始终有效”的方法也适用于数据传输对象,除了在这里,由于我们利用了自定义基元,因此时间更短。

DTO的工厂方法可以像验证必填字段的存在一样简单,也可以像应用跨字段验证一样复杂。

public final class Email {public final EmailAddress sender;public final EmailAddress receiver;public final Subject subject;public final Body body;public static Email restore(final EmailAddress sender,final EmailAddress receiver,final Subject subject,final Body body) {RequiredParameterValidator.ensureNotNull(sender, "sender");RequiredParameterValidator.ensureNotNull(receiver, "receiver");RequiredParameterValidator.ensureNotNull(body, "body");return new Email(sender, receiver, subject, body);
}

不幸的是,现代(反)序列化和验证框架在这种DTO中不能很好地发挥作用。

这是一个JSON示例,如果您使用默认配置将电子邮件DTO馈送到这样的框架,则可能会获得最佳效果:

{"sender": {"value": "sender@example.com"},"receiver": {"value": "receiver@example.com"},"subject": {"value": "subject"},"body": {"value": "body"}
}

虽然人们期望的是:

{"sender": "sender@example.com","receiver": "receiver@example.com","subject": "subject","body": "body"
}

尽管可以使用大量样板代码来缓解此问题,但是验证是另一种野兽,当您希望从服务器“一次报告所有验证错误”时,验证就变得致命。 为什么不立即告诉用户发送方和接收方均无效,而不是发送寻求许可A38的请求发送给用户。 实际上,这就是我们在尝试编写现代微服务时,同时又遵循Clean Code的最佳实践的感觉,Domain Driven Design,Domain Driven Security的“始终有效”方法……

这就是MapMate需要解决的问题。

MapMate

与HttpMate一样,我们确保提供一个易于构建的构建器,同时保留细粒度定制的可能性。 这是使我们的电子邮件示例序列化,反序列化和验证我们的自定义基元和DTO的绝对最低配置。

public static MapMate mapMate() {return MapMate.aMapMate("com.envimate.examples.email_use_case").usingJsonMarshallers(new Gson()::toJson, new Gson()::fromJson).build();
}

这部分将使以下JSON成为有效请求:

{"sender": "sender@example.com","receiver": "receiver@example.com","subject": "Hello world!","body": "Hello from Sender to Receiver!"
}

您必须指定要扫描(递归)的程序包和一对(非)编组器。 可以是任何可以从Map生成字符串的内容,反之亦然。 这是使用ObjectMapper的示例:

final ObjectMapper objectMapper = new ObjectMapper();
return MapMate.aMapMate("com.envimate.examples.email_use_case").usingJsonMarshallers(value -> {try {return objectMapper.writeValueAsString(value);} catch (JsonProcessingException e) {throw new UnsupportedOperationException("Could not parse value " + value, e);}}, new Unmarshaller() {@OverridepublicT unmarshal(final String input, final Classtype) {try {return objectMapper.readValue(input, type);} catch (final IOException e) {throw new UnsupportedOperationException("Could not parse value " + input + " to type " + type, e);}}}).withExceptionIndicatingValidationError(CustomTypeValidationException.class).build();

承诺的验证异常聚合又如何呢?
在我们的示例中,如果自定义原语或DTO无效,则所有验证都返回CustomTypeValidationException的实例。

添加以下行,以指示MapMate将您的Exception类识别为验证错误的指示。

public static MapMate mapMate() {return MapMate.aMapMate("com.envimate.examples.email_use_case").usingJsonMarshallers(new Gson()::toJson, new Gson()::fromJson).withExceptionIndicatingValidationError(CustomTypeValidationException.class).build();
}

现在,如果我们尝试以下请求:

{"sender": "not-a-valid-sender-value","receiver": "not-a-valid-receiver-value","subject": "Hello world!","body": "Hello from Sender to Receiver!"
}

我们将收到以下答复:

HTTP/1.1 400 Bad Request
Date: Tue, 04 Jun 2019 18:30:51 GMT
Transfer-encoding: chunked{"message":"receiver: Invalid email address: 'not-a-valid-receiver-value',sender: Invalid email address: 'not-a-valid-sender-value'"}

最后的话

此处提供的MapMate生成器可简化初始使用。 但是,所有描述的默认值都是可配置的,此外,您可以从“自定义基元”和“ DTO”中排除程序包和类,可以配置哪些异常被视为“验证错误”以及如何对其进行处理,还可以为“自定义”指定其他方法名称原始序列化,或者提供您的lambda来同时进行这两个序列的反序列化。

有关MapMate的更多示例和详细信息,请查看MapMate存储库 。

让我们知道您的想法以及您接下来想在MapMate中看到的功能!

翻译自: https://www.javacodegeeks.com/2019/08/deserialization-and-validation-of-custom-primitives-and-dtos.html

纹理和基元

纹理和基元_自定义基元和DTO的(反)序列化和验证相关推荐

  1. 运动基元_开发人员的新分布式基元

    运动基元 面向对象的基元(进程内基元) 作为Java开发人员,我非常熟悉面向对象的概念,例如类,对象,继承,封装,多态性等.除了面向对象的概念之外,我还非常熟悉Java运行时.它提供的功能,如何调整它 ...

  2. 纹理和基元_通过粘性仙人掌基元进行延迟加载和缓存

    纹理和基元 您显然知道什么是延迟加载 ,对吗? 而且您无疑知道缓存 . 据我所知,Java中没有一种优雅的方法来实现它们中的任何一个. 这是我在Cactoos原语的帮助下为自己找到的. Matteo ...

  3. 运动基元_发现大量Java基元集合处理

    运动基元 在阅读博客文章5减少Java垃圾收集开销的技巧时 ,我想起了一个名为Trove的小型Java收集库,该库"为Java提供了高速的常规和原始收集". 我对将Trove应用到 ...

  4. R语言多层桑基图_桑基图的绘制核心微生物组分类学及丰度展示

    为什么要用桑基图 桑基图 (Sankey diagram)是一种展现数据之间流动和相互关系的图像,类似于一种网络图,通过不同层级节点之间的连接以及连接边的宽度,展示数据之间的流动关系. 在微生物群落分 ...

  5. OpenCASCADE可视化:3D演示之图形基元

    OpenCASCADE可视化:3D演示之图形基元 结构层次 图形基元 原始数组 文本原语 材料 纹理 自定义着色器 所述Graphic3d包用于在3D查看器来创建3D图形对象.这些称为结构的对象由一组 ...

  6. 定制基元和DTO的(反)序列化和验证

    最近,我们为您提供了新的HTTP框架HttpMate. 在引言文章中 ,我们将请求和响应映射到域对象称为"最复杂的技术细节",以及如何通过另一个伴侣MapMate帮助我们. 实际上 ...

  7. [CLR via C#]5.1 基元类型

    原文:[CLR via C#]5.1 基元类型 某些数据类在开发中非常常用,以至于许多编译器允许代码已简化的语法来操作它们.例如可以使用以下语法来分配一个整数: System.Int32 a = ne ...

  8. 基元检测 Primitive Detection

    基元的概念 基元泛指图像中有特点的单元.常说的基元有:边缘.角点.斑点.直线段.圆.等 基元检测是图像分析的基础 边缘(Edge)检测 边缘是图像中像素灰度值发生剧烈变化而不连续的结果 边缘是赋予单个 ...

  9. 第五章 基元类型,引用类型和值类型

    目录 5.1 编程语言的基元类型 5.2 引用类型和值类型 5.3 值类型的装箱和拆箱 5.4 对象哈希码 5.5 dynamic基元类型 5.1 编程语言的基元类型 编译器直接支持的数据类型称为基元 ...

最新文章

  1. 判断一个整数是否是水仙花数 || 编写程序,根据输入的月份和年份,求出该月的天数(1-12)|| 运算符的优先级问题 ||输出小写的a-z以及大写的Z—A||求出1-1/2+1/3-1/4…..1/
  2. 数学建模论文写作小技巧分享
  3. BZOJ 3992 [SDOI2015]序列统计
  4. 想问一下C++里queue要怎么遍历
  5. 论文 计算机动态网页的制作,毕业论文 动态网页制作学习网站的设计与实现
  6. mysqld.exe
  7. ssm框架逻辑删除mysql_ssm框架小总结——批量删除
  8. 如何解决移动硬盘找不到的问题
  9. 转移服务器显示乱码,两台服务器之间数据传输乱码问题
  10. sql server 标准版升级专业版或者开发版没有sql server profiler
  11. 网站服务器建立数据库连接时出错,修复Wordpress博客网站“建立数据库连接时出错”错误记录 | 科技爱好者博客 -专注于树莓派(Raspberry Pi)...
  12. ARCore 之路:如何创建一个 ARCore 程序?
  13. SAS学习笔记41 宏变量存储及间接引用
  14. 公主与骑士-ZZUOJ
  15. 联邦贸易委员会:大数据带来的歧视风险
  16. 我同意 三江方士 对 哥德巴赫猜想 的 看法
  17. Microsoft 365独家安全解决方案
  18. VO、DTO、BO、QO、DO 如何使用,在那一层使用,一张图告诉你;别再纠结命名规则啦,我来告诉你
  19. 苹果摆脱对中国制造的依赖?iPhone14的拆解结果显示恰恰相反,更离不开中国制造了...
  20. 福利彩票36选7模拟程序

热门文章

  1. cf 1511 D. Min Cost String
  2. 牛客网【每日一题】7月30日题目精讲—Xor Path
  3. Poj 1011 UVA - 307 Sticks
  4. [LOJ #521]「LibreOJ β Round #3」绯色 IOI(抵达)(结论)
  5. 数论一之定理证明——裴蜀/威尔逊/费马/扩展欧几里得/[扩展]欧拉/[扩展]中国剩余定理,欧拉函数,逆元,剩余系,筛法
  6. P8215-[THUPC2022 初赛]分组作业【网络流】
  7. P3426-[POI2005]SZA-Template【KMP】
  8. P4197-Peaks【Kruskal重构树,主席树】
  9. [2020.10.25NOIP模拟赛]序列【Splay】
  10. jzoj4010-Philips and Calculator【搜索,dp】