用于生成和使用 JSON 的控制器的 Spring RequestMapping
Posted
技术标签:
【中文标题】用于生成和使用 JSON 的控制器的 Spring RequestMapping【英文标题】:Spring RequestMapping for controllers that produce and consume JSON 【发布时间】:2016-05-09 12:12:46 【问题描述】:由于多个 Spring 控制器使用和生成 application/json
,我的代码中到处都是长注释,例如:
@RequestMapping(value = "/foo", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
有没有办法为consumes
和produces
生成具有默认 值的“复合/继承/聚合”注释,这样我就可以改写如下内容:
@JSONRequestMapping(value = "/foo", method = RequestMethod.POST)
我们如何定义像上面的@JSONRequestMapping
这样的东西?注意value
和method
就像在@RequestMapping
中一样传入,如果默认不合适,也可以传入consumes
或produces
。
我需要控制要返回的内容。我想要 produces
/consumes
注释方法,以便获得适当的 Content-Type
标头。
【问题讨论】:
【参考方案1】:您的问题的简单答案是没有Annotation-Inheritance in Java。但是,有一种方法可以以我认为有助于解决您的问题的方式使用 Spring 注释。
@RequestMapping 在类型级别和方法级别均受支持。
当您将@RequestMapping
放在类型级别时,该类中的每个方法的大多数属性都是“继承的”。这是 Spring 参考文档中的 mentioned。查看api docs 以了解在将@RequestMapping
添加到类型时如何处理每个属性的详细信息。我为下面的每个属性总结了这一点:
name
:类型级别的值与方法级别的值连接,使用“#”作为分隔符。
value
:类型级别的值是由方法继承的。
path
:类型级别的值是由方法继承的。
method
:类型级别的值是由方法继承的。
params
:类型级别的值是由方法继承的。
headers
:类型级别的值是由方法继承的。
consumes
:类型级别的值被方法覆盖。
produces
:类型级别的值被方法覆盖。
这是一个简短的控制器示例,展示了如何使用它:
package com.example;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(path = "/",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.GET, RequestMethod.POST)
public class JsonProducingEndpoint
private FooService fooService;
@RequestMapping(path = "/foo", method = RequestMethod.POST)
public String postAFoo(@RequestBody ThisIsAFoo theFoo)
fooService.saveTheFoo(theFoo);
return "http://myservice.com/foo/1";
@RequestMapping(path = "/foo/id", method = RequestMethod.GET)
public ThisIsAFoo getAFoo(@PathVariable String id)
ThisIsAFoo foo = fooService.getAFoo(id);
return foo;
@RequestMapping(path = "/foo/id", produces = MediaType.APPLICATION_XML_VALUE, method = RequestMethod.GET)
public ThisIsAFooXML getAFooXml(@PathVariable String id)
ThisIsAFooXML foo = fooService.getAFoo(id);
return foo;
【讨论】:
从技术上讲,Ali Dehghani 的回答最好地回答了我提出的问题,因此我将其标记为已接受。但我最终使用了你的建议,在某种意义上看起来更干净,所以我也给你一个赏金,(+100,因为我不能做两次 +50)。谢谢。 问题:我的场景和你一模一样,但是不能接受返回字符串的方法(415),因为它不是 JSON。你没有这个问题吗?对象被正确反序列化,因为我们在模型的字段上有 Jackson 和@Jsonproperty
。
@WesternGun 不确定我是否关注。 java 方法应该返回一个可序列化的对象,而不是一个字符串。在我的示例中,只有 POST 方法返回一个字符串,因为它返回创建资源的位置。可能值得打开您自己的问题。【参考方案2】:
从 Spring 4.2.x 开始,您可以创建自定义映射注释,使用 @RequestMapping
作为元注释。所以:
有没有办法产生“复合/继承/聚合” 带有消耗和生产的默认值的注释,这样我 可以改为这样写:
@JSONRequestMapping(value = "/foo", method = RequestMethod.POST)
是的,有这样的方法。您可以创建如下元注释:
@Target(ElementType.METHOD, ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@RequestMapping(consumes = "application/json", produces = "application/json")
public @interface JsonRequestMapping
@AliasFor(annotation = RequestMapping.class, attribute = "value")
String[] value() default ;
@AliasFor(annotation = RequestMapping.class, attribute = "method")
RequestMethod[] method() default ;
@AliasFor(annotation = RequestMapping.class, attribute = "params")
String[] params() default ;
@AliasFor(annotation = RequestMapping.class, attribute = "headers")
String[] headers() default ;
@AliasFor(annotation = RequestMapping.class, attribute = "consumes")
String[] consumes() default ;
@AliasFor(annotation = RequestMapping.class, attribute = "produces")
String[] produces() default ;
然后您可以使用默认设置,甚至可以根据需要覆盖它们:
@JsonRequestMapping(method = POST)
public String defaultSettings()
return "Default settings";
@JsonRequestMapping(value = "/override", method = PUT, produces = "text/plain")
public String overrideSome(@RequestBody String json)
return json;
您可以在 spring 的 javadoc 和 github wiki 中阅读有关 AliasFor
的更多信息。
【讨论】:
这真的很有趣;不知道这个新功能。 JavaDoc for @AliasFor 真的很好。还有一个wiki page更详细地描述了Spring的注解模型。 谢谢!这看起来像我正在寻找的信息。在public @interface JsonRequestMapping
中,是否需要重新声明name
、value
、path
、method
、params
、headers
?我只对consumes
、produces
的默认值感兴趣。
是的,如果你想覆盖它们在RequestMapping
中的对应值,你应该重新声明它们。【参考方案3】:
您根本不需要配置消费或生产属性。 Spring 将根据以下因素自动提供 JSON。
请求的接受头是application/json @ResponseBody 带注释的方法 类路径上的杰克逊库您还应该遵循 Wim 的建议并使用 @RestController 注释定义您的控制器。这将使您免于使用 @ResponseBody
注释每个请求方法这种方法的另一个好处是,如果客户想要 XML 而不是 JSON,他们会得到它。他们只需要在接受标头中指定 xml。
【讨论】:
对我来说完美无缺。我什至删除了消费和生产【参考方案4】:您可以使用@RestController
代替@Controller
注释。
【讨论】:
其实是@Controller
和@ResponseBody
的组合,所以和Content Type没有任何共同之处【参考方案5】:
Spring 中有 2 个注解:@RequestBody 和 @ResponseBody。这些注释分别消费和生成 JSON。更多信息here。
【讨论】:
这是有趣的信息,但不是我想要的。我需要控制我要返回的内容。我想要produces
/consumes
注释方法,以便获得适当的 Content-Type
标头。以上是关于用于生成和使用 JSON 的控制器的 Spring RequestMapping的主要内容,如果未能解决你的问题,请参考以下文章
用于发布 JSON 的标准 spring mvc 控制器的 Katharsis 返回 400
Spring 4 mvc REST XML 和 JSON 响应
我如何在 Spring Boot 中更改 Repository.findAll() 生成的 json 数组的格式