Spring學習筆記1——IOC: 儘量使用註解以及java代碼

来源:http://www.cnblogs.com/everSeeker/archive/2016/07/13/5667503.html
-Advertisement-
Play Games

在實戰中學習Spring,本系列的最終目的是完成一個實現用戶註冊登錄功能的項目。 預想的基本流程如下: 1、用戶網站註冊,填寫用戶名、密碼、email、手機號信息,後臺存入資料庫後返回ok。(學習IOC,mybatis,SpringMVC的基礎知識,表單數據驗證,文件上傳等) 2、伺服器非同步發送郵件 ...


在實戰中學習Spring,本系列的最終目的是完成一個實現用戶註冊登錄功能的項目。

預想的基本流程如下:

1、用戶網站註冊,填寫用戶名、密碼、email、手機號信息,後臺存入資料庫後返回ok。(學習IOC,mybatis,SpringMVC的基礎知識,表單數據驗證,文件上傳等)

2、伺服器非同步發送郵件給註冊用戶。(學習消息隊列)

3、用戶登錄。(學習緩存、Spring Security)

4、其他。

邊學習邊總結,不定時更新。項目環境為Intellij + Spring4。

 

一、準備工作。

1、mysql中建庫建表。

 

2、Intellij中創建maven webapp工程。

(1) pom.xml中導入需要的依賴包。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.everSeeker</groupId>
    <artifactId>register</artifactId>
    <packaging>war</packaging>
    <version>1.0</version>
    <name>register Maven Webapp</name>
    <url>http://maven.apache.org</url>

    <properties>
        <spring.version>4.3.1.RELEASE</spring.version>
    </properties>

    <dependencies>
        <!--spring core, context-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--test-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <!--<scope>test</scope>-->
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--springmvc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.2.4.Final</version>
        </dependency>
        <!--servlet-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!--mysql, mybatis-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>java_config_web</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
pom.xml

(2) 工程目錄結構如下所示:

 

二、Mybatis

1、配置mysql資料庫的基本信息。

# Database
db.mysql.driverClass = com.mysql.jdbc.Driver
db.mysql.jdbcUrl = jdbc:mysql://localhost:3306/register_notice?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
db.mysql.user = root
db.mysql.password = 333
db.minPoolSize = 10
db.maxPoolSize = 100
db.initialPoolSize = 20
db.maxIdleTime = 60
db.acquireIncrement = 5
db.maxStatements = 100
db.idleConnectionTestPeriod = 60
db.acquireRetryAttempts = 30
db.breakAfterAcquireFailure = true
db.testConnectionOnCheckout = false
db.properties

 

2、配置mybatis.xml以及spring-mybatis.xml。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <!--配置實體類的別名-->
    <typeAliases>
        <!--以下2種方法選其一即可。 第1種方法:使用typeAlias,為單個類設置別名。-->
        <!--<typeAlias type="com.everSeeker.entity.User" alias="User" />-->
        
        <!--第2種方法:使用package,為包下麵的所有類設置別名,預設規則為com.everSeeker.entity.User設置為User,去除前面的包名。-->
        <package name="com.everSeeker.entity" />
    </typeAliases>

</configuration>
mybatis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd">

       <!-- 在該文件中引入db.properties文件,可以保證之後的配置比如${db.mysql.driverClass}肯定能找到對應的值 -->
       <!-- 否則,如果直接在RootConfig.java中同事載入db.properties以及spring-mybatis.xml的話,不能保證db.properties先被引入,從而導致程式報錯 -->
       <context:property-placeholder location="classpath:db.properties"/>

       <!--數據源配置 c3p0
           常見的數據源實現類包有2個,一個是apache的DBCP(org.apache.commons.dbcp.BasicDataSource),另一個為C3P0。
       -->
       <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
             destroy-method="close">

              <property name="driverClass" value="${db.mysql.driverClass}" />
              <property name="jdbcUrl" value="${db.mysql.jdbcUrl}" />
              <property name="user" value="${db.mysql.user}" />
              <property name="password" value="${db.mysql.password}" />

              <!--連接池中保留的最小連接數。 -->
              <property name="minPoolSize" value="${db.minPoolSize}" />

              <!--連接池中保留的最大連接數。Default: 15 -->
              <property name="maxPoolSize" value="${db.maxPoolSize}" />

              <!--初始化時獲取的連接數,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
              <property name="initialPoolSize" value="${db.initialPoolSize}" />

              <!--最大空閑時間,60秒內未使用則連接被丟棄。若為0則永不丟棄。Default: 0 -->
              <property name="maxIdleTime" value="${db.maxIdleTime}" />

              <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 -->
              <property name="acquireIncrement" value="${db.acquireIncrement}" />

              <!--JDBC的標準參數,用以控制數據源內載入的PreparedStatements數量。但由於預緩存的statements 屬於單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。
                  如果maxStatements與maxStatementsPerConnection均為0,則緩存被關閉。Default: 0 -->
              <property name="maxStatements" value="${db.maxStatements}" />

              <!--每60秒檢查所有連接池中的空閑連接。Default: 0 -->
              <property name="idleConnectionTestPeriod" value="${db.idleConnectionTestPeriod}" />

              <!--定義在從資料庫獲取新連接失敗後重覆嘗試的次數。Default: 30 -->
              <property name="acquireRetryAttempts" value="${db.acquireRetryAttempts}" />

              <!--獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效 保留,併在下次調用getConnection()的時候繼續嘗試獲取連接。如果設為true,那麼在嘗試
                  獲取連接失敗後該數據源將申明已斷開並永久關閉。Default: false -->
              <property name="breakAfterAcquireFailure" value="${db.breakAfterAcquireFailure}" />

              <!--因性能消耗大請只在需要的時候使用它。如果設為true那麼在每個connection提交的 時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable
                  等方法來提升連接測試的性能。Default: false -->
              <property name="testConnectionOnCheckout" value="${db.testConnectionOnCheckout}" />
       </bean>

       <!-- myBatis配置.
            classpath和classpath*的區別,參考文檔:http://blog.csdn.net/zl3450341/article/details/9306983.
            classpath只會返回第一個匹配的資源,建議確定路徑的單個文檔使用classpath;匹配多個文檔時使用classpath*.
       -->
       <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
             p:dataSource-ref="dataSource"
             p:configLocation="classpath:mybatis.xml"
             p:mapperLocations="classpath*:mapper/*Mapper.xml" />

       <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
              <!--basePackage指定要掃描的包,在此包之下的映射器都會被搜索到。可指定多個包,包與包之間用逗號或分號分隔
                  MapperScannerConfigurer將掃描basePackage所指定包下的所有介面類(包括子包),如果他們在SQL映射文件
                  中定義過,則將他們動態定義為一個Spring Bean. -->
              <property name="basePackage" value="com.everSeeker.dao" />
              <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
       </bean>

       <!-- 事務管理器配置, 使用jdbc事務 -->
       <bean id="transactionManager"
             class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource" ref="dataSource" />
       </bean>

       <!-- 使用annotation定義事務,對標註了@Transactional註解的bean進行處理,以織入事務管理切麵.
           預設情況下,自動使用名稱為transactionManager的事務管理器。
           proxy-target-class為true,表示spring將通過創建子類來代理業務類,需要在類路徑中添加CGLib.jar類庫。-->
       <tx:annotation-driven transaction-manager="transactionManager"
                             proxy-target-class="true" />
</beans>
spring-mybatis.xml

 

3、創建User類以及UserDao介面。

public class User {
    @Size(min = 32, max = 32, message = "uuid應該為32位字元串")
    private String id;

    @Size(min = 1, max = 32, message = "賬號長度應該在1-32位之間")
    private String username;

    @NotEmpty(message = "密碼不能為空")
    private String password;

    @NotEmpty(message = "email不能為空")
    @Email(message = "email格式不正確")
    private String email;

    @Size(min = 11, max = 11, message = "手機號長度為11位")
    private String cellphone;

    private long regDate;

    public User() {
        this.id = UUID.randomUUID().toString().replaceAll("-", "");
        this.regDate = 0;
    }

    public User(String username, String password, String email, String cellphone) {
        this(username, password, email, cellphone, new Date().getTime());
    }

    public User(String username, String password, String email, String cellphone, long regDate) {
        this.id = UUID.randomUUID().toString().replaceAll("-", "");
        this.username = username;
        this.password = password;
        this.email = email;
        this.cellphone = cellphone;
        this.regDate = regDate;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getCellphone() {
        return cellphone;
    }

    public void setCellphone(String cellphone) {
        this.cellphone = cellphone;
    }

    public long getRegDate() {
        return regDate;
    }

    public void setRegDate(long regDate) {
        this.regDate = regDate;
    }

    @Override
    public String toString() {
        return "[User: id=" + id + ", username=" + username + ", password=" + password + ", email=" + email + ", cellphone=" +
                cellphone + ", regDate=" + regDate + "]";
    }
}
User.java

User.java中的@NotNull, @NotEmpty, @Size以及@Email等註釋暫時忽略,以後解釋。

@Repository
public interface UserDao {
    void addUser(User user);
    User getUserByUsername(String username);
}
UserDao.java

 

4、在src/main/resources/mapper目錄下創建UserMapper.xml映射文件,實現UserDao介面中的方法。註:*Mapper.xml文件必須放在src/main/resources目錄下,之前放在src/main/java/com/everSeeker/dao目錄下,產生了莫名奇妙的錯誤。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.everSeeker.dao.UserDao">
    <resultMap id="ResultMapUser" type="com.everSeeker.entity.User">
    </resultMap>

    <insert id="addUser" parameterType="User">
        INSERT INTO user(id, username, password, email, cellphone, regDate) VALUES(#{id}, #{username}, #{password}, #{email}, #{cellphone}, #{regDate})
    </insert>

    <select id="getUserByUsername" parameterType="String" resultMap="ResultMapUser">
        SELECT * FROM user WHERE username=#{username}
    </select>

</mapper>
UserMapper.xml

 

三、IOC

1、創建IOC容器,通過註解方式,RootConfig.java。

@Configuration
@ComponentScan(basePackages = {"com.everSeeker"}, excludeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, value = RootConfig.WebPackage.class)})
@ImportResource({"classpath:spring-mybatis.xml"})
public class RootConfig {
    public static class WebPackage extends RegexPatternTypeFilter {
        public WebPackage() {
            super(Pattern.compile("com\\.everSeeker\\.web"));
        }
    }
}

@Configuration: 表明這是一個配置類。

@ComponentScan: 啟用組建掃描,basePackages:需要掃描的基礎package。excludeFilters: 符合filter條件的不掃描。

@ImportResource: 引入xml文件。

@PropertySource: 引入properties文件。

 

2、由於創建的是webapp項目,並且採用了SpringMVC,那麼DispatcherServlet是核心。以前的Spring版本中,一般會配置在web.xml中。而在Spring4中,可以在Java代碼中來實現。WebAppInitializer.java。

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {  //繼承了AbstractAnnotationConfigDispatcherServletInitializer的類會自動配置DispatcherServlet和Spring應用上下文

    @Override
    protected String[] getServletMappings() {       //將DispatcherServlet映射到"/"
        return new String[] { "/" };
    }

    /**
     * RootConfig類用來配置ContextLoaderListener創建的應用上下文中的bean,
     * 比如@Repository, @Service等組件
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };
    }

    /**
     * DispatcherServlet載入應用上下文時,使用定義在WebConfig配置類中的bean,
     * 用來載入包含Web組件的bean,比如控制器,視圖解析器以及處理器映射,  @Controller, @RequestMapping等
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfig.class };
    }

    @Override
    protected void customizeRegistration(ServletRegistration.Dynamic registration) {
        //限制上傳文件的大小不超過2MB,整個請求不超過4M,所有上傳的文件都要寫到磁碟中
        registration.setMultipartConfig(new MultipartConfigElement("/tmp/uploads", 2097152, 4194304, 0));
    }
}

 

3、創建WebConfig.java。

@Configuration
@EnableWebMvc
@ComponentScan("com.everSeeker.web")
public class WebConfig extends WebMvcConfigurerAdapter {
    //配置jsp視圖解析器
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resourceViewResolver = new InternalResourceViewResolver();
        resourceViewResolver.setPrefix("/WEB-INF/views/");
        resourceViewResolver.setSuffix(".jsp");
        resourceViewResolver.setExposeContextBeansAsAttributes(true);
        return resourceViewResolver;
    }

    //配置multipart解析器, 上傳文件用
    @Bean
    public MultipartResolver multipartResolver() throws IOException {
        return new StandardServletMultipartResolver();
    }

    //配置靜態資源的處理
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

@Bean: 聲明這個方法會創建所需類型的實例,並註冊為Spring應用上下文中的bean。

 

1、參考文獻:Spring實戰(第4版)。

2、傳送門:基於純Java代碼的Spring容器和Web容器零配置的思考和實現


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

-Advertisement-
Play Games
更多相關文章
  • 個人網站地址:nee32.com 一、實體框架(EF)簡介 EF框架是一個數據持久層框架,它的全稱是ADO.NET Entity Framework,是微軟開發的基於ADO.NET的ORM(Object Relational Mapping,對象關係映射)框架,常見的數據持久層框架有還有Nhiber ...
  • 其實完成這個功能之前,我就在思考:是先把想法寫了來,和大伙討論討論後再實現,還是實現後再寫文論述自己的思維。忽然腦後傳來一個聲音說:你發文後會進入發呆階段。所以還是靜下心,讓我輕輕地把代碼擼完再說。最近這幾天,自己在大腦里演練過各種技術難點,解決方案,推敲了各種該解決的問題,覺的差不多了,才決定擼碼... ...
  • Jquery的代碼是這樣實現的:(參考某大神EdieLei的回覆 博客鏈接 http://www.cnblogs.com/edielei) 個人覺得這種方法比之each好,所以拿來記錄: 當然each的方法也可以: ///參考狼牙曼巴的博客 ...
  • ...
  • 代碼很簡單,不多做解釋,如果有疑問和建議請留言,回第一時間回覆 C#代碼first class Program { static void Main(string[] args) { MyCallback mc = new MyCallback(); mc.Callback(new Action(s ...
  • 重寫模板查找方式: Application_Start()註冊 _ViewStart.cshtml @{ Layout = "~/Views/Shared/_Layout.cshtml".Replace("~/Views/", MyRazorViewEngine.GetView(Request)); ...
  • 完美的.net真泛型真的完美嗎 碼C#多年,不求甚解覺得泛型就是傳說中那麼完美,性能也是超級好,不錯,在絕大部分場景下泛型表現簡直可以用完美來形容,不過隨著前一陣重做IOC時,才發現與自己預想中不一樣,覺得自己還是圖樣圖森破,太過拿衣服了 在前面一篇文章(一步一步造個IoC輪子(二),詳解泛型工廠) ...
  • cocos2dx版本為3.10 1.在使用spine的過程中,發現了一個比較嚴重的問題:每次創建SkeletonAnimation的時候都會很卡,即使是使用同一個骨骼數據skeletonData。 跟蹤代碼發現,在每次調用函數spine::SkeletonAnimation::createWithF ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...