web api开启错误提示_当HTTP状态代码不足时:处理Web API错误报告

web api开启错误提示

RESTful Web API设计的一个领域(经常被忽视)是如何报告与业务或应用程序有关的错误和问题。 首先要想到HTTP状态代码的正确用法,尽管非常方便,但通常它的信息量还不够。 让我们以400错误请求为例。 是的,它清楚地表明请求是有问题的,但是究竟出了什么问题呢?

RESTful的体系结构风格并不决定在这种情况下应该做什么,因此每个人都在发明自己的风格,约定和规范。 它可能像将错误消息包含到响应中一样简单,也可能像复制/粘贴长堆栈跟踪记录一样是短视的(对于Java或.NET,仅举几例)。 并不缺乏想法,但是幸运的是,我们至少有一些以RFC 7807形式提供的指南:HTTP API的问题详细信息 。 尽管它不是官方规范而是草案(仍然),但它概述了当前问题的良好通用原则,这就是我们在本文中要讨论的内容。

简而言之, RFC 7807:HTTP API的问题详细信息仅提出了错误或问题表示形式( JSON或XML格式),其中可能至少包括以下详细信息:

  • type –标识问题类型的URI引用
  • 标题 –问题类型的简短易读摘要
  • status – HTTP状态代码
  • 详细信息 –针对此问题的发生的易于理解的解释
  • 实例 –标识问题具体发生的URI引用

更重要的是,问题类型定义可以使用其他成员扩展问题详细信息对象,从而为上述成员做出贡献。 如您所见,从实现角度看,它看起来非常简单。 更好的是,感谢
Zalando ,我们已经有了
RFC 7807:HTTP API实现的问题详细信息 对于Java (和 特别是Spring Web )。 所以……让我们尝试一下!

我们将使用最先进的技术堆栈, Spring Boot和Apache CXF ,流行的Web服务框架和JAX-RS 2.1实现来构建我们虚构的People Management Web API。 为了简单起见,仅公开了两个端点:注册和按人员标识符查找。

Web API错误

撇开开发现实世界服务时可能遇到的大量问题和业务约束,即使使用此简单的API,也可能会出错。 我们要解决的第一个问题是,如果您要寻找的人尚未注册怎么办? 看起来很适合404 Not Found ,对吗? 确实,让我们从第一个问题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异常非常相似,并且确实是一个,因为AbstractThrowableProblemRuntimeException的子类。 因此,我们可以从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()));
}

如果我们运行服务器并尝试获取提供任何标识符的人员,则将返回问题详细信息响应(因为未预先填充数据集),例如:

$ 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"
}

请注意,响应中包含了application / problem + json媒体类型的用法以及其他属性ID 。 尽管有许多事情可以改进,但是可以说它比仅裸露的404 (或由EntityNotFoundException导致的500 )要好。 另外,如果需要进一步的说明,可以参考此类问题的文档部分(在我们的情况下为http:// localhost:21020 / problems / person-not-found )。

因此,在例外之后设计问题只是一种选择。 您可能经常(出于非常正当的理由)会限制将业务逻辑与无关的细节耦合在一起。 在这种情况下,从JAX-RS资源返回问题详细信息作为响应有效负载是完全有效的。 例如,注册过程可能会引发NonUniqueEmailException,因此我们的Web API层可以将其转换为适当的问题详细信息。

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

要触发此问题,只需运行服务器实例并尝试两次注册同一个人即可,就像下面所做的那样。

$ 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"                                                              
}

太好了,所以我们的最后一个例子有些复杂,但同时可能是最现实的一个例子。 我们的Web API在很大程度上依赖Bean验证 ,以确保API使用者提供的输入有效。 我们如何将验证错误表示为问题的详细信息? 最直接的方法是提供专用的ExceptionMapper提供程序,它是JAX-RS规范的一部分。 让我们介绍一个。

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

上面的代码片段区分了两种问题: ConstraintViolationException指示无效输入并映射到400 Bad Request ,而泛型ValidationException指示服务器端问题并映射到500 Internal Server Error 。 我们仅提取有关违规的基本详细信息,但是即使这样做,也可以大大改进错误报告功能。

$ 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成员中的附加信息非常冗长:我们分别知道类( PeopleResource ),方法( register ),方法的参数( 有效负载 )和属性( lastNameemail )(所有这些都从属性路径)。

有意义的错误报告是现代RESTful Web API的基础之一。 通常这并不容易,但绝对值得付出努力。 消费者(通常只是其他开发人员)应该对哪里出了问题以及如何处理有一个清晰的了解。 RFC 7807:HTTP API的问题详细信息是朝正确方向迈出的一步, 问题和问题弹簧网络之类的库在这里为您提供支持,请充分利用它们。

完整的源代码可在Github上找到 。

翻译自: https://www.javacodegeeks.com/2019/05/http-status-code-enough-tackling-web-apis-error-reporting.html

web api开启错误提示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/332316.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

会议室分配时间最长_论文导论动态任务分配GPU上图计算的高效处理方式

编者按GPU作为一种硬件&#xff0c;相比较于CPU来说&#xff0c;有更多的并行度和更高的带宽&#xff0c;在图像处理领域取得了非常好的应用效果。越来越多的研究也正试图将GPU也应用到图计算领域中&#xff0c;然而GPU适合进行规则运算&#xff0c;但是图是一种不规则的数据表…

JS(Javascript)校验表单项的内容是否合规

<script !src"">/*** 分析&#xff1a;* 1.给表单绑定onsubmit事件* 监听器中判断每个方法检验的结果&#xff0c;如果都是true&#xff0c;则监听器方法返回true&#xff1b;如果有一个为false,* 则监听器方法返回false* 2.定义一些方法分别校验各个表单项* 3…

java \t怎么从头开始_如何从头开始以正确的面向对象方式创建Java Web Framework

java \t怎么从头开始您如何用Java设计Web应用程序&#xff1f; 您安装了Spring&#xff0c;阅读了手册&#xff0c;创建了控制器 &#xff0c;创建了一些视图&#xff0c;添加了一些注释 &#xff0c;它就可以工作了。 如果没有Spring &#xff08;Ruby中没有Ruby on Rails&…

kl散度的理解_以曲率的视角理解自然梯度优化

一个故事我要讲一个故事:一个你几乎肯定听过的故事&#xff0c;但它的侧重点与你习以为常关注的不同。所有现代深度学习模型都使用梯度下降进行训练。 在梯度下降的每个步骤中&#xff0c;您的参数值从某个起始点开始&#xff0c;然后将它们移动到最大损失减少的方向。 你可以通…

JSP的学习笔记

文章目录概念原理JSP 脚本JSP 内置对象概念 Java Server Pages &#xff1a;Java 服务器端页面 可以理解为&#xff1a;一个特殊的页面&#xff0c;其中既可以指定定义 html 标签&#xff0c;又可以定义 Java 代码 用于简化 Servlet 中的输出页面数据的代码的书写!!! 原理 J…

程序员面试算法_程序员的前20个搜索和排序算法面试问题

程序员面试算法大家好&#xff0c;如果您正在准备编程工作面试或正在寻找新工作&#xff0c;那么您知道这不是一个容易的过程。 在您职业的任何阶段&#xff0c;您都必须幸运地接到电话并进行第一轮面试&#xff0c;但是在初学者方面&#xff0c;当您寻找第一份工作时就更加困难…

御用导航提示页面_UI网站导航设计知识与五个知识案例

导航设计是网站的基石。它确定了至关重要的指标&#xff0c;例如转化率和跳出率&#xff0c;页面停留时间&#xff0c;点击量&#xff0c;并且通常是决定您的访问者拥有良好体验并以比您所说的“用户流量”更快退出网站的决定性因素。但是&#xff0c;好的设计是什么样的呢?一…

Servlet规范中定义的过滤器

文章目录什么是过滤器写过滤器的步骤配置初始化参数过滤器的优先级过滤器的优点过滤器执行过程图解过滤敏感词的示例代码使用 Servlet 来过滤敏感词使用过滤器来过滤敏感词通过配置初始化参数来设置敏感词关于 web.xml 的配置什么是过滤器 Servlet 规范当中定义的一种特殊的类…

Spring Boot错误–创建在类路径资源DataSourceAutoConfiguration中定义的名称为“ dataSource”的bean时出错...

大家好&#xff0c;如果您使用的是Spring Boot&#xff0c;并且遇到诸如“无法为数据库类型NONE确定嵌入式数据库驱动程序类”或“在类路径资源ataSourceAutoConfiguration中定义的名称为dataSource的bean创建错误”之类的错误&#xff0c;那么您来对地方了地点。 在本文中&…

simulink怎么生成vxworks的执行程序_从EPB模型谈谈Simulink代码生成

前段时间有读者朋友问代码生成的例子&#xff0c;说他正在做EPB的仿真模型&#xff0c;但总是和应用串不到一块去&#xff0c;我这里就从一个简单EPB控制模型来谈谈simulink代码生成&#xff0c;总结代码生成方法的同时也顺便回答他的问题。1.EPB模型准备用来举例说明的模型很简…

通过Socket实现文件上传/上传文件

文章目录整个功能的实现步骤客户端的实现步骤服务端的实现步骤示例代码整个功能的实现步骤 1.客户端使用本地的字节输入流读取被上传的文件数据 2.客户端使用网络字节输出流&#xff0c;将读取到的文件数据发送给服务端 3.服务端使用网络字节输入流读取客户端发送过来的文件数…

构造函数调用默认构造函数_显式无参数构造函数与默认构造函数

构造函数调用默认构造函数大多数不熟悉Java的开发人员都会Swift了解到&#xff0c;如果他们没有指定至少一个显式构造函数&#xff0c;则会为Java类隐式创建一个“ 默认构造函数 ”&#xff08; 由javac进行创建&#xff09;。 Java语言规范的 8.8.9节简要指出&#xff1a;“如…

Artifact到底是什么

将项目构建成一个可以在服务器部署的文件包&#xff08;如&#xff1a;war 包或者 exploded 的文件包&#xff09;&#xff0c;或者构建成的 jar 包&#xff08;这些 jar 包通常是某些模块程序文件&#xff0c;也可以是某些插件程序文件&#xff09;&#xff0c;这些都叫 Artif…

python win10 桌面_Python3如何实现Win10桌面自动切换

得空写了个自动切换桌面背景图片的小程序。再不写python就要扔键盘了&#xff0c;对vue还有那么一点好感&#xff0c;天天php真是有够烦。准备工作准备个文件夹放在桌面上&#xff0c;平时看到什么高清好图就拽进去。运行脚本脚本如下&#xff1a;#!/usr/bin/pythonimport ctyp…

java中抽象类继承抽象类_用Java中的抽象类扩展抽象类

java中抽象类继承抽象类示例问题 当我创建Java :: Geci抽象类AbstractFieldsGenerator和AbstractFilteredFieldsGenerator我遇到了一个不太复杂的设计问题。 我想强调一下&#xff0c;这个问题和设计对于某些人来说似乎很明显&#xff0c;但是在我最近与一位初级开发人员&#…

pb90代码如何连接sql2008r2_如何使用 HTTP Headers 来保护你的 Web 应用

众所周知&#xff0c;无论是简单的小网页还是复杂的单页应用&#xff0c;Web 应用都是网络攻击的目标。2016 年&#xff0c;这种最主要的攻击模式 —— 攻击 web 应用&#xff0c;造成了大约 40% 的数据泄露。事实上&#xff0c;现在来说&#xff0c;了解网络安全并不是锦上添花…

Servlet配置初始化参数/配置参数

文章目录给某个Servlet配置初始化参数配置全局的参数ServletConfig给某个Servlet配置初始化参数 也可以通过注解的方式来配置初始化参数&#xff1a; 在 Servlet 中获取针对 Servlet 的参数值&#xff1a; String version getInitParameter("version");配置全局的…

poi动态创建文档_POI创建的文档具有不同条件的灵活样式

poi动态创建文档介绍 这篇文章解释了基于各种标准将样式应用于文档的困难并提供了解决方案。 Java编程中的常见任务之一是根据存储在数据库中的数据创建Excel报告。 在这些情况下&#xff0c;Java程序员使用POI :-)。 这些报告通常对样式和数据格式有严格的规定。 数据通常是SQ…

python编码程序_python 编码

网页的编码方式是简体中文gb2312 (查看网页源码&#xff0c;可以看到)&#xff0c;而python内部的编码方式为unicode&#xff0c;之前的代码是这样的&#xff1a;contentAll urllib.urlopen(urlLink).read()soup BeautifulSoup.BeautifulSoup(contentAll) #生成BeautifulSou…

比特(bit)_二进制数

二进制数位&#xff0c;也叫比特位&#xff0c;其实就是指位置&#xff0c;是指二进制数的位置。例如&#xff0c;0101 这是二进制数&#xff0c;而且是 4 位的二进制数&#xff0c;因为有 4 个位置&#xff0c;每个位置放一个二进制数&#xff0c;确切的说是每个位置放一个二进…