Spring Data Rest 自定义方法返回字符串。

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 - 此时不能注入装配器。


请查看这个问题,看看是否有任何答案可以解决您的问题。 - dubonzi
@dubonzi 不,那并没有帮助。问题不仅仅是换行符。这是一个更大的Spring Data Rest问题。 - Robert
2个回答

4

结论

可以通过使用以下映射和配置来解决问题:

// 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基本上假定当对方法进行请求时,在HTTP请求的ACCEPT头中制定了可接受的媒体类型。否则,它会使用默认媒体类型(可使用RepositoryRestConfigurer#configureRepositoryRestConfiguration进行配置)。

Spring Data REST的默认媒体类型的默认值为application/jsonapplication/hal+json(取决于该默认值,请参阅此处)。这就是为什么在结果中只看到用双引号""括起来的Stringsapplication/json内容类型的原因。字符串正在使用Jackson转换器进行转换(它会用引号括起Strings),而不是使用String转换器。

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

没有深入研究文档,事实上,@BasePathAwareController似乎暗示除了标准的Spring Data Rest实体之外,还有其他可供使用的内容可以利用Spring Data REST。

即使未指定ACCEPT头,我个人仍会将produces类型返回给客户端。如果我要编写一些修改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));
}

0

我有同样的问题,但是我还没有完全解决它。但我希望这些额外的信息至少能够帮到你:

在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,那么就没有办法返回一个普通的字符串了。


1
深入挖掘代码,似乎部分问题在于指定“ACCEPT”头;请参见我的答案。 - Dovmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接