我们正在用Spring(4.1.1.)实现REST API。对于某些HTTP请求,我们希望返回一个没有主体的头作为响应。但是,使用ResponseEntity<Void>
似乎不起作用。当用MockMvc
测试调用时,返回406(不可接受)。使用没有参数值的ResponseEntity<String>
(new ResponseEntity<String>( HttpStatus.NOT_FOUND )
)工作正常。
方法:
@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)
6条答案
按热度按时间hxzsmxv21#
注:这适用于问题4.1.1.RELEASE中提到的版本。
Spring MVC通过
HttpEntityMethodProcessor
处理ResponseEntity
返回值。当
ResponseEntity
值没有设置主体时,如代码段中的情况,HttpEntityMethodProcessor
尝试通过@RequestMapping
处理程序方法的签名中ResponseEntity
返回类型的参数化来确定响应主体的内容类型。所以对于
该类型将是
Void
。然后HttpEntityMethodProcessor
将遍历其所有注册的HttpMessageConverter
示例,并找到一个可以为Void
类型编写主体的示例。根据您的配置,它可能会找到,也可能找不到。如果确实找到了,它仍然需要确保相应的主体将使用与请求的
Accept
头中提供的类型(在本例中为application/xml
)相匹配的Content-Type来编写。如果在所有这些检查之后,不存在这样的
HttpMessageConverter
,SpringMVC将确定它不能产生可接受的响应,因此返回406 NotAcceptable HTTP响应。对于
ResponseEntity<String>
,Spring将使用String
作为响应主体,并找到StringHttpMessageConverter
作为处理程序。由于StringHttpMessageHandler
可以生成任何媒体类型的内容(在Accept
头中提供),因此它将能够处理您的客户机正在请求的application/xml
。Spring MVC已经更改为如果
ResponseEntity
中的主体不是null
,则只返回406。如果您使用的是更新版本的Spring MVC,则不会看到原始问题中的行为。在iddy85's solution中,它似乎暗示
ResponseEntity<?>
,主体的类型将被推断为Object
。(版本〉2.5.0)及其XML扩展,SpringMVC可以访问MappingJackson2XmlHttpMessageConverter
,它可以使用MappingJackson2XmlHttpMessageConverter
为类型Object
生成application/xml
。**他们的解决方案仅在这些条件下有效。**否则,它将由于上面描述的相同原因而失败。fkaflof62#
根据Spring 4 MVC ResponseEntity.BodyBuilder and ResponseEntity Enhancements Example,它可以写为:
最新消息:
如果返回值是
Optional
,有方便的方法,返回ok()
或notFound()
:rkttyhzu3#
您也可以不指定type参数,这样看起来更简洁,也更符合Spring在查看文档时的意图:
mrzz3bfm4#
您的方法实现不明确,请尝试以下操作,稍微编辑代码并使用
HttpStatus.NO_CONTENT
(即204 No Content)代替HttpStatus.OK
服务器已经完成了请求,但不需要返回实体体,并且可能需要返回更新的元信息。响应可以包括新的或更新的元信息,以实体头的形式,如果存在的话,应该与请求的变量相关联。
qrjkbowd5#
对于Spring 5.2+,这对我很有效:
dldeef676#
就个人而言,为了处理空响应,我在集成测试中使用MockMvcResponse对象,如下所示:
在我的控制器中,我在如下的特定情况下返回空响应: