【问题标题】:Spring Data Rest Custom Method return StringSpring Data Rest 自定义方法返回字符串
【发布时间】:2016-02-14 17:43:10
【问题描述】:

我需要一个 Spring Data Rest 上的自定义方法,它具有某种输入并返回一个字符串。

@BasePathAwareController
@RequestMapping(value = "/businessActions")
public class BusinessActionController implements ResourceProcessor<RepositoryLinksResource> {


    /**
     * This BusinessAction's purpose is: Generiert für ein Modell das entsprechende Barrakuda-File.
     * It returns one String.
     */
    @RequestMapping(value = "/modellGenerieren", method = RequestMethod.GET)
    public String modellGenerieren(@Param(value="project") String project) throws IOException {
        // Get project by id from repository and map to string.
        return "asdf\n";
    }
}

通过使用@BasePathAwareController,端点将返回"asdf\n",我想要的输出是:

asdf
<new line>

我能够仅使用@Controller 来生成此输出,但这会破坏基本路径的意识,我需要在此控制器的其他方法中使用PersistentEntityResourceAssembler - 那时无法注入汇编程序。

【问题讨论】:

  • 看看this question,看看是否有任何答案能解决您的问题。
  • @dubonzi 不,这没有帮助。问题不仅仅是换行符。这是一个更大的 spring 数据休息问题。

标签: spring spring-data spring-data-rest


【解决方案1】:

底线

可以通过如下映射和配置来解决:

// The OP's original controller with a small tweak
@BasePathAwareController
@RequestMapping("/businessActions")
public class MyCustomRestEndpoint {

    // Let's specify the #produces type as text/plain (rather than the Spring Data REST JSON default)
    @GetMapping(value = "/modellGenerieren", produces = MediaType.TEXT_PLAIN_VALUE)
    public @ResponseBody ResponseEntity<String> modellGenerieren(@Param(value="project") String project) throws IOException {
        // Get project by id from repository and map to string.
        return ResponseEntity.ok("A String!");
    }

}

@Configuration
public class PlainTextConfiguration implements RepositoryRestConfigurer {

    // Allow for plain String responses from Spring via the `text/plain` content type
    @Override
    public void configureHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters)
    {
        StringHttpMessageConverter converter = new StringHttpMessageConverter();
        converter.setSupportedMediaTypes(configureMediaTypes());

        messageConverters.add(converter);

    }

    private List<MediaType> configureMediaTypes() {
        List<MediaType> mediaTypes = new ArrayList<>();
        mediaTypes.add(MediaType.TEXT_PLAIN);
        mediaTypes.add(MediaType.parseMediaType("text/plain;charset=iso-8859-1"));
        mediaTypes.add(MediaType.parseMediaType("text/plain;charset=UTF-8"));
        mediaTypes.add(MediaType.parseMediaType("text/plain;charset=UTF-16"));
        return mediaTypes;
    }

}

并且通过在发出请求时指定ACCEPT 标头(这是关键!):

GET http://localhost:8080/api/businessActions/modellGenerieren
Content-Type: text/plain
Accept: text/plain

这会产生以下响应:

GET http://localhost:8080/api/businessActions/modellGenerieren

HTTP/1.1 200 OK
Date: Mon, 24 Dec 2018 06:21:10 GMT
Content-Type: text/plain;charset=iso-8859-1
Accept-Charset: ... <large charset>
Content-Length: 9

A String!

Response code: 200 (OK); Time: 151ms; Content length: 9 bytes

原因

经调查,您似乎永远无法返回未加引号的字符串的原因似乎是 BasePathAwareHandlerMapping#lookupHandlerMethod 函数的行为。

lookupHandlerMethod 基本上假设在对方法发出请求时,允许的媒体类型是通过 ACCEPT 标头中的 HTTP 请求发出的。否则默认为默认媒体类型(可使用RepositoryRestConfigurer#configureRepositoryRestConfiguration 配置)。

Spring Data REST 的默认媒体类型的默认值为 application/jsonapplication/hal+json(取决于该默认值,see here)。这就是为什么您只能在结果中的字符串周围看到带有双引号 ""application/json 内容类型。 String 正在使用 Jackson 转换器(用引号将 Strings 括起来)而不是 String 转换器进行转换。

在调查之后,我同意你的看法,这似乎是一个奇怪的假设。也就是说,框架不应该假设所有请求总是明确指定具有所需媒体类型的ACCEPT 标头(至少,我个人并不总是希望看到它),否则假设所有请求都应该是默认媒体类型只是因为像你这样的用例。

不用太深入研究文档,@BasePathAwareController 这一事实似乎暗示,在利用 Spring Data REST 时,不仅可以使用标准的 Spring Data Rest 实体。

即使没有指定 ACCEPT 标头,我也会亲自将产品类型返回给客户端 - 如果我要编写一些代码来修改 BasePathAwareHandlerMapping,我会添加以下关于我的注释行:

@Override
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {

    ...

    if (!defaultFound) {
        mediaTypes.add(configuration.getDefaultMediaType());
    }

    // Lookup the Handler Method for this request
    // If no media types are specific in the ACCEPT header of this request...
    // Then look and see if the method has a #produces specified and define that as the ACCEPT type

    super.lookupHandlerMethod(lookupPath, new CustomAcceptHeaderHttpServletRequest(request, mediaTypes));
}

【讨论】:

    【解决方案2】:

    我也有同样的问题。而且我还没有完全解决。但我希望这些额外信息至少会有所帮助:

    Spring 和 spring-data-rest 中有 4 个注释,恕我直言,它们混杂在一起。 (例如参见Bug

    Spring Data Rest controllers: behaviour and usage of @BasePathAwareController, @RepositoryRestController, @Controller and @RestController

    如果您使用@BasePathAwareController,您将获得 Spring-data-rest 的所有魔力(请参阅SDR Doc)=> 但是您不能返回一个简单的字符串。 (至少到目前为止我还没有找到方法。)

    如果您使用@RestController,那么您的端点完全独立于 SDR。

    如果您希望 @RestController 在与 SDR API 的其余部分相同的路径前缀下公开,那么您可以使用:

    @RestController
    @RequestMapping("${spring.data.rest.base-path}")
    public class MyCustomRestEndpoint { ... }
    

    这会从 application.properties

    中读取路径前缀
    spring.data.rest.base-path=/api/v17
    

    然后你可以返回一个普通的字符串。

    但是如果你使用@BasePathAwareController,正如OP所说,因为你需要PersistentEntityResourceAssembler,那么就没有办法返回一个普通的字符串。

    【讨论】:

    • 深入研究代码,似乎部分是指定ACCEPT 标头;看我的回答
    猜你喜欢
    • 2017-03-06
    • 2018-10-07
    • 2016-03-26
    • 2018-04-18
    • 2015-07-03
    • 2014-02-02
    • 2014-11-27
    • 2015-03-25
    • 1970-01-01
    相关资源
    最近更新 更多