本文介绍了cxf-codegen-plugin不符合cxf ValidationFeature的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据,以便进行验证操作输入和输出绑定必须使用 @Valid

According to ValidationFeature documentation in order for the validation to happen the operations input and output bindings must be annotated with @Valid

但是,cxf-codegen-plugin生成的web服务接口没有这些注释,而且我似乎找不到命令行参数或允许添加它们的插件。

However, the webservice interface generated by cxf-codegen-plugin does not have these annotations, and I don't seem to find a command line argument or a plugin that allows to add them.

@在不违反Liskov替换原则的情况下,不能在Webservice接口的实现中放入有效的批注:在这种情况下,JSR-349(休眠验证器)的参考实现会产生 HV000151:一种覆盖另一种方法的方法不得更改参数约束配置

The @Valid annotations cannot be put in the implementation of the webservice interface without violating Liskov substitution principle: the reference implementation of JSR-349 (Hibernate Validator) in this case produces HV000151: A method overriding another method must not alter the parameter constraint configuration

问题:是否有人知道一种方法来注释cxf生成的Web服务接口方法 @Valid的参数

Question: Is anybody aware of a way to annotate the cxf-generated webservice interface method parameters with @Valid?

我知道,但这似乎并不是一件容易的事。
最简单的解决方案是将 @Valid 注释手动添加到Web服务界面,但我不满意修改生成的代码

I'm aware of the existance of the Annox plugin but this does not seems to be an easy task to accomplish with it.The easiest solution possible would be to manually add the @Valid annotation to the webservice interface but I'm not comfortable in modifying generated code

示例

pom.xml

<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>org.example.www</groupId>
    <artifactId>webservice-test-bval</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>Test bean validation on web service</name>

    <properties>
        <org.springframework.boot.version>1.4.2.RELEASE</org.springframework.boot.version>
        <com.github.krasa.krasa-jaxb-tools>1.5</com.github.krasa.krasa-jaxb-tools>
        <org.apache.cxf.version>3.1.3</org.apache.cxf.version>
        <cxf-codegen-plugin.version>3.0.1</cxf-codegen-plugin.version>
    </properties>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <!-- Import dependency management from Spring Boot -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${org.springframework.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <dependencies>
        <!-- Spring boot dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


        <!-- CXF dependencies -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>${org.apache.cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>${org.apache.cxf.version}</version>
        </dependency>
        <!-- Schema validation -->
        <dependency>
            <groupId>com.github.krasa</groupId>
            <artifactId>krasa-jaxb-tools</artifactId>
            <version>${com.github.krasa.krasa-jaxb-tools}</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.validation</groupId>
                    <artifactId>validation-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${org.springframework.boot.version}</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>${cxf-codegen-plugin.version}</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceRoot>${project.build.directory}/generated/</sourceRoot>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>${project.basedir}/src/main/resources/wsdl/test.wsdl</wsdl>
                                    <wsdlLocation>classpath:wsdl/test.wsdl</wsdlLocation>
                                    <extraargs>
                                        <extraarg>-xjc-XJsr303Annotations</extraarg>
                                    </extraargs>
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>com.github.krasa</groupId>
                        <artifactId>krasa-jaxb-tools</artifactId>
                        <version>${com.github.krasa.krasa-jaxb-tools}</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>

src / main / resources / wsdl /

test.wsdl

test.wsdl

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.example.org/test/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    name="test" targetNamespace="http://www.example.org/test/">
    <wsdl:types>
        <xsd:schema targetNamespace="http://www.example.org/test/">
            <xsd:element name="NewOperation">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="in">
                            <xsd:simpleType>
                                <xsd:restriction base="xsd:int">
                                    <xsd:minInclusive value="10" />
                                    <xsd:maxInclusive value="20" />
                                </xsd:restriction>
                            </xsd:simpleType>
                        </xsd:element>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
            <xsd:element name="NewOperationResponse">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="out">
                            <xsd:simpleType>
                                <xsd:restriction base="xsd:string">
                                    <xsd:pattern value="[A-Z]+" />
                                </xsd:restriction>
                            </xsd:simpleType>
                        </xsd:element>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="NewOperationRequest">
        <wsdl:part element="tns:NewOperation" name="parameters" />
    </wsdl:message>
    <wsdl:message name="NewOperationResponse">
        <wsdl:part element="tns:NewOperationResponse" name="parameters" />
    </wsdl:message>
    <wsdl:portType name="testWS">
        <jaxws:bindings xmlns:jaxws="http://java.sun.com/xml/ns/jaxws">
            <jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
        </jaxws:bindings>
        <wsdl:operation name="NewOperation">
            <wsdl:input message="tns:NewOperationRequest" />
            <wsdl:output message="tns:NewOperationResponse" />
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="testWSSOAP" type="tns:testWS">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="NewOperation">
            <soap:operation soapAction="http://www.example.org/test/NewOperation" />
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="testWS">
        <wsdl:port binding="tns:testWSSOAP" name="testWSSOAP">
            <soap:address location="http://www.example.org/" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

src / main / java /

org.example.test

org.example.test

package org.example.test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.support.SpringBootServletInitializer;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

package org.example.test;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.validation.Valid;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;

import org.example.test.ObjectFactory;

@WebService(targetNamespace = "http://www.example.org/test/", name = "testWS")
@XmlSeeAlso({ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface TestWSValid {

    @WebMethod(operationName = "NewOperation", action = "http://www.example.org/test/NewOperation")
    @Valid @WebResult(name = "NewOperationResponse", targetNamespace = "http://www.example.org/test/", partName = "parameters")
    public NewOperationResponse newOperation(
            @Valid @WebParam(partName = "parameters", name = "NewOperation", targetNamespace = "http://www.example.org/test/")
        NewOperation parameters
    );
}

org.example.test.configuration

org.example.test.configuration

package org.example.test.configuration;

import javax.xml.ws.Endpoint;

import org.apache.cxf.Bus;
import org.apache.cxf.feature.Feature;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.apache.cxf.validation.BeanValidationFeature;
import org.example.test.services.TestWSImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ImportResource({ "classpath:META-INF/cxf/cxf.xml", "classpath:META-INF/cxf/cxf-servlet.xml" })
@ComponentScan({ "org.example.test" })
public class ApplicationConfiguration {

    @Autowired
    private Bus cxfBus;

    @Bean
    public Endpoint testWSEndpoint() {
        EndpointImpl endpoint = new EndpointImpl(cxfBus, new TestWSImpl());
        endpoint.setAddress("/testws");
        endpoint.publish();
        return endpoint;
    }

    @Bean
    public ServletRegistrationBean cxfServlet() {
        ServletRegistrationBean servlet = new ServletRegistrationBean(new CXFServlet(), "/services/*");
        servlet.setLoadOnStartup(1);
        return servlet;
    }

    @Bean
    public Feature validationFeature() {
        Feature validationFeature = new BeanValidationFeature();
        validationFeature.initialize(cxfBus);
        cxfBus.getFeatures().add(validationFeature);
        ConstraintViolationInterceptor interceptor = new ConstraintViolationInterceptor();
        cxfBus.getInFaultInterceptors().add(interceptor);
        cxfBus.getOutFaultInterceptors().add(interceptor);
        cxfBus.getProperties().put("exceptionMessageCauseEnabled", true);
        return validationFeature;
    }
}

package org.example.test.configuration;

import java.text.MessageFormat;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolationException;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.binding.soap.interceptor.Soap11FaultOutInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;

public class ConstraintViolationInterceptor extends AbstractSoapInterceptor {

    public ConstraintViolationInterceptor() {
        super(Phase.MARSHAL);
        getBefore().add(Soap11FaultOutInterceptor.class.getName());
    }

    private static final String TEMPLATE = "[{0}] {1} : {2}";

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        Fault fault = (Fault) message.getContent(Exception.class);
        Throwable exception = fault.getCause();
        if (exception instanceof ConstraintViolationException) {
            fault.setMessage(processConstraints((ConstraintViolationException) exception));
        }
    }

    private String processConstraints(ConstraintViolationException exception) {
        return exception.getConstraintViolations().stream().map((error) -> {
            return MessageFormat.format(TEMPLATE, error.getPropertyPath(), error.getMessage(), error.getInvalidValue());
        }).collect(Collectors.joining(System.lineSeparator()));
    }

}

org.example.test.services

org.example.test.services

package org.example.test.services;

import javax.jws.WebService;

import org.example.test.NewOperation;
import org.example.test.NewOperationResponse;
import org.example.test.ObjectFactory;
import org.example.test.TestWS;

@WebService(endpointInterface = "org.example.test.TestWS", portName = "TestWSPort", serviceName = "TestWS", targetNamespace = "http://www.example.org/test/")
public class TestWSImpl implements TestWS {

    @Override
    public NewOperationResponse newOperation(NewOperation parameters) {
        int in = parameters.getIn();
        NewOperationResponse response = new ObjectFactory().createNewOperationResponse();
        if (in < 10 || in > 20) {
            response.setOut("no no no");
        } else {
            response.setOut("OK");
        }
        return response;
    }

}

关于上述项目,您可以测试,只要 TestWSImpl实现TestWS (生成的类)就不会发生验证,但是如果 TestWSImpl实现TestWSValid (包含带有 @Valid 附加代码的生成代码的类),然后验证将按预期进行

With reference to the project above, you can test that as long as TestWSImpl implements TestWS (the generated class) there is no validation occurring, but if TestWSImpl implements TestWSValid (the class that contains the generated code with the @Valid addition) then the validation works as expected

推荐答案

Apache CXF的wsdl2java支持是可插入的。有一个META-INF / tools-plugin.xml描述符,它允许您定义自定义生成器(前端配置文件)。因此,如果您需要在所有cxf生成的Web服务界面上使用 @Valid 批注,则只需插入自定义的 SEIGenerator
Apache CXF使用Velocity模板生成SEI接口。因此,您只需要使用自定义模板覆盖默认模板即可。

Apache CXF's wsdl2java support is pluggable. There is a META-INF/tools-plugin.xml descriptor that allows you to define custom generators ("frontend profiles"). So if you need a @Valid annotation on all cxf-generated webservice interfaces, you can just plug-in a custom SEIGenerator.Apache CXF uses Velocity templates to generate the SEI interfaces. So you just need to overwrite the default template with the custom one.

因此,无需使用Anox或Krasa,您只需创建一个简单的 cxf -codegen-plugin 覆盖。

So instead of using the Anox or Krasa you can just create a simple cxf-codegen-plugin overwrite.

因此,让我们创建一个单独的项目,仍然可以将其放在同一项目中,但可以放在不同的模块中,但是为了获得更好的可重用性,我会说一个新项目。

So lets create a separate project, still you can put it in the same project but in different module, but for better reusability I would say a new project.

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

    <groupId>org.example.test</groupId>
    <artifactId>valid-cxf</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modelVersion>4.0.0</modelVersion>

    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-codegen-plugin</artifactId>
            <version>${org.apache.cxf.version}</version>
        </dependency>
    </dependencies>

</project>

让我们使用服务加载程序来定义新的默认SEI生成器。

Lets use service loader to define new default SEI generator.

src / main / resources / META-INF / tools-plugin.xml

<?xml version="1.0" encoding="utf-8"?>
<plugin xmlns="http://cxf.apache.org/tools/plugin" name="play" version="" provider="play.typesafe.com">
    <frontend name="sample" package="org.apache.cxf.tools.wsdlto.frontend.jaxws" profile="JAXWSProfile">
    <container name="JAXWSContainer" package="org.apache.cxf.tools.wsdlto.frontend.jaxws" toolspec="jaxws-toolspec.xml"/>
    <processor name="WSDLToJavaProcessor" package="org.apache.cxf.tools.wsdlto.frontend.jaxws.processor"/>
    <builder name="JAXWSDefinitionBuilder" package="org.apache.cxf.tools.wsdlto.frontend.jaxws.wsdl11"/>
    <generators package="com.example.plugin">
        <generator name="CustomSEIGenerator"/>
    </generators>
</frontend>

在这里我们定义了一个新的 SEIGenerator ,名称为 CustomSEIGenerator ,来自软件包 com.example.plugin

Here we have defined a new SEIGenerator with name CustomSEIGenerator from the package com.example.plugin

接下来让我们定义一个覆盖所有速度的速度模板(在本例中,仅对 @Valid 注释进行硬编码Web服务接口)基于官方CXF

Next lets define a velocity template with all of our overwrites (in our case just hardcoded @Valid annotation on web service interface) This one is based on the official CXF sei.vm

src / main / resources / valid-sei.vm

#if ($intf.packageJavaDoc != "")
/**
$intf.packageJavaDoc
 */
#end
package $intf.PackageName;

#if ($mark-generated == "true")
import javax.annotation.Generated;
#end
import javax.validation.Valid;
#foreach ($import in $intf.Imports)
import ${import};
#end

/**
#if ($intf.classJavaDoc != "")
 $intf.classJavaDoc
 *
 #end
 * This class was generated by $fullversion
 * $currentdate
 * Generated source version: $version
 *
 */
#foreach ($annotation in $intf.Annotations)
 $annotation
#end
#if ($mark-generated == "true")
@Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date =     "$currentdate", comments = "$fullversion")
#end
public interface $intf.Name ${sei-superinterface-string}{
#foreach ($method in $intf.Methods)

#if ($method.JavaDoc != "")
    /**
${method.JavaDoc}
     */
#end
#foreach ($annotation in $method.Annotations)
    $annotation
#end
@Valid
#if ($mark-generated == "true")
    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "$currentdate")
#end
    public $method.returnValue ${method.Name}(#if($method.ParameterList.size() == 0))#end
#if($method.ParameterList.size() != 0)

#foreach ($param in ${method.ParameterList})
    $param
#end
)#end#if($method.Exceptions.size() > 0) throws#foreach($exception in $method.Exceptions) $exception.ClassName#if($method.Exceptions.size() != $velocityCount),#end#end#end;
#end
}

最后创建 CustomSEIGenerator ,它将使用我们的速度模板。

And at the end lets create the CustomSEIGenerator which will use our velocity template.

package org.example.test;

import org.apache.cxf.tools.common.ToolException;
import org.apache.cxf.tools.wsdlto.frontend.jaxws.generators.SEIGenerator;

import java.io.Writer;

/**
 * Just a sample custom generator which use custom velocity template to generate SEI
 *
 */
public class CustomSEIGenerator extends SEIGenerator {

    @Override
    protected void doWrite(String templateName, Writer outputs) throws ToolException {

        if (templateName.endsWith("/sei.vm")) {
            templateName = "valid-sei.vm";
        }

        super.doWrite(templateName, outputs);
    }
}

假设您已经构建了覆盖并发布到您的插件本地Maven存储库,接下来您只需要向项目 pom.xml 中添加依赖项信息。
因此,问题中的pom将保持不变,只有 cxf-codegen-plugin 将获得新的依赖关系。

Assuming you have build the plugin overwrite and published to your local maven repo, next you just need to add a dependency information to your project pom.xml.So the pom from the question will remain the same only the cxf-codegen-plugin will get a new dependency.

在您的pom.xml中

<plugin>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-codegen-plugin</artifactId>
        <version>${cxf-codegen-plugin.version}</version>
        <executions>
            <execution>
                <id>generate-sources</id>
                <phase>generate-sources</phase>
                <configuration>
                        <sourceRoot>${project.build.directory}/generated/</sourceRoot>
                        <wsdlOptions>
                            <wsdlOption>
                                <wsdl>${project.basedir}/src/main/resources/wsdl/test.wsdl</wsdl>
                                <wsdlLocation>classpath:wsdl/test.wsdl</wsdlLocation>
                            </wsdlOption>
                            <extraargs>
                                <extraarg>-fe</extraarg>
                                <extraarg>sample</extraarg>
                            </extraargs>
                        </wsdlOptions>
                    </configuration>
                    <goals>
                        <goal>wsdl2java</goal>
                    </goals>
                </execution>
            </executions>
            <dependencies>
                <dependency>
                <groupId>org.example.test</groupId>
                <artifactId>valid-cxf</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
    </plugin>

就是这样,现在您可以完全管理wsdl生成的内容和方式。

Thats it, now you can fully manage what and how is generated from your wsdl.

可以在此存储库中找到完整的示例

A fully working example can be found at this repo https://github.com/babltiga/cxf-valid-sample

这篇关于cxf-codegen-plugin不符合cxf ValidationFeature的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 18:53