春季验证:使用两个验证程序时,获取“验证程序的无效目标”

时间:2018-08-13 21:03:03

标签: spring spring-mvc spring-boot custom-validators

我在使用@InitBinder注释实现两个验证器时遇到问题。

控制器代码:

<Window x:Class="Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="Window1" Height="250" Width="400">
    <Window.Resources>
        <Style x:Key="FocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
        <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
        <SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
        <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
        <SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
        <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
        <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
        <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
        <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
        <Style TargetType="{x:Type Button}">
            <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
            <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
            <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Padding" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Grid>
                            <Border BorderThickness="0,0,0,0"
                                    CornerRadius="5" Margin="0,6,0,-5" AllowDrop="True">
                                <Border.Background>
                                    <RadialGradientBrush SpreadMethod="Reflect">
                                        <GradientStop Color="#FFA8A8A8" Offset="0.923"/>
                                        <GradientStop Color="White"/>
                                        <GradientStop Color="#FFE0E0E0" Offset="0.391"/>
                                    </RadialGradientBrush>
                                </Border.Background>
                            </Border>
                            <Border x:Name="Shadowborder" BorderBrush="Black" BorderThickness="0,0,0,10"
                                    CornerRadius="5">
                                <Border.Effect>
                                    <DropShadowEffect Direction="270" Opacity="0.5"/>
                                </Border.Effect>
                            </Border>
                            <Border CornerRadius="5" x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                                <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            </Border>
                        </Grid>

                        <ControlTemplate.Triggers>
                            <Trigger Property="IsDefaulted" Value="true">
                                <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
                            </Trigger>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
                                <Setter Property="Margin" TargetName="border" Value="0,5,0,-5"/>
                                <Setter Property="Margin" TargetName="Shadowborder" Value="0,5,0,-5"/>
                                <Setter Property="Effect" TargetName="Shadowborder">
                                    <Setter.Value>
                                        <DropShadowEffect Direction="270" Opacity="0.3" BlurRadius="1" ShadowDepth="1"/>
                                    </Setter.Value>
                                </Setter>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
                                <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Button HorizontalAlignment="Center" VerticalAlignment="Center" Padding="20" Content="Hello!"/>
    </Grid>
</Window>

会话验证器:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/fragment_video_appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:fitsSystemWindows="true">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            android:background="@color/white">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginTop="200dp"
                android:orientation="vertical"
                app:layout_collapseMode="parallax">

                <TextView
                    android:id="@+id/fragment_video_title_textview"
                    style="@style/TextAppearance.AppCompat.Light.SearchResult.Subtitle"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="16dp"
                    android:layout_marginStart="16dp"
                    android:layout_marginTop="8dp"
                    android:ellipsize="end"
                    android:maxLines="1"
                    android:textStyle="bold"
                    tools:text="funny videos" />

                <TextView
                    android:id="@+id/fragment_video_plays_textview"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="16dp"
                    android:layout_marginEnd="16dp"
                    android:textSize="12sp"
                    tools:text="1.7k plays ⋅ 4 years ago" />

                <TextView
                    android:id="@+id/fragment_video_description_textview"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="16dp"
                    android:layout_marginEnd="16dp"
                    android:layout_marginTop="10dp"
                    android:textColor="@color/colorPrimaryText"
                    android:textSize="13sp"
                    tools:text="funny videos compilation"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="1dp"
                    android:layout_marginTop="8dp"
                    android:background="@color/mediumLightGray"/>

                <FrameLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="16dp"
                    android:layout_marginEnd="16dp">

                    <include layout="@layout/item_user" />
                </FrameLayout>
            </LinearLayout>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:minHeight="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:contentInsetStart="0dp">

                <android.support.constraint.ConstraintLayout
                    android:id="@+id/fragment_video_info_layout"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <android.support.v7.widget.AppCompatImageView
                        android:id="@+id/fragment_video_imageview"
                        android:layout_width="match_parent"
                        android:layout_height="200dp"
                        android:scaleType="fitXY"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@android:color/darker_gray" />

                    <TextView
                        android:id="@+id/fragment_video_timelength_textview"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:paddingStart="5dp"
                        android:paddingEnd="5dp"
                        android:paddingTop="3dp"
                        android:paddingBottom="3dp"
                        android:textSize="12sp"
                        android:textColor="@color/white"
                        android:background="@color/dimfilter"
                        app:layout_constraintBottom_toBottomOf="@+id/fragment_video_imageview"
                        app:layout_constraintEnd_toEndOf="parent"
                        tools:text="03:45"/>

                </android.support.constraint.ConstraintLayout>

            </android.support.v7.widget.Toolbar>

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/fragment_video_viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.design.widget.TabLayout
            android:id="@+id/fragment_video_tablayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/mediumLightGray"
            app:tabIndicatorColor="@color/colorPrimary"
            app:tabTextColor="@color/darkMediumGray"
            app:tabSelectedTextColor="@color/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:layout_collapseMode="pin"/>

    </android.support.v4.view.ViewPager>

    <include layout="@layout/layout_content_progress" />

    <include layout="@layout/layout_message" />

</android.support.design.widget.CoordinatorLayout>

频道验证器:

@Autowired
private SessionValidator sessionValidator;

@Autowired
private ChannelValidator channelValidator;

@InitBinder
public void initBinder(WebDataBinder binder){
    binder.addValidators(sessionValidator, channelValidator);
}

@RequestMapping(method = RequestMethod.GET)
public UserInfo findBySession(
        @Valid @ModelAttribute Session session,
        @Valid @ModelAttribute Channel channel){
    //...
}

我在调用控制器时收到以下异常:

@Component
public class SessionValidator implements Validator {

    @Override
    public boolean supports(Class<?> aClass){
        return Session.class.equals(aClass);
    }

    @Override
    public void validate(Object o, Errors errors){
        //...
    }
}

有人知道如何解决吗?预先感谢!

2 个答案:

答案 0 :(得分:1)

您绑定了两个验证器,但是对于每个要验证的参数,将仅支持其中一个。
SessionValidator支持Session参数,但不支持Channel参数,反之,ChannelValidator支持Channel参数,但不支持{{1} }参数。
例外。

作为第一种选择,您可以在每个Spring Validator子类中支持两种参数类型。有点笨拙,但应该可以使用:

Session

您当然应该检查@Override public boolean supports(Class<?> aClass){ return Channel.class.equals(aClass) || Session.class.equals(aClass); } 中的类型,并仅在与验证器类匹配时执行验证。

作为第二种选择,通过为每个类实现validate()来进行验证并通过在控制器中显式验证参数来使用标准验证API。

作为第三种选择,如果可以的话,您可以直接注释javax.validation.ConstraintValidatorSession类上的约束。因此,您仍然可以在参数声明中使用Channel来保持自动验证。

答案 1 :(得分:1)

您只能添加支持的验证器:

@InitBinder
protected void initBinder(WebDataBinder binder) {
    if (binder.getTarget() == null) return;
    final ImmutableList<Validator> validatorsList = ImmutableList.of(
        new CunsomValidator1(),
        new CunsomValidator2()
    );

    for (Validator validator : validatorsList) {
        if (validator.supports(binder.getTarget().getClass())) {
            binder.addValidators(validator);
        }
    }
}