简体   繁体   English

Spring:根据XSD架构验证REST控制器

[英]Spring: Validate REST controller against XSD schema

At the moment I have RestController with the following code 目前我有RestController与以下代码

package be.smartask.api;

import be.smartask.api.model.NumberValue;
import be.smartask.api.model.TextValue;
import be.smartask.api.model.Translations;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;


/**
 * @author Glenn Van Schil
 *         Created on 21/01/2016
 */
@CrossOrigin
@RestController
@RequestMapping(path = "/values")
public class Controller {

    @RequestMapping(method = RequestMethod.POST, headers = "Value-Type=text")
    ResponseEntity<?> createAttribute(@RequestBody TextValue value) {
        System.out.println(value.getCode());
        for (Translations.Translation translation : value.getTranslations().getTranslation()) {
            System.out.println(translation.getLang());
            System.out.println(translation.getValue());
        }
        return new ResponseEntity<>(HttpStatus.CREATED);
    }

    @RequestMapping(method = RequestMethod.POST, headers = "Value-Type=number")
    ResponseEntity<?> createAttribute(@RequestBody NumberValue value) {
        System.out.println(value.getMinValue());
        System.out.println(value.getMaxValue());
        return new ResponseEntity<>(HttpStatus.CREATED);
    }
}

This works great, I can post a NumberValue like this: 这很好用,我可以像这样发布一个NumberValue:

<numberValue id="id" minValue="0" maxValue="10"/>

But when I post a TextValue to NumberValue method 但是当我将TextValue发布到NumberValue方法时

<textvalue code="LUXE">
    <translations>
        <translation lang="en">luxury car</translation>
    </translations>
</textvalue>

It still works. 它仍然有效。 It just leaves the minValue and maxValue to 0,0. 它只是将minValue和maxValue留给0,0。

My question is: How can i enforce a 400: Bad Request (or something like that) when the body is not exactly the same as defined in my xsd file? 我的问题是:如果正文与我的xsd文件中定义的不完全相同,我怎么能强制执行400:错误请求(或类似的东西)? By defined I mean the xml tag's name is not the same or a required xs:attribute is missing, ... 根据定义,我的意思是xml标签的名称不相同或缺少必需的xs:属性,...

My xsd file: 我的xsd文件:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
           xmlns:xs="http://www.w3.org/2001/XMLSchema">

            <!-- ValueVO -->
    <xs:complexType name="geopoint">
        <xs:attribute type="xs:double" name="lat"/>
        <xs:attribute type="xs:double" name="lon"/>
    </xs:complexType>

    <xs:complexType name="translations">
        <xs:sequence>
            <xs:element name="translation" minOccurs="0" maxOccurs="unbounded">
                <xs:complexType>
                    <xs:simpleContent>
                        <xs:extension base="xs:string">
                            <xs:attribute type="xs:string" name="lang" use="required"/>
                        </xs:extension>
                    </xs:simpleContent>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="value">
        <xs:attribute type="xs:string" name="id" use="required"/>
    </xs:complexType>

    <xs:complexType name="textValue">
        <xs:complexContent>
            <xs:extension base="value">
                <xs:sequence>
                    <xs:element type="translations" name="translations"/>
                </xs:sequence>
                <xs:attribute type="xs:string" name="code" use="required"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="numberValue">
        <xs:complexContent>
            <xs:extension base="value">
                <xs:attribute type="xs:double" name="minValue" use="required"/>
                <xs:attribute type="xs:double" name="maxValue" use="required"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="dateValue">
        <xs:complexContent>
            <xs:extension base="value">
                <xs:attribute type="xs:date" name="minValue" use="required"/>
                <xs:attribute type="xs:date" name="maxValue" use="required"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="cityValue">
        <xs:complexContent>
            <xs:extension base="value">
                <xs:sequence>
                    <xs:element type="geopoint" name="geopoint"/>
                    <xs:element type="translations" name="translations"/>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="timeValue">
        <xs:complexContent>
            <xs:extension base="value"/>
        </xs:complexContent>
    </xs:complexType>

    <xs:element name="value" type="value"/>
    <xs:element name="textValue" type="textValue"/>
    <xs:element name="numberValue" type="numberValue"/>
    <xs:element name="dateValue" type="dateValue"/>
    <xs:element name="cityValue" type="cityValue"/>
    <xs:element name="timeValue" type="timeValue"/>

    <xs:element name="values">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="value" maxOccurs="unbounded" minOccurs="0"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <!-- END ValueVO --> 
</xs:schema>

UPDATE UPDATE

I added the following to my spring config based on Sheetal Mohan Sharma's answer 我根据Sheetal Mohan Sharma的回答在Spring配置中添加了以下内容

<?xml version="1.0" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd


http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="be.smartask.api"/>

    <mvc:annotation-driven/>

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <ref bean="marshallingHttpMessageConverter"/>
                <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
            </list>
        </property>
    </bean>

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    </bean>
    <bean id="marshallingHttpMessageConverter"
          class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"
          p:marshaller-ref="jaxb2Marshaller" p:unmarshaller-ref="jaxb2Marshaller"/>

    <bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="schema" value="classpath:schema/xsd/smartaskRead.xsd"/>
        <property name="classesToBeBound">
            <list>
                <value>be.smartask.api.model.NumberValue</value>
                <value>be.smartask.api.model.TextValue</value>
            </list>
        </property>
    </bean>
</beans>

But NumberValue still accepts TextValue... 但NumberValue仍然接受TextValue ...

You can look at using jaxb marshaller and unmarshaller Use message converters and jaxmashaller to check against XSD. 您可以查看使用jaxb marshaller和unmarshaller使用消息转换器和jaxmashaller来检查XSD。 Error will be thrown if validation fails but it may not be specific. 如果验证失败,则会引发错误,但可能不具体。 Check the links below. 检查以下链接。

<bean id="marshallingHttpMessageConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"p:marshaller-ref="jaxb2Marshaller" p:unmarshaller-ref="jaxb2Marshaller" />

<bean id="jaxb2Marshaller">
    <property name="schema" value="classpath:/mySchema.xsd"/>
    <property name="classesToBeBound">
        <list>
            <value>com.xyz.RequestPojo</value>
            <value>com.xyz.ResponsePojo</value>
        </list>
    </property>
</bean>

Few good examples - here and here 几个很好的例子 - 这里这里

We managed to do this even with API versioning by providing the namespace inside the XSD files 通过在XSD文件中提供命名空间,我们甚至通过API版本控制实现了这一目标

config.xml config.xml中

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">

    <context:component-scan base-package="be.smartask.api"/>

    <context:annotation-config/>

    <mvc:cors>
        <mvc:mapping path="/**"/>
    </mvc:cors>

    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean id="marshallingHttpMessageConverter"
                  class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
                <property name="marshaller" ref="jaxb2Marshaller"/>
                <property name="unmarshaller" ref="jaxb2Marshaller"/>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="schemas">
          <list>
            <value>classpath:/schema/xsd/v1/smartAsk.xsd</value>
            <value>classpath:/schema/xsd/v2/smartAsk.xsd</value>
          </list>
        </property>
        <property name="packagesToScan">
          <list>
            <value>be.smartask.api.model.smartask.v1</value>
            <value>be.smartask.api.model.smartask.v2</value>
          </list>
        </property>
    </bean>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

First you provide the schema locations, second the the package location of the xsd objects will be generated and last override the marshallingHttpMessageConverter with your newly created Jaxb2Marshaller 首先提供模式位置,然后生成xsd对象的包位置,最后使用新创建的Jaxb2Marshaller覆盖marshallingHttpMessageConverter

If you plan on doing some versioning in your API it's best to supply a namespace so the marshaller know which xsd file to use for which package 如果您计划在API中进行一些版本控制,最好提供一个命名空间,以便编组人员知道哪个xsd文件用于哪个包

<xs:schema xmlns="smartask:v1"
       xmlns:xs="http://www.w3.org/2001/XMLSchema"
       xmlns:xsd="http://www.w3.org/2001/XMLSchema"
       xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
       xmlns:annox="http://annox.dev.java.net"
       attributeFormDefault="unqualified"
       elementFormDefault="qualified"
       targetNamespace="smartask:v1"
       jaxb:version="2.1"
       jaxb:extensionBindingPrefixes="annox">

<xs:schema xmlns="smartask:v2"
       xmlns:xs="http://www.w3.org/2001/XMLSchema"
       xmlns:xsd="http://www.w3.org/2001/XMLSchema"
       xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
       xmlns:annox="http://annox.dev.java.net"
       attributeFormDefault="unqualified"
       elementFormDefault="qualified"
       targetNamespace="smartask:v2"
       jaxb:version="2.1"
       jaxb:extensionBindingPrefixes="annox">

in our case it was "smartask:v1" and "smartask:v2" 在我们的例子中,它是“smartask:v1”和“smartask:v2”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM