本篇文章為大家帶來了關於API的相關知識,其中主要介紹了設計API需要注意哪些地方?怎麼設計一個優雅的API接口,有興趣的朋友,下面一起來看吧,希望對大家有幫助。
在實際工作中,我們需要經常跟第三方平台打交道,可能會對接第三方平台API接口,或者提供API接口給第三方平台調用。
那麼問題來了,如果設計一個優雅的API接口,能夠滿足:安全性、可重複呼叫、穩定性、好定位問題等多面向需求?
今天跟大家一起聊聊設計API介面時,需要注意的一些地方,希望對你會有所幫助。
為了防止API介面中的資料被竄改,很多時候我們需要對API介面做簽章
。
介面請求方將請求參數
時間戳
密鑰
拼接成一個字串,然後透過md5
等hash演算法,產生一個前面sign。
然後在請求參數或請求頭中,增加sign參數,傳遞給API介面。
API介面的閘道服務,取得到該sign值,然後用相同的請求參數時間戳密鑰拼接成一個字串,用相同的m5演算法產生另外一個sign,對比兩個sign值是否相等。
如果兩個sign相等,則認為是有效請求,API介面的網關服務會將給請求轉送給對應的業務系統。
如果兩個sign不相等,則API介面的網關服務會直接傳回簽章錯誤。
問題來了:簽名中為什麼要加時間戳記?
答:為了安全性考慮,防止同一次請求被反覆利用,增加了密鑰沒破解的可能性,我們必須要對每次請求都設定一個合理的過期時間,例如:15分鐘。
這樣一次請求,在15分鐘內是有效的,超過15分鐘,API介面的網關服務會傳回超過有效期限的例外狀況提示。
目前產生簽章中的金鑰有兩種形式:
一種是雙方約定一個固定值privateKey。
另一種是API介面提供者給予AK/SK兩個值,雙方約定用SK作為簽章中的金鑰。 AK介面呼叫方作為header中的accessKey傳遞給API介面提供方,這樣API介面提供者可以根據AK取得到SK,而產生新的sgin。
有些時候,我們的API介面直接傳遞的非常重要的數據,例如:用戶的銀行卡號、轉帳金額、用戶身分證等,如果將這些參數,直接明文,暴露到公網上是非常危險的事。
由此,我們需要對資料進行加密
。
目前使用比較多的是用BASE64
加解密。
我們可以將所有的數據,安裝一定的規律拼接成一個大的字串,然後在加一個密鑰
,拼接到一起。
接著使用JDK1.8之後的Base64工具類別處理,效果如下:
【加密前的数据】www.baidu.com 【加密后的数据】d3d3LmJhaWR1LmNvbQ==复制代码
為了安全性,使用Base64可以加密多次。
API介面的呼叫方在傳遞參數時,body中只有一個參數data,它就是base64之後的加密資料。
API介面的閘道服務,在接收到data資料後,根據雙方事先預定的金鑰、加密演算法、加密次數等,進行解密,並且反序列化出參數資料。
為了進一步加強API介面的安全性,防止介面的簽章或加密被破解了,攻擊者可以在自己的伺服器上請求該介面。
需求限制請求ip
,增加ip白名單
。
只有在白名單中的ip位址,才能成功請求API接口,否則直接傳回無存取權限。
ip白名單也可以加在API網關服務上。
但也要防止公司的內部應用伺服器被攻破,這種情況也可以從內部伺服器發起API介面的請求。
這時候就需要增加web防火牆了,例如:ModSecurity等。
如果你的API介面被第三方平台呼叫了,這就意味著著,呼叫頻率是沒辦法控制的。
第三方平台呼叫你的API介面時,如果並發量一下子太高,可能會導致你的API服務不可用,介面直接掛掉。
由此,必須要對API介面做限流
。
限流方法有三種:
對請求ip做限流:例如同一個ip,在一分鐘內,對API介面總的請求次數
,不能超過10000次。
對請求介面做限流:例如同一個ip,在一分鐘內,對指定的API介面
,請求次數不能超過2000次。
對請求用戶做限流:例如同一個AK/SK用戶
,在一分鐘內,對API介面總的請求次數,不能超過10000次。
我们在实际工作中,可以通过nginx
,redis
或者gateway
实现限流的功能。
我们需要对API接口做参数校验
,比如:校验必填字段是否为空,校验字段类型,校验字段长度,校验枚举值等等。
这样做可以拦截一些无效的请求。
比如在新增数据时,字段长度超过了数据字段的最大长度,数据库会直接报错。
但这种异常的请求,我们完全可以在API接口的前期进行识别,没有必要走到数据库保存数据那一步,浪费系统资源。
有些金额字段,本来是正数,但如果用户传入了负数,万一接口没做校验,可能会导致一些没必要的损失。
还有些状态字段,如果不做校验,用户如果传入了系统中不存在的枚举值,就会导致保存的数据异常。
由此可见,做参数校验是非常有必要的。
在Java中校验数据使用最多的是hiberate
的Validator
框架,它里面包含了@Null、@NotEmpty、@Size、@Max、@Min等注解。
用它们校验数据非常方便。
当然有些日期字段和枚举字段,可能需要通过自定义注解的方式实现参数校验。
我之前调用过别人的API接口,正常返回数据是一种json格式,比如:
{ "code":0, "message":null, "data":[{"id":123,"name":"abc"}] },
签名错误返回的json格式:
{ "code":1001, "message":"签名错误", "data":null }
没有数据权限返回的json格式:
{ "rt":10, "errorMgt":"没有权限", "result":null }
这种是比较坑的做法,返回值中有多种不同格式的返回数据,这样会导致对接方很难理解。
出现这种情况,可能是API网关定义了一直返回值结构,业务系统定义了另外一种返回值结构。如果是网关异常,则返回网关定义的返回值结构,如果是业务系统异常,则返回业务系统的返回值结构。
但这样会导致API接口出现不同的异常时,返回不同的返回值结构,非常不利于接口的维护。
其实这个问题我们可以在设计API网关
时解决。
业务系统在出现异常时,抛出业务异常的RuntimeException,其中有个message字段定义异常信息。
所有的API接口都必须经过API网关,API网关捕获该业务异常,然后转换成统一的异常结构返回,这样能统一返回值结构。
我们的API接口需要对异常
进行统一处理。
不知道你有没有遇到过这种场景:有时候在API接口中,需要访问数据库,但表不存在,或者sql语句异常,就会直接把sql信息在API接口中直接返回。
返回值中包含了异常堆栈信息
、数据库信息
、错误代码和行数
等信息。
如果直接把这些内容暴露给第三方平台,是很危险的事情。
有些不法分子,利用接口返回值中的这些信息,有可能会进行sql注入或者直接脱库,而对我们系统造成一定的损失。
因此非常有必要对API接口中的异常做统一处理,把异常转换成这样:
{ "code":500, "message":"服务器内部错误", "data":null }
返回码code
是500
,返回信息message
是服务器内部异常
。
这样第三方平台就知道是API接口出现了内部问题,但不知道具体原因,他们可以找我们排查问题。
我们可以在内部的日志文件中,把堆栈信息、数据库信息、错误代码行数等信息,打印出来。
我们可以在gateway
中对异常进行拦截,做统一封装,然后给第三方平台的是处理后没有敏感信息的错误信息。
在第三方平台请求你的API接口时,接口的请求日志非常重要,通过它可以快速的分析和定位问题。
我们需要把API接口的请求url、请求参数、请求头、请求方式、响应数据和响应时间等,记录到日志文件中。
最好有traceId
,可以通过它串联整个请求的日志,过滤多余的日志。
当然有些时候,请求日志不光是你们公司开发人员需要查看,第三方平台的用户也需要能查看接口的请求日志。
这时就需要把日志落地到数据库,比如:mongodb
或者elastic search
,然后做一个UI页面,给第三方平台的用户开通查看权限。这样他们就能在外网查看请求日志了,他们自己也能定位一部分问题。
第三方平台極有可能在極短的時間內,請求我們介面多次,例如:在1秒內請求兩次。有可能是他們業務系統有bug,或是在做介面呼叫失敗重試,因此我們的API介面需要做冪等設計
。
也就是說要支援在極短的時間內,第三方平台用相同的參數請求API介面多次,第一次請求資料庫會新增數據,但第二次請求以後就不會新增數據,但也會回傳成功。
這樣做的目的是不會產生錯誤資料。
我們在日常工作中,可以透過在資料庫
中增加唯一索引
,或是在redis
儲存requestId
和請求參來保證接口冪等性。
對介面冪等性感興趣的小夥伴,可以看看我的另一篇文章《高並發下如何保證介面的冪等性? 》,裡面有非常詳細的介紹。
對於對我提供的批次接口,一定要限制請求的記錄條數
。
如果要求的資料太多,很容易造成API介面逾時
等問題,讓API介面變得不穩定。
通常情況下,建議一次請求中的參數,最多支援傳入500筆記錄。
如果使用者傳入多餘500筆記錄,則介面直接給予提示。
建議這個參數做成可設定的,並且事先要跟第三方平台協商好,避免上線後產生不必要的問題。
上線前我們務必對API介面做一下壓力測試
,知道各個介面的qps
情況。
以便於我們能夠更好的預估,需要部署多少伺服器節點,對於API介面的穩定性至關重要。
之前雖說對API介面做了限流,但是實際上API介面是否能夠達到限制的閥值,這是一個問號,如果不做壓力測試,是有很大風險的。
例如:你API介面限流1秒只允許50次請求,但實際API介面只能處理30次請求,這樣你的API介面也會處理不過來。
我們在工作中可以用jmeter
或apache benc
對API介面做壓力測試。
一般的API介面的邏輯都是同步處理的,請求完後立刻回傳結果。
但有時候,我們的API接口裡面的業務邏輯非常複雜,特別是有些批量接口,如果同步處理業務,耗時會非常長。
這種情況下,為了提升API介面的效能,我們可以改成非同步處理
。
在API介面中可以傳送一則mq訊息
,然後直接回傳成功。之後,有個專門的mq消費者
去非同步消費該訊息,做業務邏輯處理。
直接非同步處理的接口,第三方平台有兩種方式取得到。
第一種方式是:我們回呼
第三方平台的接口,告知他們API接口的處理結果,很多支付接口就是這麼玩的。
第二種方式是:第三方平台透過輪詢
呼叫我們另外一個查詢狀態的API接口,每隔一段時間查詢一次狀態,傳入的參數是之前的那個API介面中的id集合。
有時候第三方平台呼叫我們API介面時,取得的資料中有一部分是敏感數據,例如:使用者手機號碼、銀行卡號等等。
這樣資訊如果透過API介面直接保留到外網,是非常不安全的,很容易造成用戶隱私資料外洩的問題。
這就需要對部分資料做資料脫敏
了。
我們可以在傳回的資料中,部分內容用星號
取代。
已用戶手機號碼為例:182****887
。
這樣即使資料外洩了,也只洩漏了一部分,不法份子拿到這份資料也沒啥用。
說實話,一份完整的API介面文檔,在雙方做介面對接時,可以減少很多溝通成本,讓對方少走很多彎路。
介面文件中需要包含以下資訊:
介面位址
要求方式,例如:post或get
請求參數和欄位介紹
傳回值和欄位介紹
傳回碼與錯誤訊息
加密或簽章範例
完整的請求demo
額外的說明,例如:開通ip白名單。
介面文件中最好能夠統一介面和欄位名稱的命名風格,例如都用駝峰標識
命名。
統一欄位的類型和長度,例如:id欄位用Long類型,長度規定20。 status欄位用int型,長度固定2等。
統一時間格式字段,例如:time用String類型,格式為:yyyy-MM-dd HH:mm:ss。
介面文件中寫明AK/SK和域名,找某某單獨提供等。
推薦學習:《PHP影片教學》
以上是設計API介面時,要注意這些地方!的詳細內容。更多資訊請關注PHP中文網其他相關文章!