日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時間:2023/12/3 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 纹理和基元_自定义基元和DTO的(反)序列化和验证 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

紋理和基元

最近,我們?yōu)槟峁┝诵碌腍TTP框架HttpMate。 在介紹性文章中 ,我們將請求和響應(yīng)映射到域?qū)ο蠓Q為“最復(fù)雜的技術(shù)細(xì)節(jié)”,以及如何通過另一個伴侶MapMate幫助我們。

實(shí)際上,當(dāng)將請求屬性映射到您的域?qū)ο髸r,MapMate減輕了HttpMate的負(fù)擔(dān)。 它負(fù)責(zé)將響應(yīng)轉(zhuǎn)換為適當(dāng)?shù)母袷?#xff08;JSON,XML,YAML等),本質(zhì)上執(zhí)行反序列化和序列化,但還有很多其他工作。

在本文中,我們將重點(diǎn)介紹MapMate如何以受控和可預(yù)測的方式幫助我們處理(反序列化)請求/響應(yīng)對象。

自定義基元

讓我們回顧一下上一篇文章中的示例; 我們有一個簡單的UseCase發(fā)送電子郵件。 為此,我們需要一個Email對象,該對象應(yīng)具有:

  • 發(fā)件人
  • 接收者
  • 學(xué)科
  • 身體

所有這些字段都可以表示為字符串,甚至可以表示為字節(jié)數(shù)組。 選擇用來表示數(shù)據(jù)的通用類型越多,以后解釋數(shù)據(jù)的可能性就越大。 想象一下以下方法:

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

這給我們留下了很多未解決的問題:

  • 是發(fā)件人instanceOf是字符串還是字節(jié)[]?
  • 編碼是什么?
  • 拉鏈被壓縮了嗎?

清單繼續(xù)。 盡管在某些情況下這可能是適當(dāng)?shù)?#xff0c;但我敢打賭,您會更滿意:

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

后者留出了較少的解釋空間:例如,我們不再需要假設(shè)編碼或完全質(zhì)疑參數(shù)的類型。

但是,它仍然是模棱兩可的,發(fā)件人字段是否帶有用戶名或她的電子郵件地址? 同樣的歧義是編寫單元測試時產(chǎn)生無限不確定性的原因……在某種程度上,使用隨機(jī)字符串生成器來測試一種人們只能接受電子郵件地址的方法。

對于人員和編譯器,以下方法簽名在歧義方面做得更好:

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

我們可以以相同的方式相信字符串是字符串,整數(shù)是整數(shù),我們現(xiàn)在可以相信EmailAddress是電子郵件地址,主題實(shí)際上是主題–它們成為了send email方法的自定義原語。

發(fā)件人和接收者不是面面俱到的“字符串”,它們與“主題”和“正文”有很大不同。 它們是電子郵件地址,我們可以通過使用一些理智的正則表達(dá)式來驗(yàn)證其值來表示它們。 (謹(jǐn)防ReDoS )

使用工廠方法作為創(chuàng)建“始終有效”對象的方法的合理性已得到廣泛討論和驗(yàn)證。 考慮到這一點(diǎn),我們將為示例用例創(chuàng)建一個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);} }

由于–唯一的實(shí)例變量是私有的且是最終變量,因此只能使用私有的構(gòu)造函數(shù)進(jìn)行分配,只有在將其傳遞給構(gòu)造函數(shù)之前,可以使用驗(yàn)證輸入的公共工廠方法從類外部調(diào)用該私有構(gòu)造函數(shù)–我們可以請確保每當(dāng)我們收到EmailAddress實(shí)例時,它都是有效的。

如果您現(xiàn)在對EmailAddressValidator實(shí)現(xiàn)感到好奇,請確保簽出此示例項(xiàng)目的源代碼 。

現(xiàn)在,我們的域?qū)ο蟛粌H可以使用默認(rèn)原語(例如String,Double,Integer等),還可以使用自定義原語(例如EmailAddress和Body,Subject等)。通常,盡管我們需要能夠?qū)⒂驅(qū)ο蟠鎯υ跀?shù)據(jù)庫中或?qū)⑵鋫鬟_(dá)給其他服務(wù)或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”是自定義基元的字符串表示形式。 現(xiàn)在,我們可以發(fā)送EmailAddress的“ stringValue”,然后根據(jù)接收到的值對其進(jìn)行重構(gòu)。 本質(zhì)上,“ fromString”和“ stringValue”方法分別是EmailAddress的“反序列化”和“序列化”機(jī)制。

按照這種方法,我們還可以為電子郵件的正文和主題創(chuàng)建自定義基元:

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;} }

數(shù)據(jù)傳輸對象

有了我們的自定義基元,我們現(xiàn)在可以創(chuàng)建適當(dāng)?shù)臄?shù)據(jù)傳輸對象–電子郵件,這是一個非常簡單的任務(wù),因?yàn)樗旧鲜且粋€不變的結(jié)構(gòu):

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

相同的“始終有效”的方法也適用于數(shù)據(jù)傳輸對象,除了在這里,由于我們利用了自定義基元,因此時間更短。

DTO的工廠方法可以像驗(yàn)證必填字段的存在一樣簡單,也可以像應(yīng)用跨字段驗(yàn)證一樣復(fù)雜。

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); }

不幸的是,現(xiàn)代(反)序列化和驗(yàn)證框架在這種DTO中不能很好地發(fā)揮作用。

這是一個JSON示例,如果您使用默認(rèn)配置將電子郵件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" }

盡管可以使用大量樣板代碼來緩解此問題,但是驗(yàn)證是另一種野獸,當(dāng)您希望從服務(wù)器“一次報告所有驗(yàn)證錯誤”時,驗(yàn)證就變得致命。 為什么不立即告訴用戶發(fā)送方和接收方均無效,而不是發(fā)送尋求許可A38的請求發(fā)送給用戶。 實(shí)際上,這就是我們在嘗試編寫現(xiàn)代微服務(wù)時,同時又遵循Clean Code的最佳實(shí)踐的感覺,Domain Driven Design,Domain Driven Security的“始終有效”方法……

這就是MapMate需要解決的問題。

MapMate

與HttpMate一樣,我們確保提供一個易于構(gòu)建的構(gòu)建器,同時保留細(xì)粒度定制的可能性。 這是使我們的電子郵件示例序列化,反序列化和驗(yàn)證我們的自定義基元和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生成字符串的內(nèi)容,反之亦然。 這是使用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();

承諾的驗(yàn)證異常聚合又如何呢?
在我們的示例中,如果自定義原語或DTO無效,則所有驗(yàn)證都返回CustomTypeValidationException的實(shí)例。

添加以下行,以指示MapMate將您的Exception類識別為驗(yàn)證錯誤的指示。

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

現(xiàn)在,如果我們嘗試以下請求:

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

我們將收到以下答復(fù):

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生成器可簡化初始使用。 但是,所有描述的默認(rèn)值都是可配置的,此外,您可以從“自定義基元”和“ DTO”中排除程序包和類,可以配置哪些異常被視為“驗(yàn)證錯誤”以及如何對其進(jìn)行處理,還可以為“自定義”指定其他方法名稱原始序列化,或者提供您的lambda來同時進(jìn)行這兩個序列的反序列化。

有關(guān)MapMate的更多示例和詳細(xì)信息,請查看MapMate存儲庫 。

讓我們知道您的想法以及您接下來想在MapMate中看到的功能!

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

紋理和基元

總結(jié)

以上是生活随笔為你收集整理的纹理和基元_自定义基元和DTO的(反)序列化和验证的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。