web-dev-qa-db-ja.com

Spring MVCコントローラーでのJsonViewの動的選択

Spring MVCで単一のビュークラスを静的に定義するために@JsonView(...)でコントローラーメソッドに注釈を付けることが可能であることを知っています。残念ながら、これは私が持つ可能性のあるすべてのタイプのビューに対して異なるエンドポイントが必要であることを意味します。

他の人がこれを尋ねたのを見る before 。このアプローチは機能する可能性がありますが、Springには多くの場合、同じことを行う多くの方法があります。いくつかの内部について少し知識がある場合、ソリューションは最初に表示されるよりもはるかに単純になることがあります。

現在のプリンシパルに基づいて適切なビューを動的に選択できる単一のコントローラーエンドポイントが欲しいのですが。適切なビュークラスまたはModelインスタンスを直接含む属性を持つMappingJacksonValueを直接返すことは可能ですか?

org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternalには、使用するビューを決定するコードスニペットがあります。

if (value instanceof MappingJacksonValue) {
            MappingJacksonValue container = (MappingJacksonValue) object;
            value = container.getValue();
            serializationView = container.getSerializationView();
        }

これはorg.springframework.web.servlet.mvc.method.annotation.JsonViewResponseBodyAdvice#beforeBodyWriteInternalからのようですが、Jackson2HttpMessageConverterが適切な情報を取得するために必要な情報を含む特定の値を返すだけでこれを回避できる方法がある場合、問題が発生します見る。

どんな助けも大歓迎です。

27
David

他の誰かが同じことを達成したいと思った場合、それは実際には非常に簡単です。

シリアル化するオブジェクトとビュークラスの両方を含むコントローラーからaorg.springframework.http.converter.json.MappingJacksonValueインスタンスを直接返すことができます。

これはorg.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternalメソッドによって取得され、適切なビューが使用されます。

これは次のように機能します。

@RequestMapping(value = "/accounts/{id}", method = GET, produces = APPLICATION_JSON_VALUE)
public MappingJacksonValue getAccount(@PathVariable("id") String accountId, @AuthenticationPrincipal User user) {
    final Account account = accountService.get(accountId);
    final MappingJacksonValue result = new MappingJacksonValue(account);
    final Class<? extends View> view = accountPermissionsService.getViewForUser(user);
    result.setSerializationView(view);
    return result;
}
39
David

ここに私を助けた上記の答えのバリエーションがあります。 Spring HATEOASペイロードの使用中にMappingJacksonValueを直接返す問題を発見しました。コントローラーのハンドラーから直接返すと、何らかの理由でResourcesResourceSupportのミックスインが正しく適用されず、JSON HAL _linksがリンクとしてレンダリングされます。また、Spring ResponseEntityは、ペイロードにbodyおよびstatusオブジェクトを表示する必要があるため、レンダリングされません。

ControllerAdviceを使用して同じことを実現すると、ペイロードが正しくレンダリングされ、必要に応じてビューが適用されます

@ControllerAdvice(assignableTypes = MyController.class)
public class MyControllerAdvice extends AbstractMappingJacksonResponseBodyAdvice {

  @Override
  protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, MethodParameter returnType,
                                         ServerHttpRequest req, ServerHttpResponse res) {
    ServletServerHttpRequest request = (ServletServerHttpRequest)req;
    String view = request.getServletRequest().getParameter("view");
    if ("hello".equals(view)) {
      bodyContainer.setSerializationView(HelloView.class);
    }
  }
}
11
Pawel Zieminski