深入瞭解數據校驗:Java Bean Validation 2.0(JSR380)

来源:https://www.cnblogs.com/fangshixiang/archive/2019/07/23/11234489.html
-Advertisement-
Play Games

一個可以沉迷於技術的程式猿,wx加入加入技術群:fsx641385712 ...


每篇一句

吾皇一日不退役,爾等都是臣子

相關閱讀

【小家Java】深入瞭解數據校驗(Bean Validation):基礎類打點(ValidationProvider、ConstraintDescriptor、ConstraintValidator)


對Spring感興趣可掃碼加入wx群:`Java高工、架構師3群`(文末有二維碼)

前言

前幾篇文章在講Spring的數據綁定的時候,多次提到過數據校驗。可能有人認為數據校驗模塊並不是那麼的重要,因為硬編碼都可以做。若是這麼想的話,那就大錯特錯了~
前面講解DataBinder的時候一個小細節,它所在的包是:org.springframework.validation,並且在分析源碼的時候能看到DataBinder它不僅能夠完成數據綁定,也提供了對數據校驗的支持且還保存了校驗結果。

我以數據綁定DataBinder為引子引出了數據校驗這一塊,是想表明它的重要性。連Java都把它抽象成了JSR標準進行提出,so我認為這塊是必修課,有必要瞭解本章的內容。

為什麼要有數據校驗?

數據校驗 是非常常見的工作,在日常的開發中貫穿於代碼的各個層次,從上層的View層到底層的數據層。

在此處有必要再強調一句:前面說了數據綁定並不屬於Spring MVC的專利,同樣的數據校驗也不是只會發生在web層,它可以在任意一層,從後面的示例中你會有更深的理解

在任何時候,當你要處理一個應用程式的業務邏輯,數據校驗是你必須要考慮和麵對的事情。應用程式必須通過某種手段來確保輸入進來的數據從語義上來講是正確的(比如生日必須是過去時,年齡必須>0等等~)。

我們知道通常情況下程式肯定是分層的,不同的層一般由不同的人來開發。若你是一個有經驗的程式員, 我相信你肯定見過在不同的層了都出現了相同的校驗代碼,這就是某種意義上的垃圾代碼。

public String queryValueByKey(String parmTemplateCode, String conditionName, String conditionKey, String resultName) {
    checkNotNull(parmTemplateCode, "parmTemplateCode not null");
    checkNotNull(conditionName, "conditionName not null");
    checkNotNull(conditionKey, "conditionKey not null");
    checkNotNull(resultName, "resultName not null");
    ...
}

從這個簡單的方法入參校驗至少能發現如下問題:

  1. 需要寫大量的代碼來進行參數驗證。(這種代碼多了就算垃圾代碼)
  2. 需要通過註釋來知道每個入參的約束是什麼(否則別人咋看得懂)
  3. 每個程式員做參數驗證的方式不一樣,參數驗證不通過拋出的異常也不一樣(後期幾乎沒法維護)

如上會導致代碼冗餘和一些管理的問題(代碼量越大,管理起來維護起來就越困難),比如說語義的一致性等。為了避免這樣的情況發生,最好是將驗證邏輯與相應的域模型(領域模型的概念)進行綁定,這就是本文提供的一個新思路(其實是JavaEE提供的思路)

為瞭解決這個問題,Bean ValidationJavaBean 驗證定義了相應的元數據模型和 API。預設的元數據是 各種Java Annotations,當然也支持xml方式並且你也可以擴展~
可以說Bean ValidationJavaBean的一個拓展,它可以佈局於任意一層代碼,不局限於Web應用還是端應用。

Java Bean Validation

JSR是Java Specification Requests的縮寫,意思是Java 規範提案。關於數據校驗這塊,最新的是JSR380,也就是我們常說的Bean Validation 2.0

Bean Validation 2.0 是JSR第380號標準。該標準連接如下:https://www.jcp.org/en/egc/view?id=380
Bean Validation的主頁:http://beanvalidation.org
Bean Validation的參考實現:https://github.com/hibernate/hibernate-validator

Bean Validation是一個通過配置註解來驗證參數的框架,它包含兩部分Bean Validation API(規範)和Hibernate Validator(實現)。
Bean Validation是Java定義的一套基於註解/xml的數據校驗規範,目前已經從JSR 303的1.0版本升級到JSR 349的1.1版本,再到JSR 380的2.0版本(2.0完成於2017.08),已經經歷了三個版本(我截圖如下:)
在這裡插入圖片描述
現在絕大多數coder使用者其實都還在使用Bean Validation 1.1,畢竟一般來說它已經夠用了~
本文會介紹Bean Validation 2.0提供的一些實用的新東西,畢竟Java8現在已成為主流,完全可以使用了~

簡單Demo示例

要想使用它,首先就得導包嘛~根據經驗,和JCache類似Java只提供了規範,並沒有提供實現,所以我們可以先找到它的API包然後導入:

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <!-- <version>1.1.0.Final</version> -->
    <version>2.0.1.Final</version>
</dependency>

關於版本之間的差異其實不是本文說明的重點,畢竟2.0做到了很好的向下相容,使用起來是無縫的。
但是本處還是給個1.1版本和2.0.1的截圖,感官上簡單對比一下區別:
在這裡插入圖片描述
在這裡插入圖片描述

相容性表格

Bean Validation Hibernate Validation JDK Spring Boot
1.1 5.4 + 6+ 1.5.x
2.0 6.0 + 8+ 2.0.x

關於Bean Validation 2.0的關註點(新特性)

因為2.0推出的時間確實不算長,so此處我把一些重要的關註點列舉如下:

  1. 對Java的最低版本要求是Java 8
  2. 支持容器的校驗,通過TYPE_USE類型的註解實現對容器內容的約束:List<@Email String>
  3. 支持日期/時間的校驗,@Past@Future
  4. 拓展元數據(新增註解):@Email,@NotEmpty,@NotBlank,@Positive, @PositiveOrZero,@Negative,@NegativeOrZero,@PastOrPresent和@FutureOrPresent
    1. 像@Email、@NotEmpty、@NotBlank之前是Hibernate額外提供的,2.0標準後hibernate自動退位讓賢並且標註為過期了
  5. Bean Validation 2.0的唯一實現為Hibernate Validator。(其實還有Apache BVal,但是你懂的,forget it)
  6. 對於Hibernate Validator,它自己也擴展了一些註解支持。
    1. 6.0以上版本新增(對應標準2.0版本):@UniqueElements、@ISBN、@CodePointLength、、、、、、、、
    2. 6.0以下版本可以使用的: @URL、@ScriptAssert、@SafeHtml、@Range、@ParameterScriptAssert、@Mod11Check、@Mod10Check、@LuhnCheck、@Length、@EAN、@Currency、@CreditCardNumber、@ConstraintComposition、@DurationMax、@DurationMin、**@REGON、@PESEL、@NIP、@TituloEleitoral、@CPF、@CNPJ**
    3. Hibernate Validator預設會校驗完所有的屬性,然後返回所有的驗證失敗信息。開啟fail fast mode後,只要有一個驗證失敗,則返回驗證失敗信息。

so,對於Java Bean Validation的實現落地產品就沒啥好選的,導入Hibernate Validator(最新版本)吧:

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.17.Final</version>
</dependency>

==小細節:==
在這裡插入圖片描述
可以看到,導入了hibernate-validator就必要再自己導入Java Bean ValidationAPI了,因此建議不用再手動導入API,交給內部來管理依賴。

定義一個待校驗的普通JavaBean:

@Getter
@Setter
@ToString
public class Person {

    // 錯誤消息message是可以自定義的
    @NotNull(message = "名字不能為null")
    public String name;
    @Positive
    public Integer age;

    @NotNull
    @NotEmpty
    private List<@Email String> emails;
    @Future
    private Date start;

}

書寫測試用例:

    public static void main(String[] args) {
        Person person = new Person();
        //person.setName("fsx");
        person.setAge(-1);
        // email校驗:雖然是List都可以校驗哦
        person.setEmails(Arrays.asList("[email protected]", "[email protected]", "aaa.com"));
        //person.setStart(new Date()); //start 需要是一個將來的時間: Sun Jul 21 10:45:03 CST 2019
        //person.setStart(new Date(System.currentTimeMillis() + 10000)); //校驗通過

        // 對person進行校驗然後拿到結果(顯然使用時預設的校驗器)   會保留下校驗失敗的消息
        Set<ConstraintViolation<Person>> result = Validation.buildDefaultValidatorFactory().getValidator().validate(person);
        // 對結果進行遍歷輸出
        result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
                .forEach(System.out::println);
    }

運行,報錯啦:

Caused by: java.lang.ClassNotFoundException: javax.el.ELManager
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
...

可以看到運行必須依賴於javax.el這個包。(其實我是比較費解的,為何校驗框架非得依賴它呢?有小伙伴可以幫忙解釋一下嗎?)

那行,導入依賴javax.el以及它的實現:

<!-- 註意這裡導入的是Apr, 2013發佈的el3.x的版本,但是glassfish並沒有對此版本進行支持了  當然tomcat肯定是支持的 -->
<dependency>
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>3.0.1-b06</version>
</dependency>
<!-- servlet容器大都對el有實現(支持jsp的都對此有實現),比如tomcat/glassfish等 -->
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>javax.el</artifactId>
    <version>2.2.6</version>
</dependency>

需要註意的是,網上大都建議導入org.glassfish.web包。但是EL3.0後它並沒有再提供支持了,因此我個人是不建議使用它,而是使用下麵tomcat的實現的~

關於EL的實現此處啰嗦一句:JavaEE並沒有提供el的實現,需要容器自行提供,比如上面你想要導入最為流行的tomcat,你可以導入如下jar即可:

<!-- 嵌入式的tomcat -->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-el</artifactId>
    <version>9.0.22</version>
</dependency>
<!-- 傳統的tomcat(需要註意的是:傳統的tomcat這種jar是不需要你手動導入的,tomcat自帶的) -->
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-jasper-el</artifactId>
    <version>9.0.22</version>
    <scope>provided</scope>
</dependency>

此處還需要說明一點的是:嵌入式tomcat(比如SpringBoot環境)若要使用時需要顯示導入的。但是傳統tomcat中你若要使用是不用自己導入的(tomcat自帶此jar)。

但是,但是,但是自從tomcat8.5後不再自帶jsper-el的包了,需要手動導入。(tomcat7還是有的~

==最佳實踐:==
一般來說,javax.el-api以及validation-api都是沒有必要單獨導入的,第三方包都會自帶。所以絕大數情況下,我們只需要這麼導入即可正常work,形如下麵這樣非常趕緊整潔:

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.17.Final</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-el</artifactId>
    <version>9.0.22</version>
</dependency>
此處可能有伙伴會問:為何自己在使用的時候從來都沒有導入過EL相關Jar包,也能正常數據校驗呢?

答:那是因為絕大多數情況下你使用@Valid是使用在Spring MVC上,它是不依賴於EL方式的,下篇文章會詳細說明關於數據校驗在Spring上的使用。而本文主要還是講解API的方式~

---

經過一番導包後,再次運行列印如下(方式一、方式二結果一致):

name名字不能為null: null //  此處錯誤消息是自己的自定義內容
age必須是正數: -1
emails[2].<list element>不是一個合法的電子郵件地址: aaa.com

這樣通過API調用的方式就完成了對這個JavaBean的屬性校驗~

核心API分析

Validation

官方給它的定義為:This class is the entry point for Bean Validation.它作為校驗的入口,有三種方式來啟動它:

  1. 最簡單方式:使用預設的ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 雖然是預設的單也會有如下2種情況:
    1. 若使用了xml配置了一個provider,那就會使用這個provider來提供Factory
    2. 若沒有xml或者xml力沒有配置provider,那就是用預設的ValidationProviderResolver實現類來處理
  2. 方式二:選擇自定義的ValidationProviderResolver來跟XML配置邏輯選出一個ValidationProvider來。大致代碼如下:
Configuration configuration = Validation.byDefaultProvider()
        .providerResolver(new MyResolverStrategy()) // 自定義一個ValidationProviderResolver的實現類
        .configure();
ValidatorFactory factory = configuration.buildValidatorFactory();
  1. 第三種方式就更加自由了:你可以直接提供一個類型安全ValidationProvider實現。比如HibernateValidator就是一個ValidationProvider的實現:
HibernateValidatorConfiguration configuration = Validation.byProvider(HibernateValidator.class)
        // .providerResolver( ... ) // 因為制定了Provider,這個參數就可選了
        .configure()
        .failFast(false);
ValidatorFactory validatorFactory = configuration.buildValidatorFactory();

這三種初始化方式,在源碼處就是對應提供的三個public static方法:

public class Validation {

    // 方式一
    public static ValidatorFactory buildDefaultValidatorFactory() {
        return byDefaultProvider().configure().buildValidatorFactory();
    }
    // 方式二
    public static GenericBootstrap byDefaultProvider() {
        return new GenericBootstrapImpl();
    }
    // 方式三
    public static <T extends Configuration<T>, U extends ValidationProvider<T>> ProviderSpecificBootstrap<T> byProvider(Class<U> providerType) {
        return new ProviderSpecificBootstrapImpl<>( providerType );
    }
    ...
}

對於若你想使用xml文件獨立配置校驗規則,可以使用Configuration.addMapping(new FileInputStream(validationFile));,現在很少這麼使用,略~
使用註意事項:ValidatorFactory被創建後應該緩存起來再提供使用,因為它是縣城安全的。

因為現在都會使用Hibernate-Validation來處理校驗,因此此處只關心方式三~

HibernateValidatorConfiguration

此介面表示配置,繼承自標註介面javax.validation.Configuration。很明顯,它是HibernateValidator的專屬配置類
在這裡插入圖片描述
先看頂級介面:javax.validation.Configuration,為構建ValidatorFactory的配置類。預設情況下,它會讀取配置文件META-INF/validation.xml,Configuration提供的API方法是覆蓋xml配置文件項的。若沒有找到validation.xml,就會使用預設的ValidationProviderResolver也就是:DefaultValidationProviderResolver

public interface Configuration<T extends Configuration<T>> {
    // 該方法調用後就不會再去找META-INF/validation.xml了
    T ignoreXmlConfiguration();
    // 消息內插器  它是個狠角色,關於它的使用場景,後續會有詳解(包括Spring都實現了它來做事)
    // 它的作用是:插入給定的約束衝突消息
    T messageInterpolator(MessageInterpolator interpolator);
    // 確定bean驗證提供程式是否可以訪問屬性的協定。對每個正在驗證或級聯的屬性調用此約定。(Spring木有實現它)
    // 對每個正在驗證或級聯的屬性都會調用此約定
    // Traversable: 可移動的
    T traversableResolver(TraversableResolver resolver);
    // 創建ConstraintValidator的工廠
    // ConstraintValidator:定義邏輯以驗證給定對象類型T的給定約束A。(A是個註解類型)
    T constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory);
    // ParameterNameProvider:提供Constructor/Method的方法名們
    T parameterNameProvider(ParameterNameProvider parameterNameProvider);
    // java.time.Clock 用作判定@Future和@Past(預設取值當前時間)
    // 若你希望他是個邏輯實現,提供一個它即可
    // @since 2.0
    T clockProvider(ClockProvider clockProvider);
    // 值提取器。這是add哦~ 負責從Optional、List等這種容器里提取值~
    // @since 2.0
    T addValueExtractor(ValueExtractor<?> extractor);
    // 載入xml文件
    T addMapping(InputStream stream);
    // 添加特定的屬性給Provider用的。此屬性等效於XML配置屬性。
    // 此方法通常是框架自己分析xml文件得到屬性值然後放進去,調用者一般不使用(當然也可以用)
    T addProperty(String name, String value);
    
    // 下麵都是get方法嘍
    MessageInterpolator getDefaultMessageInterpolator();
    TraversableResolver getDefaultTraversableResolver();
    ConstraintValidatorFactory getDefaultConstraintValidatorFactory();
    ParameterNameProvider getDefaultParameterNameProvider();
    ClockProvider getDefaultClockProvider();
    BootstrapConfiguration getBootstrapConfiguration(); // 整個配置也可返回出去

    // 上面都是工作,這個方法才是最終需要調用的:得到一個ValidatorFactory
    ValidatorFactory buildValidatorFactory();
}

該介面提供了一些標準的配置項。在實際應用中都是使用Hibernate Validation,所以再看看這個具體的子介面:

public interface HibernateValidatorConfiguration extends Configuration<HibernateValidatorConfiguration> {

    // 這批屬性,證明直接可以通過System屬性值來控制,大大地方便~
    // 這個機制快速失敗機制:true檢查完一個有錯誤就返回,false全部檢查完把錯誤消息一起返回   預設false
    String FAIL_FAST = "hibernate.validator.fail_fast"; 
    String ALLOW_PARAMETER_CONSTRAINT_OVERRIDE = "hibernate.validator.allow_parameter_constraint_override";
    String ALLOW_MULTIPLE_CASCADED_VALIDATION_ON_RESULT = "hibernate.validator.allow_multiple_cascaded_validation_on_result";
    String ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS = "hibernate.validator.allow_parallel_method_parameter_constraint";
    // @since 5.2
    @Deprecated
    String CONSTRAINT_MAPPING_CONTRIBUTOR = "hibernate.validator.constraint_mapping_contributor";
    // @since 5.3
    String CONSTRAINT_MAPPING_CONTRIBUTORS = "hibernate.validator.constraint_mapping_contributors";
    // @since 6.0.3
    String ENABLE_TRAVERSABLE_RESOLVER_RESULT_CACHE = "hibernate.validator.enable_traversable_resolver_result_cache";
    // @since 6.0.3  ScriptEvaluatorFactory:執行腳本
    @Incubating
    String SCRIPT_EVALUATOR_FACTORY_CLASSNAME = "hibernate.validator.script_evaluator_factory";
    // @since 6.0.5 comparing date/time in temporal constraints. In milliseconds.
    @Incubating
    String TEMPORAL_VALIDATION_TOLERANCE = "hibernate.validator.temporal_validation_tolerance";

    // ResourceBundleMessageInterpolator用於 load resource bundles
    ResourceBundleLocator getDefaultResourceBundleLocator();
    // 創建一個ConstraintMapping:通過編程API配置的約束映射
    // 設置映射後,必須通過addMapping(constraintmapping)將其添加到此配置中。
    ConstraintMapping createConstraintMapping();
    // 拿到所有的值提取器  @since 6.0
    @Incubating
    Set<ValueExtractor<?>> getDefaultValueExtractors();

    // 往下就開始配置了~~~~~~~~~~
    HibernateValidatorConfiguration addMapping(ConstraintMapping mapping);
    HibernateValidatorConfiguration failFast(boolean failFast);
    // used for loading user-provided resources:
    HibernateValidatorConfiguration externalClassLoader(ClassLoader externalClassLoader);
    // true:表示允許覆蓋約束的方法。false表示不予許(拋出異常)  預設值是false
    HibernateValidatorConfiguration allowOverridingMethodAlterParameterConstraint(boolean allow);
    // 定義是否允許對返回值標記多個約束以進行級聯驗證。  預設是false
    HibernateValidatorConfiguration allowMultipleCascadedValidationOnReturnValues(boolean allow);
    // 定義約束的**並行方法**是否應引發ConstraintDefinitionException
    HibernateValidatorConfiguration allowParallelMethodsDefineParameterConstraints(boolean allow);
    // 是否允許緩存TraversableResolver  預設值是true
    HibernateValidatorConfiguration enableTraversableResolverResultCache(boolean enabled);
    // 設置一個腳本執行器
    @Incubating
    HibernateValidatorConfiguration scriptEvaluatorFactory(ScriptEvaluatorFactory scriptEvaluatorFactory);
    // 允許在時間約束中比較日期/時間時設置可接受的誤差範圍
    // 比如@Past @PastOrPresent @Future @FutureOrPresent
    @Incubating
    HibernateValidatorConfiguration temporalValidationTolerance(Duration temporalValidationTolerance);
    // 允許設置將傳遞給約束驗證器的有效負載。如果多次調用該方法,則只傳播最後傳遞的有效負載。
    @Incubating
    HibernateValidatorConfiguration constraintValidatorPayload(Object constraintValidatorPayload);
}

關於此介面的唯一實現類:ConfigurationImpl,這裡就不用再做分析了,因為對於Validation這塊,咱們面向介面編程是完全沒有問題的~

準備好了Configuration後,下一步顯然就是configuration.buildValidatorFactory()來得到一個ValidatorFactory嘍,關於ValidatorFactory這塊的內容,請聽下文分解~

總結

該文講解是關於Bean Validation數據校驗,在現在Spring的高度封裝下,越來越少的人能夠主動去發現Java實現/標準了~
實際上Spring的強大並不是自己創造了多少輪子,而是它主要是帶來了更為簡單的抽象,從而減少樣板代碼、促進解耦、提高可單測性。因此對於有些常用的功能還是建議稍微瞭解多一點,做到心中有數,運用起來也才會更加的游刃有餘

知識交流

若文章格式混亂,可點擊原文鏈接-原文鏈接-原文鏈接-原文鏈接-原文鏈接

==The last:如果覺得本文對你有幫助,不妨點個贊唄。當然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的~==

若對技術內容感興趣可以加入wx群交流:Java高工、架構師3群
若群二維碼失效,請加wx號:fsx641385712(或者掃描下方wx二維碼)。並且備註:"java入群" 字樣,會手動邀請入群


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Java中鎖的概念 自旋鎖 : 是指當一個線程在獲取鎖的時候,如果鎖已經被其他線程獲取,那麼該線程將迴圈等待,然後不斷判斷鎖是否能夠被成功獲取,直到獲取到鎖才會退出迴圈。 樂觀鎖 : 假定沒有衝突,在修改數據時如果發現數據和之前獲取的不一致,則讀最新數據,修改後重試修改 悲觀鎖 :假定會發生併發衝突 ...
  • 一、 函數list (1)定義:用打開的文件作為參數,把文件內的每一行內容作為一個元素 (2)格式:list(文件) (3)例子: 2.函數read (1)作用:按照字元進行讀取文件內容 (2)格式:文件.read(數字) 如果數字預設,那麼代表把所有的字元全都讀出來;如果裡面含有數字那麼代表一次性 ...
  • 9.9 線程理論 1、什麼是線程 線程指的是一條流水線的工作過程 進程根本就不是一個執行單位,進程其實是一個資源單位,一個進程內自帶一個線程,線程才是執行單位 2、進程VS線程 同一進程內的線程們共用該進程內資源,不同進程內的線程資源肯定是隔離的 創建線程的開銷比創建進程要小的多 同一進程內的線程們 ...
  • 本文介紹如何利用Python+uiautomator2 每日自動賺取支付寶積分。 支付寶的積分有啥用?誘惑誘惑你: 可以兌換視頻網站的VIP會員。 可以兌換各種優惠券。 可以在年底活動中兌換蘋果手機。 其他,一言難盡... 比如,我喜歡買知識課堂的課程。(知識付費時代,我用積分買知識) 好了,有了動 ...
  • 一、 cookie 1. 定義:保存在瀏覽器本地上的一組組鍵值對 2. 特點: 由伺服器讓瀏覽器進行設置的 瀏覽器保存在瀏覽器本地 下次訪問時自動攜帶 3. 應用: 登錄 保存瀏覽習慣 簡單的投票 4. 使用cookie的原因:因為HTTP是無狀態的,用cookie來保存狀態 5. 在django中 ...
  • 本文介紹的Java規則的說明分為3個主要級別,中級是平時開發用的比較多的級別,在今後將陸續寫出其他的規則。遵守了這些規則可以提高程式的效率、使代碼又更好的可讀性等。 一、在finally方法里關掉input或者output資源 方法體裡面定義了input或者output流的話,需要在finally里 ...
  • T1 足球聯賽 題目 【題目描述】 巴蜀中學新一季的足球聯賽開幕了。足球聯賽有n只球隊參賽,每賽季,每隻球隊要與其他球隊各賽兩場,主客各一場,贏一場得3分,輸一場不得分,平局兩隻隊伍各得一分。 英勇無畏的小鴻是機房的主力前鋒,她總能在關鍵時刻踢出一些匪夷所思的妙球。但是很可惜,她過早的燃燒完了她的職 ...
  • hhh 為年薪20萬加油ヾ(◍°∇°◍)ノ゙ 一、變數:(變數的命名規則:一般使用字母開頭,可以使用下劃線連接,以及數字) 正確的變數命名示範: (儘量使用容易理解什麼用途的詞語) a1 name_Li name2 錯誤的變數示例: 1a a=1 print(a) b='年薪百萬不是夢' print ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...