web api开启错误提示_当HTTP状态代码不足时:处理Web API错误报告
web api開啟錯(cuò)誤提示
RESTful Web API設(shè)計(jì)的一個(gè)領(lǐng)域(經(jīng)常被忽視)是如何報(bào)告與業(yè)務(wù)或應(yīng)用程序有關(guān)的錯(cuò)誤和問題。 首先要想到HTTP狀態(tài)代碼的正確用法,盡管非常方便,但通常它的信息量還不夠。 讓我們以400錯(cuò)誤請(qǐng)求為例。 是的,它清楚地表明請(qǐng)求是有問題的,但是究竟出了什么問題呢?
RESTful的體系結(jié)構(gòu)風(fēng)格并不決定在這種情況下應(yīng)該做什么,因此每個(gè)人都在發(fā)明自己的風(fēng)格,約定和規(guī)范。 它可能像將錯(cuò)誤消息包含到響應(yīng)中一樣簡(jiǎn)單,也可能像復(fù)制/粘貼長(zhǎng)堆棧跟蹤記錄一樣是短視的(對(duì)于Java或.NET,僅舉幾例)。 并不缺乏想法,但是幸運(yùn)的是,我們至少有一些以RFC 7807形式提供的指南:HTTP API的問題詳細(xì)信息 。 盡管它不是官方規(guī)范而是草案(仍然),但它概述了當(dāng)前問題的良好通用原則,這就是我們?cè)诒疚闹幸懻摰膬?nèi)容。
簡(jiǎn)而言之, RFC 7807:HTTP API的問題詳細(xì)信息僅提出了錯(cuò)誤或問題表示形式( JSON或XML格式),其中可能至少包括以下詳細(xì)信息:
- type –標(biāo)識(shí)問題類型的URI引用
- 標(biāo)題 –問題類型的簡(jiǎn)短易讀摘要
- status – HTTP狀態(tài)代碼
- 詳細(xì)信息 –針對(duì)此問題的發(fā)生的易于理解的解釋
- 實(shí)例 –標(biāo)識(shí)問題具體發(fā)生的URI引用
更重要的是,問題類型定義可以使用其他成員擴(kuò)展問題詳細(xì)信息對(duì)象,從而為上述成員做出貢獻(xiàn)。 如您所見,從實(shí)現(xiàn)角度看,它看起來非常簡(jiǎn)單。 更好的是,感謝
Zalando ,我們已經(jīng)有了
RFC 7807:HTTP API實(shí)現(xiàn)的問題詳細(xì)信息 對(duì)于Java (和 特別是Spring Web )。 所以……讓我們嘗試一下!
我們將使用最先進(jìn)的技術(shù)堆棧, Spring Boot和Apache CXF ,流行的Web服務(wù)框架和JAX-RS 2.1實(shí)現(xiàn)來構(gòu)建我們虛構(gòu)的People Management Web API。 為了簡(jiǎn)單起見,僅公開了兩個(gè)端點(diǎn):注冊(cè)和按人員標(biāo)識(shí)符查找。
撇開開發(fā)現(xiàn)實(shí)世界服務(wù)時(shí)可能遇到的大量問題和業(yè)務(wù)約束,即使使用此簡(jiǎn)單的API,也可能會(huì)出錯(cuò)。 我們要解決的第一個(gè)問題是,如果您要尋找的人尚未注冊(cè)怎么辦? 看起來很適合404 Not Found ,對(duì)嗎? 確實(shí),讓我們從第一個(gè)問題PersonNotFoundProblem開始 !
public class PersonNotFoundProblem extends AbstractThrowableProblem {private static final long serialVersionUID = 7662154827584418806L;private static final URI TYPE = URI.create("http://localhost:21020/problems/person-not-found");public PersonNotFoundProblem(final String id, final URI instance) {super(TYPE, "Person is not found", Status.NOT_FOUND, "Person with identifier '" + id + "' is not found", instance, null, Map.of("id", id));} }它與典型的Java異常非常相似,并且確實(shí)是一個(gè),因?yàn)锳bstractThrowableProblem是RuntimeException的子類。 因此,我們可以從JAX-RS API中拋出它。
@Produces({ MediaType.APPLICATION_JSON, "application/problem+json" }) @GET @Path("{id}") public Person findById(@PathParam("id") String id) {return service.findById(id).orElseThrow(() -> new PersonNotFoundProblem(id, uriInfo.getRequestUri())); }如果我們運(yùn)行服務(wù)器并嘗試獲取提供任何標(biāo)識(shí)符的人員,則將返回問題詳細(xì)信息響應(yīng)(因?yàn)槲搭A(yù)先填充數(shù)據(jù)集),例如:
$ curl "http://localhost:21020/api/people/1" -H "Accept: */*" HTTP/1.1 404 Content-Type: application/problem+json{"type" : "http://localhost:21020/problems/person-not-found","title" : "Person is not found","status" : 404,"detail" : "Person with identifier '1' is not found","instance" : "http://localhost:21020/api/people/1","id" : "1" }請(qǐng)注意,響應(yīng)中包含了application / problem + json媒體類型的用法以及其他屬性ID 。 盡管有許多事情可以改進(jìn),但是可以說它比僅裸露的404 (或由EntityNotFoundException導(dǎo)致的500 )要好。 另外,如果需要進(jìn)一步的說明,可以參考此類問題的文檔部分(在我們的情況下為http:// localhost:21020 / problems / person-not-found )。
因此,在例外之后設(shè)計(jì)問題只是一種選擇。 您可能經(jīng)常(出于非常正當(dāng)?shù)睦碛?#xff09;會(huì)限制將業(yè)務(wù)邏輯與無關(guān)的細(xì)節(jié)耦合在一起。 在這種情況下,從JAX-RS資源返回問題詳細(xì)信息作為響應(yīng)有效負(fù)載是完全有效的。 例如,注冊(cè)過程可能會(huì)引發(fā)NonUniqueEmailException,因此我們的Web API層可以將其轉(zhuǎn)換為適當(dāng)?shù)膯栴}詳細(xì)信息。
@Consumes(MediaType.APPLICATION_JSON) @Produces({ MediaType.APPLICATION_JSON, "application/problem+json" }) @POST public Response register(@Valid final CreatePerson payload) {try {final Person person = service.register(payload.getEmail(), payload.getFirstName(), payload.getLastName());return Response.created(uriInfo.getRequestUriBuilder().path(person.getId()).build()).entity(person).build();} catch (final NonUniqueEmailException ex) {return Response.status(Response.Status.BAD_REQUEST).type("application/problem+json").entity(Problem.builder().withType(URI.create("http://localhost:21020/problems/non-unique-email")).withInstance(uriInfo.getRequestUri()).withStatus(Status.BAD_REQUEST).withTitle("The email address is not unique").withDetail(ex.getMessage()).with("email", payload.getEmail()).build()).build();}}要觸發(fā)此問題,只需運(yùn)行服務(wù)器實(shí)例并嘗試兩次注冊(cè)同一個(gè)人即可,就像下面所做的那樣。
$ curl -X POST "http://localhost:21020/api/people" \ -H "Accept: */*" -H "Content-Type: application/json" \-d '{"email":"john@smith.com", "firstName":"John", "lastName": "Smith"}'HTTP/1.1 400 Content-Type: application/problem+json { "type" : "http://localhost:21020/problems/non-unique-email", "title" : "The email address is not unique", "status" : 400, "detail" : "The email 'john@smith.com' is not unique and is already registered", "instance" : "http://localhost:21020/api/people", "email" : "john@smith.com" }太好了,所以我們的最后一個(gè)例子有些復(fù)雜,但同時(shí)可能是最現(xiàn)實(shí)的一個(gè)例子。 我們的Web API在很大程度上依賴Bean驗(yàn)證 ,以確保API使用者提供的輸入有效。 我們?nèi)绾螌Ⅱ?yàn)證錯(cuò)誤表示為問題的詳細(xì)信息? 最直接的方法是提供專用的ExceptionMapper提供程序,它是JAX-RS規(guī)范的一部分。 讓我們介紹一個(gè)。
@Provider public class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {@Context private UriInfo uriInfo;@Overridepublic Response toResponse(final ValidationException ex) {if (ex instanceof ConstraintViolationException) {final ConstraintViolationException constraint = (ConstraintViolationException) ex;final ThrowableProblem problem = Problem.builder().withType(URI.create("http://localhost:21020/problems/invalid-parameters")).withTitle("One or more request parameters are not valid").withStatus(Status.BAD_REQUEST).withInstance(uriInfo.getRequestUri()).with("invalid-parameters", constraint.getConstraintViolations().stream().map(this::buildViolation).collect(Collectors.toList())).build();return Response.status(Response.Status.BAD_REQUEST).type("application/problem+json").entity(problem).build();}return Response.status(Response.Status.INTERNAL_SERVER_ERROR).type("application/problem+json").entity(Problem.builder().withTitle("The server is not able to process the request").withType(URI.create("http://localhost:21020/problems/server-error")).withInstance(uriInfo.getRequestUri()).withStatus(Status.INTERNAL_SERVER_ERROR).withDetail(ex.getMessage()).build()).build();}protected Map<?, ?> buildViolation(ConstraintViolation<?> violation) {return Map.of("bean", violation.getRootBeanClass().getName(),"property", violation.getPropertyPath().toString(),"reason", violation.getMessage(),"value", Objects.requireNonNullElse(violation.getInvalidValue(), "null"));} }上面的代碼片段區(qū)分了兩種問題: ConstraintViolationException指示無效輸入并映射到400 Bad Request ,而泛型ValidationException指示服務(wù)器端問題并映射到500 Internal Server Error 。 我們僅提取有關(guān)違規(guī)的基本詳細(xì)信息,但是即使這樣做,也可以大大改進(jìn)錯(cuò)誤報(bào)告功能。
$ curl -X POST "http://localhost:21020/api/people" \-H "Accept: */*" -H "Content-Type: application/json" \-d '{"email":"john.smith", "firstName":"John"}' -i HTTP/1.1 400 Content-Type: application/problem+json { "type" : "http://localhost:21020/problems/invalid-parameters", "title" : "One or more request parameters are not valid", "status" : 400, "instance" : "http://localhost:21020/api/people", "invalid-parameters" : [ {"reason" : "must not be blank", "value" : "null", "bean" : "com.example.problem.resource.PeopleResource", "property" : "register.payload.lastName" }, { "reason" : "must be a well-formed email address", "value" : "john.smith", "bean" : "com.example.problem.resource.PeopleResource", "property" : "register.payload.email" } ] }這次捆綁到invalid-parameters成員中的附加信息非常冗長(zhǎng):我們分別知道類( PeopleResource ),方法( register ),方法的參數(shù)( 有效負(fù)載 )和屬性( lastName和email )(所有這些都從屬性路徑)。
有意義的錯(cuò)誤報(bào)告是現(xiàn)代RESTful Web API的基礎(chǔ)之一。 通常這并不容易,但絕對(duì)值得付出努力。 消費(fèi)者(通常只是其他開發(fā)人員)應(yīng)該對(duì)哪里出了問題以及如何處理有一個(gè)清晰的了解。 RFC 7807:HTTP API的問題詳細(xì)信息是朝正確方向邁出的一步, 問題和問題彈簧網(wǎng)絡(luò)之類的庫(kù)在這里為您提供支持,請(qǐng)充分利用它們。
完整的源代碼可在Github上找到 。
翻譯自: https://www.javacodegeeks.com/2019/05/http-status-code-enough-tackling-web-apis-error-reporting.html
web api開啟錯(cuò)誤提示
總結(jié)
以上是生活随笔為你收集整理的web api开启错误提示_当HTTP状态代码不足时:处理Web API错误报告的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 靖江备案价(南靖备案价)
- 下一篇: 网络犯罪案例分析题及答案(网络犯罪案例d