web-dev-qa-db-ja.com

Spring MVC @PathVariable値を検証する方法は?

Spring MVCで実装された単純なRESTful JSON APIの場合、Bean Validation(JSR-303)を使用してハンドラーメソッドに渡されたパス変数を検証できますか?

例えば:

 @RequestMapping(value = "/number/{customerNumber}")
 @ResponseBody
 public ResponseObject searchByNumber(@PathVariable("customerNumber") String customerNumber) {
 ...
 }

ここでは、Bean検証を使用してcustomerNumber変数の長さを検証する必要があります。これはSpring MVC v3.x.xで可能ですか?そうでない場合、このタイプの検証に最適なアプローチは何ですか?

ありがとう。

18
Grover

Springは、ハンドラメソッドの@javax.validation.Validアノテーション付きパラメータの@PathVariableをサポートしていません。改善リクエストがありましたが、まだ 未解決 です。

最善の策は、ハンドラメソッドの本文でカスタム検証を行うか、他の回答で提案されているorg.springframework.validation.annotation.Validatedの使用を検討することです。

26

次のように使用できます。org.springframework.validation.annotation.Validatedを使用して、RequestParamまたはPathVariableを有効にします。

 *
 * Variant of JSR-303's {@link javax.validation.Valid}, supporting the
 * specification of validation groups. Designed for convenient use with
 * Spring's JSR-303 support but not JSR-303 specific.
 *

step.1 init ValidationConfig

@Configuration
public class ValidationConfig {
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
        return processor;
    }
}

step.2次のように、コントローラハンドラクラスに@Validatedを追加します。

@RequestMapping(value = "poo/foo")
@Validated
public class FooController {
...
}

step.3ハンドラーメソッドにvalidatorsを追加します。

   @RequestMapping(value = "{id}", method = RequestMethod.DELETE)
   public ResponseEntity<Foo> delete(
           @PathVariable("id") @Size(min = 1) @CustomerValidator int id) throws RestException {
        // do something
        return new ResponseEntity(HttpStatus.OK);
    }

最終段階。例外リゾルバをコンテキストに追加します。

@Component
public class BindExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        if (ex.getClass().equals(BindException.class)) {
            BindException exception = (BindException) ex;

            List<FieldError> fieldErrors = exception.getFieldErrors();
            return new ModelAndView(new MappingJackson2JsonView(), buildErrorModel(request, response, fieldErrors));
        }
    }
}
8
BeeNoisy

@PathVariableは、ユーザーに読み取り可能なメッセージを返信するために検証されることを意図していません。原則として、pathVariableは無効にしないでください。 pathVariableが無効な場合、理由は次のとおりです。

  1. バグにより不正なURLが生成された(jspのhrefなど)。番号 @Validが必要で、メッセージは必要ありません。コードを修正してください。
  2. 「ユーザー」がURLを操作しています。繰り返しますが、@Validが必要です。ユーザーに意味のあるメッセージを与える必要はありません。

どちらの場合も、Niceエラーページまたはエラーを示す意味のあるjson応答を生成するために、通常のSpring ExceptionHandlerによってキャッチされるまで、例外バブルをそのままにしておきます。この結果を得るために、カスタムエディターを使用して検証を行うことができます。

CustomerNumberクラスを作成します。おそらく不変です(CharSequenceの実装は不要ですが、基本的にはStringであるかのように使用できます)

public class CustomerNumber implements CharSequence {

    private String customerNumber;

    public CustomerNumber(String customerNumber) {
        this.customerNumber = customerNumber;
    }

    @Override
    public String toString() {
        return customerNumber == null ? null : customerNumber.toString();
    }

    @Override
    public int length() {
        return customerNumber.length();
    }

    @Override
    public char charAt(int index) {
        return customerNumber.charAt(index);
    }

    @Override
    public CharSequence subSequence(int start, int end) {
        return customerNumber.subSequence(start, end);
    }

    @Override
    public boolean equals(Object obj) {
        return customerNumber.equals(obj);
    }

    @Override
    public int hashCode() {
        return customerNumber.hashCode();
    }
}

検証ロジックを実装するエディターを作成します(この例では、例として、空白と固定長はありません)。

public class CustomerNumberEditor extends PropertyEditorSupport {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {

        if (StringUtils.hasText(text) && !StringUtils.containsWhitespace(text) && text.length() == YOUR_LENGTH) {
            setValue(new CustomerNumber(text));
        } else {
            throw new IllegalArgumentException();
            // you could also subclass and throw IllegalArgumentException
            // in order to manage a more detailed error message
        }
    }

    @Override
    public String getAsText() {
        return ((CustomerNumber) this.getValue()).toString();
    }
}

エディターをコントローラーに登録する

@InitBinder
public void initBinder(WebDataBinder binder) {

    binder.registerCustomEditor(CustomerNumber.class, new CustomerNumberEditor());
    // ... other editors
}

CustomerNumberの代わりにStringを受け入れるコントローラーメソッドのシグネチャを変更します(ResponseObjectが何であっても...)

@RequestMapping(value = "/number/{customerNumber}")
@ResponseBody
public ResponseObject searchByNumber(@PathVariable("customerNumber") CustomerNumber customerNumber) {
    ...
}
3
pdenti

解決策は簡単です:

@GetMapping(value = {"/", "/{hash:[a-fA-F0-9]{40}}"})
public String request(@PathVariable(value = "hash", required = false) String historyHash)
{
    // Accepted requests: either "/" or "/{40 character long hash}"
}

そして、はい、パス変数は、ユーザー入力と同様に検証されることを意図しています。

1
Gábor Dani

パス変数は、システム内のどのBeanともリンクされていない可能性があります。 JSR-303アノテーションで何をアノテーションしたいですか?パス変数を検証するには、このアプローチを使用する必要があります Spring 3 mvcで@PathVariable urlを検証する問題

0
mvb13