В соответствии с ValidationFeature documentation для того, чтобы проверки произойдет операций ввода и вывода привязки должны быть аннотированный с @Valid
CXF-CodeGen-плагин не соответствует CXF ValidationFeature
Однако интерфейс Webservice генерируемого CXF-codegen- плагин не имеет этих аннотаций, и я, кажется, не нашел аргумент командной строки или плагин, который позволяет их добавлять.
В @Valid
аннотация не может быть поставлена в реализации интерфейса Webservice, не нарушая принцип замещения Лиск: ссылка реализация JSR-349 (Hibernate Validator) в этом случае производит HV000151: Метод переопределяя другой метод не должен изменять параметр ограничения конфигурации
Вопрос: ли кто-нибудь известно о пути к аннотировать CXF генерируемых веб-сервиса параметры метода интерфейса с @Valid
?
Я знаю о существовании Annox plugin, но это, как представляется, не является легкой задачей для достижения этой цели. Самым простым решением можно было бы вручную добавить @Valid
аннотацию к интерфейсу WebService, но я не комфортно в модифицирующих сгенерированный код
продукта примера
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
<?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/Основной/Java/
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
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. услуги
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 implements TestWS
(сгенерированный класс) не существует никакой проверки встречающейся, но если TestWSImpl implements TestWSValid
(класс, который содержит сгенерированный код с @Valid
дополнения), то проверки работа как ожидалось
Я установил недостающий тег в данном Инструменты- plugin.xml, и я действительно создал и настроил настраиваемый генератор-плагин как зависимость, как вы описали, но я до сих пор не вижу аннотацию @Valid. Как вы думаете, что я могу пропустить? –
Я создам простой проект, чтобы показать, как он работает) и поделиться ссылкой github ... – Babl
В основном, в моем примере просто отсутствовал параметр имени переднего конца, который необходимо передать генератору cxf, добавив ' -fe образец '. Просто просмотрите обновленный файл pom.xml. –
Babl