web-dev-qa-db-ja.com

Spring:ResponseEntity <Void>で空のHTTP応答を返すことが機能しない

SpringでREST API(4.1.1。)を実装しています。特定のHTTPリクエストでは、応答として本文のないヘッドを返します。ただし、_ResponseEntity<Void>_は機能していないようです。MockMvcテストで呼び出された場合、406(受け入れられません)が返されます。パラメータ値(new ResponseEntity<String>( HttpStatus.NOT_FOUND ))なしで_ResponseEntity<String>_を使用すると正常に機能します。

方法:

_@RequestMapping( method = RequestMethod.HEAD, value = Constants.KEY )
public ResponseEntity<Void> taxonomyPackageExists( @PathVariable final String key ) {

    LOG.debug( "taxonomyPackageExists queried with key: {0}", key ); //$NON-NLS-1$

    final TaxonomyKey taxonomyKey = TaxonomyKey.fromString( key );

    LOG.debug( "Taxonomy key created: {0}", taxonomyKey ); //$NON-NLS-1$

    if ( this.xbrlInstanceValidator.taxonomyPackageExists( taxonomyKey ) ) {

        LOG.debug( "Taxonomy package with key: {0} exists.", taxonomyKey ); //$NON-NLS-1$

        return new ResponseEntity<Void>( HttpStatus.OK );

    } else {

        LOG.debug( "Taxonomy package with key: {0} does NOT exist.", taxonomyKey ); //$NON-NLS-1$

        return new ResponseEntity<Void>( HttpStatus.NOT_FOUND );
    }

}
_

テストケース(TestNG):

_public class TaxonomyQueryControllerTest {

private XbrlInstanceValidator   xbrlInstanceValidatorMock;
private TaxonomyQueryController underTest;
private MockMvc                 mockMvc;

@BeforeMethod
public void setUp() {
    this.xbrlInstanceValidatorMock = createMock( XbrlInstanceValidator.class );
    this.underTest = new TaxonomyQueryController( this.xbrlInstanceValidatorMock );
    this.mockMvc = MockMvcBuilders.standaloneSetup( this.underTest ).build();
}

@Test
public void taxonomyPackageDoesNotExist() throws Exception {
    // record
    expect( this.xbrlInstanceValidatorMock.taxonomyPackageExists( anyObject( TaxonomyKey.class ) ) ).andStubReturn(
            false );

    // replay
    replay( this.xbrlInstanceValidatorMock );

    // do the test
    final String taxonomyKey = RestDataFixture.taxonomyKeyString;

    this.mockMvc.perform( head( "/taxonomypackages/{key}", taxonomyKey ).accept( //$NON-NLS-1$
            MediaType.APPLICATION_XML ) ).andExpect( status().isNotFound() );

}

}
_

このスタックトレースで失敗します。

_FAILED: taxonomyPackageDoesNotExist
Java.lang.AssertionError: Status expected:<404> but was:<406>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.Java:60)
at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.Java:89)
at org.springframework.test.web.servlet.result.StatusResultMatchers$10.match(StatusResultMatchers.Java:652)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.Java:153)
at de.zeb.control.application.xbrlstandalonevalidator.restservice.TaxonomyQueryControllerTest.taxonomyPackageDoesNotExist(TaxonomyQueryControllerTest.Java:54)
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:57)
at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
at Java.lang.reflect.Method.invoke(Method.Java:606)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.Java:84)
at org.testng.internal.Invoker.invokeMethod(Invoker.Java:714)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.Java:901)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.Java:1231)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.Java:127)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.Java:111)
at org.testng.TestRunner.privateRun(TestRunner.Java:767)
at org.testng.TestRunner.run(TestRunner.Java:617)
at org.testng.SuiteRunner.runTest(SuiteRunner.Java:334)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.Java:329)
at org.testng.SuiteRunner.privateRun(SuiteRunner.Java:291)
at org.testng.SuiteRunner.run(SuiteRunner.Java:240)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.Java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.Java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.Java:1224)
at org.testng.TestNG.runSuitesLocally(TestNG.Java:1149)
at org.testng.TestNG.run(TestNG.Java:1057)
at org.testng.remote.RemoteTestNG.run(RemoteTestNG.Java:111)
at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.Java:204)
at org.testng.remote.RemoteTestNG.main(RemoteTestNG.Java:175)
_
22
ScarOnTheSky

本体なしでResponseEntityを返す場合、SpringはResponseEntity戻り値の宣言で提供される型引数を使用して、本体の型を決定します。

だから

public ResponseEntity<Void> taxonomyPackageExists( @PathVariable final String key ) {

そのタイプはVoidになります。その後、Springは登録されているすべてのHttpMessageConverterインスタンスをループし、Void型の本体を記述できるインスタンスを見つけます。そのようなHttpMessageConverterは存在しないため(デフォルト構成の場合)、受け入れ可能な応答を生成できないと判断し、406 Not Acceptable HTTP応答を返します。

ResponseEntity<String>を使用すると、SpringはStringを応答本文として使用し、StringHttpMessageConverterをハンドラーとして見つけます。また、StringHttpMessageHandlerはあらゆるAcceptedメディアタイプのコンテンツを生成できるため、クライアントが要求しているapplication/xmlを処理できます。

iddy85のソリューション (現在は間違っていますが、ResponseEntity<?>を示唆しているようです)で、ボディのタイプはObjectとして推論されます。クラスパスに正しいライブラリがある場合、SpringはHttpMessageConverter型のapplication/xmlを生成するために使用できるXML Objectにアクセスできます。

26

メソッドの実装はあいまいです。次を試して、コードを少し編集し、HttpStatus.NO_CONTENTを使用しました。つまり、HttpStatus.OKの代わりにコンテンツがありません。

サーバーは要求を実行しましたが、エンティティ本体を返す必要はなく、更新されたメタ情報を返したい場合があります。応答には、エンティティヘッダーの形式で新規または更新されたメタ情報が含まれている場合があります。これは、存在する場合、要求されたバリアントに関連付けられる必要があります。

[〜#〜] t [〜#〜]の任意の値は204では無視されますが、404では無視されません

  public ResponseEntity<?> taxonomyPackageExists( @PathVariable final String key ) {
            LOG.debug( "taxonomyPackageExists queried with key: {0}", key ); //$NON-NLS-1$
            final TaxonomyKey taxonomyKey = TaxonomyKey.fromString( key );
            LOG.debug( "Taxonomy key created: {0}", taxonomyKey ); //$NON-NLS-1$

            if ( this.xbrlInstanceValidator.taxonomyPackageExists( taxonomyKey ) ) {
                LOG.debug( "Taxonomy package with key: {0} exists.", taxonomyKey ); //$NON-NLS-1$
                 return new ResponseEntity<T>(HttpStatus.NO_CONTENT);
            } else {
               LOG.debug( "Taxonomy package with key: {0} does NOT exist.", taxonomyKey ); //$NON-NLS-1$
                return new ResponseEntity<T>( HttpStatus.NOT_FOUND );
            }

    }
9
iamiddy

また、 docs を見るときに、少し簡潔であると思われるtypeパラメーターとSpringが指定したものを指定することもできません。

@RequestMapping(method = RequestMethod.HEAD, value = Constants.KEY )
public ResponseEntity taxonomyPackageExists( @PathVariable final String key ){
    // ...
    return new ResponseEntity(HttpStatus.NO_CONTENT);
}
8
adanilev

Spring 4 MVC ResponseEntity.BodyBuilderおよびResponseEntity Enhancements Example のように記述できます。

_....
   return ResponseEntity.ok().build();
....
   return ResponseEntity.noContent().build();
_

更新:

返される値がOptionalである場合、便利なメソッドがあり、返されるok()またはnotFound()

_return ResponseEntity.of(optional)
_
6
GKislin

個人的には、空の応答を処理するために、統合テストでMockMvcResponseオブジェクトを次のように使用します。

MockMvcResponse response = RestAssuredMockMvc.given()
                .webAppContextSetup(webApplicationContext)
                .when()
                .get("/v1/ticket");

    assertThat(response.mockHttpServletResponse().getStatus()).isEqualTo(HttpStatus.NO_CONTENT.value());

私のコントローラーでは、次のような特定の場合に空の応答を返します。

return ResponseEntity.noContent().build();
1
Alex