Wie kann ich in Spring MVC den MIME-Header festlegen, wenn ich @ResponseBody verwende?

76

Ich habe einen Spring MVC Controller, der einen JSON-String zurückgibt, und ich möchte den Mimetyp auf application / json setzen. Wie kann ich das machen?

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

Die Geschäftsobjekte sind bereits als JSON-Zeichenfolgen verfügbar, daher MappingJacksonJsonViewist die Verwendung für mich nicht die Lösung. @ResponseBodyist perfekt, aber wie kann ich den Mimetyp einstellen?

Sean Patrick Floyd
quelle
Verwenden von Spring 3.2 und seiner neuen Testfunktion ... Gibt es keine Lösung ohne ResponseEntity?
NimChimpsky
Verwenden Sie HttpHeaders.setContentType - hier sind einige Beispiele für die Verwendung
drorw
@drorw, die Jahre nach dieser Frage eingeführt wurde :-)
Sean Patrick Floyd

Antworten:

40

Ich würde in Betracht ziehen, den Dienst so umzugestalten, dass Ihr Domänenobjekt anstelle von JSON-Zeichenfolgen zurückgegeben wird, und Spring die Serialisierung übernehmen zu lassen (über das, MappingJacksonHttpMessageConverterwährend Sie schreiben). Ab Frühjahr 3.1 sieht die Implementierung recht ordentlich aus:

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

Bemerkungen:

Zunächst wird der <mvc:annotation-driven />oder die @EnableWebMvcmuss hinzugefügt , um Ihre Anwendung Config.

Als Nächstes wird das Erzeugungsattribut der @RequestMappingAnmerkung verwendet, um den Inhaltstyp der Antwort anzugeben. Folglich sollte es auf MediaType.APPLICATION_JSON_VALUE (oder "application/json") gesetzt werden.

Zuletzt muss Jackson hinzugefügt werden, damit jede Serialisierung und De-Serialisierung zwischen Java und JSON automatisch von Spring verarbeitet wird (die Jackson-Abhängigkeit wird von Spring erkannt und MappingJacksonHttpMessageConverterbefindet sich unter der Haube).

matsev
quelle
Diese Antwort sollte die Nummer eins sein, lass Spring die Arbeit für dich erledigen !!
Chrismarx
8
Nach dem javadoc, den Zweck der Eigenschaft erzeugt ist , die übereinstimmen Accept - Header der Anforderung. Dies erklärt, warum der Wert von produzieren eine Liste von Werten ist. Daher ist " Erzeugen" kein zuverlässiges Mittel zum Hinzufügen von Antwortheadern. Laut Javadoc hat dies nichts mit Antwortheadern zu tun.
Rwitzel
123

Verwenden Sie ResponseEntityanstelle von ResponseBody. Auf diese Weise haben Sie Zugriff auf die Antwortheader und können den entsprechenden Inhaltstyp festlegen. Laut den Frühlingsdokumenten :

Das HttpEntityist ähnlich wie @RequestBodyund @ResponseBody. Neben dem Zugriff auf den Anforderungs- und Antworttext HttpEntity(und die antwortspezifische Unterklasse ResponseEntity) können Sie auch auf die Anforderungs- und Antwortheader zugreifen

Der Code sieht folgendermaßen aus:

@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);
}
Javier Ferrero
quelle
Hallo, ich möchte ein serialisiertes Objekt zurückgeben, aber mit Ihrer Methode habe ich ein Problem, es wird nicht kompiliert, weil es sagt: HttpHeaders ist abstrakt kann nicht instanziiert werden .... können Sie mir auch erklären, wie Sie dies tun würden die Serialisierung eines Objekts zurückgeben? Jetzt funktioniert es
einwandfrei,
@ Lince81 org.springframework.http.HttpHeaders ist keine abstrakte Klasse ( static.springsource.org/spring/docs/3.0.x/javadoc-api/org/… ). Überprüfen Sie, ob Ihr Import korrekt ist und Ihre Bibliotheken aktualisiert wurden.
Javier Ferrero
@ Lince81 Der Punkt des Beispiels ist die Rückgabe eines bereits serialisierten Objekts als String, während ein anderer Inhaltstyp festgelegt wird. Wenn Spring ein Objekt serialisieren soll (als XML, JSON usw.), verwenden Sie @ResponseBody und konfigurieren Sie die entsprechenden MessageConverters (siehe Link in der Antwort)
Javier Ferrero
1
Ich wollte nur darauf hinweisen, dass Sie mit Spring MVC 3.1 einen Wert für "produziert" in der RequestMapping angeben können. Sie können also weiterhin @ResponseBody verwenden, benötigen jedoch @RequestMapping (method = RequestMethod.GET, value = "/ fooBar", produziert = "application / json").
Joe
2
Mit diesem Ansatz kann dieselbe Methode unterschiedliche Inhaltstypen zurückgeben, was Sie mit Spring's "Produces" nicht tun können. Dies ist auch am wenigsten aufdringlich, dh Sie müssen keine eigenen MessageConverter erstellen / instanziieren.
Dewtea
7

Möglicherweise können Sie dies nicht mit @ResponseBody tun, aber so etwas sollte funktionieren:

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();
  }
}
GriffeyDog
quelle
Müsste er auf die Antwort schreiben oder würde es nur reichen, den Header zu setzen?
Bozho
1
gute Antwort. Wenn der MIME-Typ dynamisch ist, muss das Byte-Array sicherlich auch dynamisch sein. Daher ist es sinnvoll, das Byte-Array nur ohne die Magie von @ResponseBody zu schreiben. Die einzige Überarbeitung wäre, den OutputStream direkt zu schreiben, anstatt ihn in den Speicher zu laden (out.toByteArray).
James Watkins
@JamesWatkins Sie müssen den JSON in den Speicher laden, um die Inhaltslänge berechnen zu können. Wenn Ihr JSON-Objekt angemessen klein ist (und dies normalerweise sein sollte!), Sollte dies kein Problem darstellen.
Stefan Haberl
Ich denke, die wahre Natur der Frage ist: "Wie stelle ich den MIME-Typ ein und sende gleichzeitig eine Nicht-HTML-Antwort?" Ich könnte eine sehr große Datei haben, die ich zum Client streamen möchte. Die Inhaltslänge ist optional und es gibt andere Möglichkeiten, sie abzuleiten (aus der Datenbank usw.).
James Watkins
3

Registrieren Sie sich org.springframework.http.converter.json.MappingJacksonHttpMessageConverterals Nachrichtenkonverter und geben Sie das Objekt direkt von der Methode zurück.

<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>

und der Controller:

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

Dies erfordert die Abhängigkeit org.springframework:spring-webmvc.

OrangeDog
quelle
Ja, wahrscheinlich eine bewährte Methode, aber während ich schrieb, sind meine Objekte bereits JSON-Zeichenfolgen und ich möchte sie nur mit dem richtigen MIME-Typ ausschreiben.
Sean Patrick Floyd
Was sind die Maven-Abhängigkeiten für die Beans, auf die Sie verweisen?
Ryan Montgomery
1

Ich glaube nicht, dass du es kannst, abgesehen davon response.setContentType(..)

Bozho
quelle
Was bedeutet, dass ich einen Parameter vom Typ HttpServletResponse definieren muss?
Sean Patrick Floyd
Versuchte das, aber es funktionierte nicht (MIME-Typ war immer noch Text / HTML)
Sean Patrick Floyd
@ Sean Patrick Floyd seltsam. Was ist der "Accept" -Header Ihrer Anfrage?
Bozho
-2

Meine Version der Realität. Laden einer HTML-Datei und Streaming in den Browser.

@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;
    }
}
R Keene
quelle
Sie wissen, dass Spring MVC sofort statische Ressourcen bereitstellen kann ?
Sean Patrick Floyd
Das ist nicht nur viel mehr Arbeit als nötig, sondern Sie brechen auch viele andere Dinge, die Spring automatisch für Sie erledigen und verwalten kann, und eröffnen Sicherheitsprobleme und andere Fehlerquellen.
BrianC