Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data Rest Custom Method return String

I need a custom Method on Spring Data Rest that has some kind of input and returns a String.

@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";
    }
}

By using @BasePathAwareController the endpoint will return "asdf\n", my desired output would be:

asdf
<new line>

Im able to produce this output by using only @Controller, but this breaks the awareness of the base path and i need the PersistentEntityResourceAssembler in other methods of this Controller - the assembler cannot be injected then.

like image 993
Peter Müller Avatar asked Nov 13 '15 07:11

Peter Müller


Video Answer


1 Answers

The Bottom Line

It can be solved by using the following mapping and configuration:

// 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;
    }

}

And by specifying the ACCEPT header when making the request (this is the key!):

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

This yields the following response:

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

The Reason

Upon investigation, it appears that the reason you can never seem to return the unquoted String is due to the behavior of the BasePathAwareHandlerMapping#lookupHandlerMethod function.

lookupHandlerMethod basically assumes that when making a request on a method, that the permissible media types are made with the HTTP request in the ACCEPT header. Otherwise it defaults to the default media type (configurable using RepositoryRestConfigurer#configureRepositoryRestConfiguration).

The default value for the default media type for Spring Data REST is either application/json or application/hal+json (depending on that default value, see here). That's why you are ONLY seeing application/json content types with the double quote, "", around your strings in the result. The String is being converted using the Jackson converter (which encloses Strings with quotes) and not a String converter.

After looking into it, I agree with you that this seems like a strange assumption. That is, the framework shouldn't assume that all requests are always explicitly specify the ACCEPT header with the desired media type (at least, I personally don't always expect to see it) and otherwise assume that all requests should be of the default media type only specifically because of a use case like yours.

Without looking too deeply into the documetation, the fact that @BasePathAwareController seems to imply that more than just the standard Spring Data Rest entities are fair game to use when leveraging Spring Data REST.

I'd personally return the produces type to the client even if the ACCEPT header wasn't specified -- and if I were to write some code to modify the BasePathAwareHandlerMapping, I'd add the following regarding my commented line:

@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));
}
like image 118
Dovmo Avatar answered Sep 30 '22 12:09

Dovmo