我们正在使用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)
答案 0 :(得分:23)
当你返回一个没有正文的ResponseEntity
时,Spring会使用ResponseEntity
返回类型声明中提供的type参数来决定一个正文类型。
所以
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's solution中(目前错误,但似乎建议ResponseEntity<?>
),正文的类型将推断为Object
。如果类路径中有正确的库,Spring将可以访问XML HttpMessageConverter
,可以使用它为类型application/xml
生成Object
。
答案 1 :(得分:9)
您的方法实现不明确,请尝试以下操作,稍微编辑您的代码并使用HttpStatus.NO_CONTENT
,即204 No Content代替HttpStatus.OK
服务器已完成请求但不需要返回 entity-body,可能想要返回更新的元信息。该 响应可能包括新的或更新的元信息形式 实体标题,如果存在,应该与...相关联 要求的变体。
对于204,将忽略 T 的任何值,但不会忽略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 );
}
}
答案 2 :(得分:8)
您也不能指定看起来更干净的type参数以及Spring在查看docs时的意图:
def form_creation(request):
if request.method=='POST':
eventlist=request.POST.getlist('events')
print(eventlist)
for e in eventlist:
form=Registration_Form(request.POST)
form.username= request.POST['username']
form.email= request.POST['email']
form.phone_number=request.POST['phone_number']
form.college_name=request.POST['college_name']
form.year=request.POST['year']
form.events=EventClass.objects.get(events=e)
print(form.is_valid())
if form.is_valid():
form.save()
return render(request,'app/result.html',{'form':form})
else:
err="Please Enter Correct Data"
content={'form':form,'err':err}
return render(request,'app/form.html',content)
else:
form=Registration_Form()
content={'form':form}
return render(request,'app/form.html',content)
答案 3 :(得分:5)
根据Spring 4 MVC ResponseEntity.BodyBuilder and ResponseEntity Enhancements Example,它可以写为:
....
return ResponseEntity.ok().build();
....
return ResponseEntity.noContent().build();
更新:
如果返回的值是Optional
,则有一种方便的方法,返回的是ok()
或notFound()
:
return ResponseEntity.of(optional)
答案 4 :(得分:1)
就个人而言,为了处理空响应,我在集成测试中使用了MockMvcResponse对象,如下所示:
MockMvcResponse response = RestAssuredMockMvc.given()
.webAppContextSetup(webApplicationContext)
.when()
.get("/v1/ticket");
assertThat(response.mockHttpServletResponse().getStatus()).isEqualTo(HttpStatus.NO_CONTENT.value());
在我的控制器中,在这样的特定情况下,我返回空响应:
return ResponseEntity.noContent().build();
答案 5 :(得分:1)
对于Spring 5.2+,这对我有效:
@PostMapping("/foo")
ResponseEntity<Void> foo(@PathVariable UUID fooId) {
return fooService.findExam(fooId)
.map(uri -> ResponseEntity.noContent().<Void>build())
.orElse(ResponseEntity.notFound().build());
}