Как игнорировать любые заголовки содержимого http в spring @restcontroller?

У меня есть несколько конечных точек webservice, которые по умолчанию должны предлагать данные json. Поэтому настраиваем следующим образом:

@Configuration
public class ContentNegotiationConfiguration implements WebMvcConfigurer {
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.defaultContentType(MediaType.APPLICATION_JSON);
    }
}

Проблема: теперь я хочу создать конечную точку, предлагающую загрузку файла (следовательно, это не json).

@RestController
public class FileServlet {
   @GetMapping(value = "/docs/{filename}", consumes = MediaType.ALL_VALUE, produces = APPLICATION_OCTET_STREAM_VALUE)
   public Object download(@Pathvariable filename) {
          File file = fileservice.resolve(filename);
          return new FileSystemResource(file);
   }
}

Доступ к этой конечной точке из браузера работает нормально. Я могу скачать файлы.

Но: при использовании собственных клиентов, которые не устанавливают никаких HTTP-заголовков, таких как content-type, accept-header и т. д., Доступ не выполняется с помощью:

WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver: Resolved 
[org.springframework.web.HttpMediaTypeNotAcceptableException:
Could not find acceptable representation]

Все они приводят к исключению:

curl localhost:8080/docs/testfile.txt
curl -O localhost:8080/docs/testfile.txt
wget localhost:8080/docs/testfile.txt

Вероятно, это связано с тем, что я установил тип содержимого по умолчанию на json выше в ContentNegotiationConfiguration. Я не могу изменить это из-за того, что все остальные конечные точки должны быть json по умолчанию.

Вопрос: как я могу явно указать игнорировать этот параметр json по умолчанию на этой единственной конечной точке и всегда просто предлагать поток загрузки?

Обратите внимание, что вы можете указать заголовки с помощью curl, см. Здесь: stackoverflow.com/questions/7172784/…

Arnaud 31.10.2018 12:19

Но я не хочу устанавливать какие-либо заголовки для запроса загрузки файла! Их нужно просто игнорировать. Также curl является здесь лишь примером. С таким же успехом это может быть wget, любой клиент php или java, что угодно.

membersound 31.10.2018 12:22

Потрите результат самостоятельно, включая заголовки, вместо того, чтобы позволить Spring сделать это.

M. Deinum 31.10.2018 13:09

Можете ли вы добавить полный стек? Потому что я хочу увидеть, откуда исходит исключение.

M. Deinum 31.10.2018 13:43

Извините, удалил свой комментарий. Это было вызвано тем, что все еще установлено поле produces. Взгляните на мой ответ, который работает так, как вы предложили!

membersound 31.10.2018 13:48
0
5
871
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Попробуйте настроить ContentNegotiationStrategy с AntPathMatcher, например:

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    //  configurer.defaultContentType(MediaType.APPLICATION_JSON,MediaType.APPLICATION_OCTET_STREAM);


    configurer.defaultContentTypeStrategy(
            new ContentNegotiationStrategy() {
                private UrlPathHelper urlPathHelper = new UrlPathHelper();
                AntPathMatcher antPathMatcher = new AntPathMatcher();
                @Override
                public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
                    HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
                    if (request == null) {
                        return null;
                    }
                    String path = this.urlPathHelper.getLookupPathForRequest(request);
                    if (antPathMatcher.match("/docs/*", path)) {
                        return Collections.singletonList(MediaType.APPLICATION_OCTET_STREAM);
                    } else {
                        return Collections.singletonList(MediaType.APPLICATION_JSON);
                    }

                }
            });

}
Ответ принят как подходящий

С подсказкой @M. Deinum, у меня заработало так:

 @GetMapping(value = "/docs/{filename}")
 public void download(@Pathvariable filename) {
    FileSystemResource file = new FileSystemResource(fileservice.resolve(filename));
    rsp.setHeader("Content-Disposition", "attachment; filename=" + file.getFilename());

    ResourceHttpMessageConverter handler = new ResourceHttpMessageConverter();
    handler.write(file, MediaType.APPLICATION_OCTET_STREAM, new ServletServerHttpResponse(rsp));
 }

Таким образом, запись выполняется непосредственно в поток, минуя согласование содержимого, но при этом полагаясь на класс Spring ResourceHttpMessageConverter, так как самому не нужно реализовывать средство записи ответов.

Другие вопросы по теме