java jax-rs_在Java EE 7和WildFly中使用Bean验证来验证JAX-RS资源数据

java jax-rs

我过去已经两次接触过这个主题。 首先,在我的文章《 在Java EE 6中将Bean验证与JAX-RS集成》中 ,描述了甚至在Java EE平台规范中尚未定义之前,如何在JBoss AS 7中将Bean验证与JAX-RS结合使用。 后来,在一篇为《 JAX Magazine 》撰写并随后发表在《 JAXenter 》上的文章中,使用了带有Glassfish 4服务器(第一台经过Java EE 7认证的服务器)的Java EE 7中定义的新标准方式。
现在,以前称为JBoss Application Server的WildFly 8终于达到了最终版本,并加入了Java EE 7认证的服务器俱乐部,现在该发表新文章了,重点介绍了这两个应用服务器GlassFish 4和WildFly之间的特殊性和差异。 8。

规格和API

Java EE 7是期待已久的Java EE 6的重大改进。随着Java EE的每个发行版,都添加了新功能并增强了现有规范。 Java EE 7以Java EE 6的成功为基础,并且继续致力于提高开发人员的生产力。

JAX-RS是RESTful Web服务的Java API,是Java EE领域中发展最快的API之一。 当然,这是由于基于REST的Web服务的大量采用以及使用这些服务的应用程序数量的增加。

这篇文章将介绍配置REST端点以支持JavaScript客户端并处理验证异常以将本地化错误消息发送到客户端的必要步骤,以及HTTP错误状态代码。

源代码

本文随附的源代码可在GitHub上找到 。

Bean验证简介

JavaBeans Validation( Bean验证 )是一种新的验证模型,可作为Java EE 6平台的一部分使用。 约束通过以JavaBeans组件(例如托管Bean)的字段,方法或类上的注释形式的约束来支持Bean验证模型。

javax.validation.constraints包中提供了几个内置约束。 Java EE 7教程包含具有所有这些约束的列表。

Bean验证中的约束通过Java注释表示:

public class Person {@NotNull@Size(min = 2, max = 50)private String name;// ...
}

Bean验证和RESTful Web服务

JAX-RS为提取请求值并将其绑定到Java字段,属性和参数(使用@HeaderParam@QueryParam等注释)提供了强大的支持。它还支持通过非注释参数(即,将请求实体主体绑定到Java对象中) ,未使用任何JAX-RS批注进行批注的参数)。 但是,在JAX-RS 2.0之前,必须以编程方式对资源类中的这些值进行任何其他验证。

最新版本的JAX-RS 2.0提供了一种解决方案,使验证批注可以与JAX-RS批注结合使用。
以下示例显示了如何使用@Pattern验证批注来验证路径参数:

@GET
@Path("{id}")
public Person getPerson(@PathParam("id")@Pattern(regexp = "[0-9]+", message = "The id must be a valid number")String id) {return persons.get(id);
}

除了验证单个字段外,您还可以使用@Valid批注验证整个实体。
例如,下面的方法接收一个Person对象并对其进行验证:

@POST
public Response validatePerson(@Valid Person person) {// ...
}

国际化

在前面的示例中,我们使用了默认或硬编码的错误消息,但这既是一种不好的做法,又一点也不灵活。 I18n是Bean验证规范的一部分,它使我们能够使用资源属性文件来指定自定义错误消息。 默认资源文件名称为ValidationMessages.properties并且必须包含属性/值对,例如:

person.id.notnull=The person id must not be null
person.id.pattern=The person id must be a valid number
person.name.size=The person name must be between {min} and {max} chars long

注意: {min}{max}是指与消息关联的约束的属性。

一旦定义,这些消息就可以注入到验证约束中,例如:

@POST
@Path("create")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response createPerson(@FormParam("id")@NotNull(message = "{person.id.notnull}")@Pattern(regexp = "[0-9]+", message = "{person.id.pattern}")String id,@FormParam("name")@Size(min = 2, max = 50, message = "{person.name.size}")String name) {Person person = new Person();person.setId(Integer.valueOf(id));person.setName(name);persons.put(id, person);return Response.status(Response.Status.CREATED).entity(person).build();
}

要提供其他语言的翻译,必须使用翻译后的消息创建一个新文件ValidationMessages_XX.properties ,其中XX是所提供语言的代码。

不幸的是,对于某些应用程序服务器,默认的Validator提供程序不基于特定的HTTP请求支持i18n。 他们不考虑Accept-Language HTTP标头,并且始终使用Locale.getDefault()提供的默认Locale 。 为了能够使用Accept-Language HTTP标头(映射到浏览器选项中配置的语言)来更改Locale ,您必须提供一个自定义实现。

自定义验证器提供程序

尽管WildFly 8正确使用Accept-Language HTTP标头来选择正确的资源包,但其他服务器(例如GlassFish 4)却不使用此标头。 因此,为了完整性和与GlassFish代码的比较(在同一个GitHub项目下提供 ),我还为WildFly实现了自定义的Validator提供程序。
如果要查看GlassFish示例,请访问JAXenter上的Bean验证与JAX-RS集成。

  1. 将RESTEasy依赖项添加到Maven
  2. WildFly使用RESTEasy ,即JAX-RS规范的JBoss实现。
    验证程序提供程序和Exception Mapper所需的RESTEasy依赖关系将在本文后面的内容中进行讨论。 让我们将其添加到Maven:

    <dependencyManagement><dependencies><dependency><groupId>org.jboss.resteasy</groupId><artifactId>resteasy-bom</artifactId><version>3.0.6.Final</version><scope>import</scope><type>pom</type></dependency></dependencies>
    </dependencyManagement><dependencies><dependency><groupId>org.jboss.resteasy</groupId><artifactId>resteasy-jaxrs</artifactId><scope>provided</scope></dependency><dependency><groupId>org.jboss.resteasy</groupId><artifactId>resteasy-validator-provider-11</artifactId><scope>provided</scope></dependency>
    </dependencies>

  3. 创建一个ThreadLocal来存储LocaleAccept-Language HTTP标头
  4. ThreadLocal变量与普通变量不同,每个访问线程的线程都有其自己的,独立初始化的变量副本。

    /*** {@link ThreadLocal} to store the Locale to be used in the message interpolator.*/
    public class LocaleThreadLocal {public static final ThreadLocal<Locale> THREAD_LOCAL = new ThreadLocal<Locale>();public static Locale get() {return (THREAD_LOCAL.get() == null) ? Locale.getDefault() : THREAD_LOCAL.get();}public static void set(Locale locale) {THREAD_LOCAL.set(locale);}public static void unset() {THREAD_LOCAL.remove();}
    }

  5. 创建一个请求过滤器以读取Accept-Language HTTP标头
  6. 请求过滤器负责读取客户端在Accept-Language HTTP标头中发送的第一语言并将Accept-Language Locale存储在我们的ThreadLocal

    /*** Checks whether the {@code Accept-Language} HTTP header exists and creates a {@link ThreadLocal} to store the* corresponding Locale.*/
    @Provider
    public class AcceptLanguageRequestFilter implements ContainerRequestFilter {@Contextprivate HttpHeaders headers;@Overridepublic void filter(ContainerRequestContext requestContext) throws IOException {if (!headers.getAcceptableLanguages().isEmpty()) {LocaleThreadLocal.set(headers.getAcceptableLanguages().get(0));}}
    }

  7. 创建自定义消息插值器以强制执行特定的Locale
  8. 接下来,创建一个自定义消息插值器,以通过绕过或覆盖默认的Locale策略来强制执行特定的Locale值:

    /*** Delegates to a MessageInterpolator implementation but enforces a given Locale.*/
    public class LocaleSpecificMessageInterpolator implements MessageInterpolator {private final MessageInterpolator defaultInterpolator;public LocaleSpecificMessageInterpolator(MessageInterpolator interpolator) {this.defaultInterpolator = interpolator;}@Overridepublic String interpolate(String message, Context context) {return defaultInterpolator.interpolate(message, context, LocaleThreadLocal.get());}@Overridepublic String interpolate(String message, Context context, Locale locale) {return defaultInterpolator.interpolate(message, context, locale);}
    }

  9. 配置验证器提供程序
  10. RESTEasy通过查找实现ContextResolver<GeneralValidator>的提供程序来获得Bean验证实现。
    要配置新的验证服务提供者以使用我们的自定义消息插值器,请添加以下内容:

    /*** Custom configuration of validation. This configuration can define custom:* <ul>* <li>MessageInterpolator - interpolates a given constraint violation message.</li>* <li>TraversableResolver - determines if a property can be accessed by the Bean Validation provider.</li>* <li>ConstraintValidatorFactory - instantiates a ConstraintValidator instance based off its class.* <li>ParameterNameProvider - provides names for method and constructor parameters.</li> ** </ul>*/
    @Provider
    public class ValidationConfigurationContextResolver implements ContextResolver<GeneralValidator> {/*** Get a context of type {@code GeneralValidator} that is applicable to the supplied type.** @param type the class of object for which a context is desired* @return a context for the supplied type or {@code null} if a context for the supplied type is not available from*         this provider.*/@Overridepublic GeneralValidator getContext(Class<?> type) {Configuration<?> config = Validation.byDefaultProvider().configure();BootstrapConfiguration bootstrapConfiguration = config.getBootstrapConfiguration();config.messageInterpolator(new LocaleSpecificMessageInterpolator(Validation.byDefaultProvider().configure().getDefaultMessageInterpolator()));return new GeneralValidatorImpl(config.buildValidatorFactory(),bootstrapConfiguration.isExecutableValidationEnabled(),bootstrapConfiguration.getDefaultValidatedExecutableTypes());}
    }

映射异常

默认情况下,当验证失败时,容器将引发异常,并将HTTP错误返回给客户端。

Bean验证规范定义了一个小的异常层次结构(它们都继承自ValidationException ),可以在验证引擎初始化期间或(在我们的情况下更重要)在输入/输出值验证期间抛出异常( ConstraintViolationException )。 如果抛出的异常是除ConstraintViolationException之外的ValidationException的子类,则此异常将映射到状态码为500(内部服务器错误)的HTTP响应。 另一方面,当抛出ConstraintViolationException时,将返回两个不同的状态代码:

  • 500内部服务器错误)
    如果在验证方法返回类型时引发了异常。
  • 400(错误请求)
    除此以外。

不幸的是,WildFly并没有抛出ConstraintViolationException异常以获取无效的输入值, ResteasyViolationException抛出了一个ResteasyViolationException ,该异常实现了ValidationException接口。
可以自定义此行为,以允许我们将错误消息添加到返回给客户端的响应中:

/*** {@link ExceptionMapper} for {@link ValidationException}.* <p>* Send a {@link ViolationReport} in {@link Response} in addition to HTTP 400/500 status code. Supported media types* are: {@code application/json} / {@code application/xml} (if appropriate provider is registered on server).* </p>** @see org.jboss.resteasy.api.validation.ResteasyViolationExceptionMapper The original WildFly class:*      {@code org.jboss.resteasy.api.validation.ResteasyViolationExceptionMapper}*/
@Provider
public class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {@Overridepublic Response toResponse(ValidationException exception) {if (exception instanceof ConstraintDefinitionException) {return buildResponse(unwrapException(exception), MediaType.TEXT_PLAIN, Status.INTERNAL_SERVER_ERROR);}if (exception instanceof ConstraintDeclarationException) {return buildResponse(unwrapException(exception), MediaType.TEXT_PLAIN, Status.INTERNAL_SERVER_ERROR);}if (exception instanceof GroupDefinitionException) {return buildResponse(unwrapException(exception), MediaType.TEXT_PLAIN, Status.INTERNAL_SERVER_ERROR);}if (exception instanceof ResteasyViolationException) {ResteasyViolationException resteasyViolationException = ResteasyViolationException.class.cast(exception);Exception e = resteasyViolationException.getException();if (e != null) {return buildResponse(unwrapException(e), MediaType.TEXT_PLAIN, Status.INTERNAL_SERVER_ERROR);} else if (resteasyViolationException.getReturnValueViolations().size() == 0) {return buildViolationReportResponse(resteasyViolationException, Status.BAD_REQUEST);} else {return buildViolationReportResponse(resteasyViolationException, Status.INTERNAL_SERVER_ERROR);}}return buildResponse(unwrapException(exception), MediaType.TEXT_PLAIN, Status.INTERNAL_SERVER_ERROR);}protected Response buildResponse(Object entity, String mediaType, Status status) {ResponseBuilder builder = Response.status(status).entity(entity);builder.type(MediaType.TEXT_PLAIN);builder.header(Validation.VALIDATION_HEADER, "true");return builder.build();}protected Response buildViolationReportResponse(ResteasyViolationException exception, Status status) {ResponseBuilder builder = Response.status(status);builder.header(Validation.VALIDATION_HEADER, "true");// Check standard media types.MediaType mediaType = getAcceptMediaType(exception.getAccept());if (mediaType != null) {builder.type(mediaType);builder.entity(new ViolationReport(exception));return builder.build();}// Default media type.builder.type(MediaType.TEXT_PLAIN);builder.entity(exception.toString());return builder.build();}protected String unwrapException(Throwable t) {StringBuffer sb = new StringBuffer();doUnwrapException(sb, t);return sb.toString();}private void doUnwrapException(StringBuffer sb, Throwable t) {if (t == null) {return;}sb.append(t.toString());if (t.getCause() != null && t != t.getCause()) {sb.append('[');doUnwrapException(sb, t.getCause());sb.append(']');}}private MediaType getAcceptMediaType(List<MediaType> accept) {Iterator<MediaType> it = accept.iterator();while (it.hasNext()) {MediaType mt = it.next();/** application/xml media type causes an exception:* org.jboss.resteasy.core.NoMessageBodyWriterFoundFailure: Could not find MessageBodyWriter for response* object of type: org.jboss.resteasy.api.validation.ViolationReport of media type: application/xml*//*if (MediaType.APPLICATION_XML_TYPE.getType().equals(mt.getType())&& MediaType.APPLICATION_XML_TYPE.getSubtype().equals(mt.getSubtype())) {return MediaType.APPLICATION_XML_TYPE;}*/if (MediaType.APPLICATION_JSON_TYPE.getType().equals(mt.getType())&& MediaType.APPLICATION_JSON_TYPE.getSubtype().equals(mt.getSubtype())) {return MediaType.APPLICATION_JSON_TYPE;}}return null;}
}

上面的示例是ExceptionMapper接口的实现,该接口映射ValidationException类型的异常。 验证失败时,Validator实现将引发此异常。 如果该异常是ResteasyViolationException的实例, ResteasyViolationException除了HTTP 400/500状态代码外,我们ResteasyViolationException在响应中发送ViolationReport 。 这样可以确保客户端收到格式化的响应,而不仅仅是从资源传播的异常。

产生的输出类似于以下内容(JSON格式):

{"exception": null,"fieldViolations": [],"propertyViolations": [],"classViolations": [],"parameterViolations": [{"constraintType": "PARAMETER","path": "getPerson.id","message": "The id must be a valid number","value": "test"}],"returnValueViolations": []
}

运行和测试

要运行本文使用的应用程序,请使用Maven构建项目,将其部署到WildFly 8应用程序服务器中,然后将浏览器指向http:// localhost:8080 / jaxrs-beanvalidation-javaee7 / 。

另外,您也可以运行在类中的测试PersonsIT其内置的Arquillian和JUnit的 。 Arquillian将自动启动嵌入式WildFly 8容器,因此请确保您没有在同一端口上运行其他服务器。

建议和改进

  1. 我们依靠应用程序服务器代码来实现自定义验证程序提供程序。 在GlassFish 4上,需要实现ContextResolver ContextResolver<ValidationConfig> ,而在WildFly 8上,我们需要实现ContextResolver<GeneralValidator> 。 为什么不在Java EE 7规范中定义一个ValidationConfigGeneralValidator必须实现的接口,而不是依赖于应用程序服务器特定的代码?
  2. 使WildFly 8 Embedded易于使用和通过Maven进行配置。 当前,要使Arquillian可以使用它,需要下载WildFly发行版(org.wildfly:wildfly-dist),将其解压缩到target文件夹中,并在Surefire / Failsafe Maven插件上配置系统属性:
    <systemPropertyVariables><java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager><jboss.home>${wildfly.home}</jboss.home><module.path>${wildfly.home}/modules</module.path>
    </systemPropertyVariables>

    而对于Glassfish,您只需要定义正确的依赖项(org.glassfish.main.extras:glassfish-embedded-all)。

  3. 使RESTEasy成为WildFly Embedded的可传递依赖项。 仅通过定义provided WildFly Embedded依赖项,在编译时就可以使用所有WildFly模块,这将是一个很好的生产力提升。
  4. 当前无法在Eclipse上使用选项Run As >> JUnit Test ,因为必须存在名为jbossHome的系统属性。 Eclipse不会从Surefire / Failsafe配置中读取此属性。 有没有解决方法?
  5. 当使用ExceptionMapper<ValidationException> RESTEasy默认实现时,以application/xml媒体类型请求数据并发生验证错误,将引发以下异常:
    org.jboss.resteasy.core.NoMessageBodyWriterFoundFailure:Could not find MessageBodyWriter for response object of type:org.jboss.resteasy.api.validation.ViolationReport of media type:application/xml

    这是RESTEasy错误吗?

翻译自: https://www.javacodegeeks.com/2014/04/validating-jax-rs-resource-data-with-bean-validation-in-java-ee-7-and-wildfly.html

java jax-rs

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

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

相关文章

计算机网络整体框架理解与把握(持续更新)

本文主要依据《计算机网络&#xff1a;自顶而下的算法》 网络可以分为5层。自顶而下分别是应用层、运输层、网络层、链路层、物理层。而大多数主要关注于前三层。 自顶而下设计&#xff0c;当然&#xff0c;最先关注的是应用层。应用层与普通用户接触最广泛。并且&#xff0c;网…

工业交换机都有哪些优势,该如何挑选

相对普通交换机而言&#xff0c;工业交换机在普通交换机的基础之上加强了功能&#xff0c;端口配置和产品系列更加丰富和灵活&#xff0c;能够满足各种工业领域的需求。广泛应用于智慧交通、安防监控、风电光伏、煤矿等领域。尤其是电力、交通、冶金被称为工业交换机应用的三大…

用Java中的抽象类扩展抽象类

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

对于计算机网络的整体框架的概括(转载)

作者&#xff1a; 阮一峰 日期&#xff1a; 2012年5月31日 我们每天使用互联网&#xff0c;你是否想过&#xff0c;它是如何实现的&#xff1f; 全世界几十亿台电脑&#xff0c;连接在一起&#xff0c;两两通信。上海的某一块网卡送出信号&#xff0c;洛杉矶的另一块网卡居然…

工业交换机与工业路由器的区别

工业交换机&#xff08;也叫工业以太网交换机&#xff09;&#xff0c;即应用于工业控制领域的以太网交换机设备&#xff0c;由于采用的网络标准&#xff0c;其开放性好、应用广泛以及价格低廉、使用的是透明而统一的TCP/IP协议&#xff0c;以太网已经成为工业控制领域的主要通…

Sublime介绍安装和使用(转载)

转载&#xff1a; 原文链接&#xff1a;https://blog.csdn.net/ITTechnologyHome/article/details/80486235 1.1 Sublime介绍 Sublime是一款跨平台、收费的文件编辑器&#xff0c;可以用来编写HTML,CSS,JavaScript,PHP等应用程序。 1.2 Sublime特点 跨平台,支持主流操作系统…

工业交换机在城市智慧轨道交通中的应用分析

俗话说&#xff0c;城市建设&#xff0c;交通先行&#xff0c;轨道交通是解决“城市病”的一把金钥匙&#xff0c;而智慧交通更是建设智慧城市的重要构成部分。从2009年开始&#xff0c;我国就逐渐进入城市轨道交通建设的高潮&#xff0c;并逐年扩大地铁市场。发展以轨道交通为…

apache hive_Hive:使用Apache Hive查询客户最喜欢的搜索查询和产品视图计数

apache hive这篇文章介绍了如何使用Apache Hive查询Hadoop下存储的搜索点击数据。 我们将以示例的形式生成有关总产品浏览量的客户最爱搜索查询和统计信息。 继续之前的文章 使用大数据分析客户产品搜索点击次数 &#xff0c; Flume&#xff1a;使用Apache Flume收集客户产品…

工业交换机在工业通信领域的应用分析

工业交换机是专门为满足灵活多变的工业应用需求而设计&#xff0c;提供一种高性价比工业以太网通讯解决方案。工业交换机&#xff0c;作为我们广为使用的局域网硬件设备&#xff0c;一直为大家所熟悉。它的普及程度其实是由于以太网的广泛使用&#xff0c;作为今天以太网的主流…

Sublime Text提示Unable to download XXX. Please view the console for more details安装插件失败解决

安装插件时&#xff0c;弹出提示框&#xff1a; Package Control Unable to download XXX. Please view the console for more details. 一番搜索后在github上找到答案 1.打开Sublime Text&#xff0c;Preferences ->Package Settings->Package Control->Settings-Use…

从响应式Spring Data存储库流式传输实时更新

这篇文章详细介绍了从数据库到对该数据感兴趣的任何其他组件进行流更新的幼稚实现。 更确切地说&#xff0c;如何更改Spring Data R2DBC存储库以向相关订阅者发出事件。 对R2DBC和Spring的一点背景知识将对这篇文章有所帮助。 我以前的著作“ 使用 Microsoft SQL Server的 Spr…

ns3入门案例1 first.cc

1、目录结构 example&#xff1a;1、根文件下自带示例结构&#xff0c;作为良好的参考资源 2、src环境下中各模块中example作为资源 build&#xff1a; 编译后文件以及可执行文件 src&#xff1a;各模块源代码 2、新代码运行 将新脚本放在scratch文件夹中&#xff0c;该目…

工业以太网交换机都有哪些作用

工业以太网交换机由于使用及定位的关系&#xff0c;区别于商业交换机&#xff0c;它更关注稳定性&#xff0c;耐高温&#xff0c;耐振动&#xff0c;耐腐蚀等一些工业特性。工业以太网交换机以其较高的防护等级&#xff08;一般IP40)、较强的电磁兼容性&#xff08;EMS 4级&…

ubuntu下面 将桌面换成 英文

1. 我们可以先将目录都改成英文的。 export LANGen_US 执行 xdg-user-dirs-gtk-update 这时会弹出一个配置界面&#xff0c;将所有中文的用户目录切换到英文。选中不再提示&#xff0c;确定。 这时&#xff0c;会删除没有内容的用户目录&#xff0c;但有内容的用户目录会保…

光纤交换机是什么,光纤交换机的作用是什么?

随着网络的发展&#xff0c;先后出现了交换机、路由器、电话光端机等网络设备&#xff0c;在数据存储网络中&#xff0c;光纤交换机起到了重要的作用。今天飞畅科技的小编来为大家介绍下光纤交换机及光纤交换机的作用&#xff0c;一起来看看吧&#xff01; 光纤交换机的简介 光…

ns3 入门案例2:third.cc

代码分析 1 头文件 #include "ns3/core-module.h" #include "ns3/point-to-point-module.h" #include "ns3/network-module.h" #include "ns3/applications-module.h" #include "ns3/mobility-module.h" #include "n…

光纤交换机产品功能介绍

光纤交换机是一种高速的网络传输中继设备&#xff0c;又叫做光纤通道交换机、SAN交换机&#xff0c;光纤传输的优点是速度快、抗干扰能力强。那么&#xff0c;光纤交换机有哪些功能呢&#xff1f;接下来我们就跟随飞畅科技的小编一起来看看吧&#xff01; 通道协议支持 SAN交换…

java8 streams_使用Java 8 Streams进行编程对算法性能的影响

java8 streams多年来&#xff0c;使用Java进行多范式编程已经成为可能&#xff0c;它支持面向服务&#xff0c;面向对象和面向方面的编程的混合。 带有lambda和java.util.stream.Stream类的Java 8是个好消息&#xff0c;因为它使我们可以将功能性编程范例添加到混合中。 确实&a…

光纤交换机光纤通道协议介绍

光纤通道&#xff08;FC&#xff0c;Fibre Channel&#xff09;协议是美国国家标准所指定的一种串行高速、低延时、低误码的标准协议能够为存储设备、IP 数据网、音频流等应用提供高速数据传输的骨干网络技术。随着光纤通道在不同领域的应用&#xff0c;在光纤通道协议的基础上…

解决E: 仓库 “http://ppa.launchpad.net/fcitx-team/nightly/ubuntu bionic Release” 没有 Release 文件。

转载声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明。 原文链接&#xff1a;在NS-3中安装可视化工具pyviz的一些问题的解决_寻同学的博客-CSDN博客 今天&#xff0c;在更新软件时&#xff0c;使用以下…