У Spring MVC, як я можу встановити заголовок типу mime, використовуючи @ResponseBody


76

У мене є контролер Spring MVC, який повертає рядок JSON, і я хотів би встановити тип mime на application / json. Як я можу це зробити?

@RequestMapping(method=RequestMethod.GET, value="foo/bar")
@ResponseBody
public String fooBar(){
    return myService.getJson();
}

Бізнес-об’єкти вже доступні у вигляді рядків JSON, тому використання MappingJacksonJsonViewдля мене не є рішенням. @ResponseBodyідеально підходить, але як я можу встановити тип mime?


використовуючи пружину 3.2 та її нову функцію тестування ... чи немає рішення без використання ResponseEntity?
NimChimpsky

Використовуйте HttpHeaders.setContentType - ось кілька прикладів того, як ним користуватися
drorw

@drorw, який був введений через роки після цього запитання :-)
Шон Патрік Флойд

Відповіді:


40

Я б подумав про рефакторинг служби, щоб повернути об'єкт домену, а не рядки JSON, і дозволити Spring обробляти серіалізацію (через те, MappingJacksonHttpMessageConverterяк ви пишете). Станом на весну 3.1, реалізація виглядає досить акуратно:

@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, 
    method = RequestMethod.GET
    value = "/foo/bar")
@ResponseBody
public Bar fooBar(){
    return myService.getBar();
}

Коментарі:

По- перше, <mvc:annotation-driven />чи @EnableWebMvcповинні бути додані до прикладної конфігурації.

Далі, виробляє атрибут @RequestMappingанотації використовується для вказівки типу вмісту відповіді. Отже, для нього слід встановити MediaType.APPLICATION_JSON_VALUE (або "application/json").

Нарешті, слід додати Джексона, щоб будь-яка серіалізація та десеріалізація між Java та JSON автоматично оброблялася до Spring (залежність Джексона виявляється Spring, і MappingJacksonHttpMessageConverterвона буде під капотом).


Ця відповідь повинна бути номер один, нехай Весна зробить за вас роботу !!
chrismarx

8
За словами Javadoc, мета власності виробляє це відповідає Accept заголовка запиту. Це пояснює , чому значення виробляє список значень. Отже, produce не є надійним засобом для додавання заголовків відповідей. На думку javadoc, це не має нічого спільного з заголовками відповідей.
rwitzel

123

Використовуйте ResponseEntityзамість ResponseBody. Таким чином ви маєте доступ до заголовків відповідей і можете встановити відповідний тип вмісту. Відповідно до весняних документів :

HttpEntityПодібно @RequestBodyі @ResponseBody. Окрім отримання доступу до тіла запиту та відповіді, HttpEntity(та підкласу, що відповідає конкретному відповіді ResponseEntity) також дозволяє отримати доступ до заголовків запиту та відповіді

Код буде виглядати так:

@RequestMapping(method=RequestMethod.GET, value="/fooBar")
public ResponseEntity<String> fooBar2() {
    String json = "jsonResponse";
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.setContentType(MediaType.APPLICATION_JSON);
    return new ResponseEntity<String>(json, responseHeaders, HttpStatus.CREATED);
}

привіт, я хочу повернути серіалізований об'єкт, але, використовуючи ваш метод, у мене проблема, він не компілюється, тому що каже: HttpHeaders абстрактний, неможливо створити екземпляр .... чи можете ви пояснити мені, як ви це зробите, маючи повернути серіалізацію об'єкта? тепер це чудово працює, якщо не використовувати ResponseEntity
Lince81

@ Lince81 org.springframework.http.HttpHeaders не є абстрактним класом ( static.springsource.org/spring/docs/3.0.x/javadoc-api/org/… ). Перевірте правильність імпорту та оновлення бібліотек.
Хав'єр Ферреро,

@ Lince81 точка прикладу повертає вже серіалізований об'єкт як String при встановленні іншого Content-Type. Якщо ви хочете, щоб Spring здійснив серіалізацію об’єкта (як XML, JSON тощо), використовуйте @ResponseBody і налаштуйте відповідні конвертери повідомлень (див. Посилання у відповіді)
Хав'єр Ферреро,

1
Просто хотів зазначити, що Spring MVC 3.1 дозволяє вказати значення "виробляє" в RequestMapping. Отже, ви все ще можете використовувати @ResponseBody, але вам потрібно @RequestMapping (method = RequestMethod.GET, value = "/ fooBar", produce = "application / json").
Джо

2
За допомогою цього підходу ви можете мати той самий метод, який повертає різні типи вмісту, чого ви не можете зробити за допомогою "продукує" Spring. Крім того, це найменш нав'язливе, тобто вам не потрібно створювати / створювати власні екземпляри власних MessageConverters.
dewtea

7

Можливо, ви не зможете зробити це за допомогою @ResponseBody, але щось подібне має працювати:

package xxx;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class FooBar {
  @RequestMapping(value="foo/bar", method = RequestMethod.GET)
  public void fooBar(HttpServletResponse response) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    out.write(myService.getJson().getBytes());
    response.setContentType("application/json");
    response.setContentLength(out.size());
    response.getOutputStream().write(out.toByteArray());
    response.getOutputStream().flush();
  }
}

йому потрібно буде писати у відповідь, чи просто встановити заголовок?
Божо

1
чудова відповідь. Якщо тип mime є динамічним, то байтовий масив також повинен бути також динамічним, тому має сенс просто написати байтовий масив без магії @ResponseBody. Єдиним переглядом було б написати OutputStream безпосередньо, замість того, щоб завантажувати його в пам'ять (out.toByteArray).
Джеймс Уоткінс,

@JamesWatkins Вам потрібно завантажити JSON в пам'ять, щоб мати змогу обчислити довжину вмісту. Якщо ваш об'єкт JSON є достатньо малим (а вони зазвичай мають бути!), Це не повинно турбувати.
Стефан Габерль,

Я думаю, що справжня природа запитання така: "Як мені встановити тип mime і одночасно надіслати відповідь, яка не є HTML?" У мене може бути дуже великий файл, який я хочу передати клієнту. Крім того, Довжина вмісту є необов’язковою, і є інші способи її отримання (з бази даних тощо).
Джеймс Уоткінс,


3

Зареєструйтесь org.springframework.http.converter.json.MappingJacksonHttpMessageConverterяк перетворювач повідомлень і поверніть об'єкт безпосередньо з методу.

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="webBindingInitializer">
    <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"/>
  </property>
  <property name="messageConverters">
    <list>
      <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
    </list>
  </property>
</bean>

і контролер:

@RequestMapping(method=RequestMethod.GET, value="foo/bar")
public @ResponseBody Object fooBar(){
    return myService.getActualObject();
}

Для цього потрібна залежність org.springframework:spring-webmvc.


Так, напевно, найкраща практика, але, як я писав, мої об’єкти - це вже рядки JSON, і я просто хочу записати їх із правильним типом mime.
Шон Патрік Флойд,

Які залежності maven для квасолі, на яку ви посилаєтесь?
Райан Монтгомері,

1

Я не думаю, що ви можете, крім response.setContentType(..)


Що означає, що я повинен визначити параметр типу HttpServletResponse?
Шон Патрік Флойд,

Спробував це, але це не спрацювало (тип mime все ще був text / html)
Шон Патрік Флойд,

@ Шон Патрік Флойд дивно. Що таке заголовок "Прийняти" вашого запиту?
Божо

-2

Моя версія реальності. Завантаження файлу HTML і потокове передавання у браузер.

@Controller
@RequestMapping("/")
public class UIController {

    @RequestMapping(value="index", method=RequestMethod.GET, produces = "text/html")
    public @ResponseBody String GetBootupFile() throws IOException  {
        Resource resource = new ClassPathResource("MainPage.html");
        String fileContents = FileUtils.readFileToString(resource.getFile());
        return fileContents;
    }
}


Мало того, що набагато більше роботи, ніж потрібно, але ви порушуєте багато інших речей, які Spring може автоматично доглядати і управляти за вас, а також відкриває проблеми безпеки та інші проблеми.
BrianC
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.