Trong Spring MVC, làm cách nào để đặt tiêu đề kiểu mime khi sử dụng @ResponseBody


76

Tôi có Bộ điều khiển Spring MVC trả về Chuỗi JSON và tôi muốn đặt mimetype thành application / json. Làm thế nào tôi có thể làm điều đó?

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

Các đối tượng nghiệp vụ đã có sẵn dưới dạng chuỗi JSON, vì vậy việc sử dụng MappingJacksonJsonViewkhông phải là giải pháp cho tôi. @ResponseBodylà hoàn hảo, nhưng làm thế nào tôi có thể đặt mimetype?


sử dụng spring 3.2 và tính năng thử nghiệm mới của nó ... không có giải pháp nào mà không sử dụng ResponseEntity?
NimChimpsky 22/12/12

Sử dụng HttpHeaders.setContentType - đây là một số ví dụ về cách sử dụng nó
drorw

@drorw mà đã được giới thiệu năm sau khi câu hỏi này :-)
Sean Patrick Floyd

Câu trả lời:


40

Tôi sẽ cân nhắc việc cấu trúc lại dịch vụ để trả về đối tượng miền của bạn thay vì các chuỗi JSON và để Spring xử lý việc tuần tự hóa (thông qua MappingJacksonHttpMessageConverternhư bạn viết). Kể từ Spring 3.1, việc triển khai trông khá gọn gàng:

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

Bình luận:

Đầu tiên, <mvc:annotation-driven />hoặc @EnableWebMvcphải được thêm vào cấu hình ứng dụng của bạn.

Tiếp theo, thuộc tính sản xuất của @RequestMappingchú thích được sử dụng để chỉ định loại nội dung của phản hồi. Do đó, nó phải được đặt thành MediaType.APPLICATION_JSON_VALUE (hoặc "application/json").

Cuối cùng, Jackson phải được thêm vào để bất kỳ tuần tự hóa và hủy tuần tự hóa nào giữa Java và JSON sẽ được Spring xử lý tự động (sự phụ thuộc Jackson được phát hiện bởi Spring và MappingJacksonHttpMessageConvertersẽ được kiểm tra).


Câu trả lời này nên là số một, hãy để Spring làm thay bạn !!
chrismarx

8
Theo javadoc, mục đích của tài sản tạo ra là để phù hợp với Accept header của yêu cầu. Điều này giải thích tại sao giá trị của sản phẩm là một danh sách các giá trị. Do đó, sản xuất không phải là phương tiện đáng tin cậy để thêm bất kỳ tiêu đề phản hồi nào. Theo javadoc, nó không liên quan gì đến tiêu đề phản hồi.
rwitzel

123

Sử dụng ResponseEntitythay vì ResponseBody. Bằng cách này, bạn có quyền truy cập vào các tiêu đề phản hồi và bạn có thể đặt loại nội dung phù hợp. Theo tài liệu Mùa xuân :

Tương HttpEntitytự với @RequestBody@ResponseBody. Bên cạnh việc truy cập vào phần thân yêu cầu và phản hồi, HttpEntity(và lớp con dành riêng cho phản hồi ResponseEntity) cũng cho phép truy cập vào các tiêu đề yêu cầu và phản hồi

Mã sẽ giống như sau:

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

xin chào, tôi muốn trả về một đối tượng được tuần tự hóa nhưng sử dụng phương pháp của bạn, tôi gặp sự cố, nó không biên dịch được vì nó nói: HttpHeaders là trừu tượng không thể được khởi tạo .... bạn có thể giải thích cho tôi cách bạn làm việc này như thế nào để trả về tuần tự hóa của một đối tượng? bây giờ nó hoạt động tốt nếu không sử dụng ResponseEntity
Lince81

@ Lince81 org.springframework.http.HttpHeaders không phải là một lớp trừu tượng ( static.springsource.org/spring/docs/3.0.x/javadoc-api/org/… ). Xem liệu nhập của bạn có đúng không và các thư viện của bạn đã được cập nhật hay chưa.
Javier Ferrero

@ Lince81, điểm của ví dụ là trả về một đối tượng đã được tuần tự hóa dưới dạng Chuỗi trong khi thiết lập Loại-Nội dung khác. Nếu bạn muốn mùa xuân để serialize một Object (như XML, JSON, vv) sử dụng @ResponseBody và cấu hình MessageConverters thích hợp (xem liên kết trong câu trả lời)
Javier Ferrero

1
Chỉ muốn chỉ ra rằng Spring MVC 3.1 cho phép bạn chỉ định một giá trị cho "sản xuất" trong RequestMapping. Vì vậy, bạn vẫn có thể sử dụng @ResponseBody, nhưng bạn cần @RequestMapping (method = RequestMethod.GET, value = "/ fooBar", production = "application / json").
Joe

2
Với cách tiếp cận này, bạn có thể có cùng một phương thức trả về các kiểu nội dung khác nhau, điều mà bạn không thể làm với "sản phẩm" của Spring. Ngoài ra, đây là cách ít xâm nhập nhất, tức là bạn không phải tạo / khởi tạo MessageConverters của riêng mình.
dewtea

7

Bạn có thể không thực hiện được với @ResponseBody, nhưng một cái gì đó như thế này sẽ hoạt động:

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();
  }
}

anh ấy cần viết thư cho phản hồi hay chỉ đặt tiêu đề là xong?
Bozho

1
câu trả lời chính xác. Nếu kiểu mime là động, thì mảng byte chắc chắn cũng phải động, vì vậy sẽ hợp lý nếu chỉ viết mảng byte mà không cần đến phép thuật của @ResponseBody. Bản sửa đổi duy nhất sẽ là ghi OutputStream trực tiếp, thay vì tải nó vào bộ nhớ (out.toByteArray).
James Watkins

@JamesWatkins Bạn cần tải JSON vào bộ nhớ để có thể tính độ dài nội dung. Nếu đối tượng JSON của bạn có kích thước hợp lý nhỏ (và chúng thường phải như vậy!) Thì điều này không cần quan tâm.
Stefan Haberl

Tôi nghĩ bản chất thực sự của câu hỏi là: "Làm cách nào để đặt loại kịch câm và gửi phản hồi không phải HTML cùng một lúc?" Tôi có thể có một tệp rất lớn mà tôi muốn phát trực tuyến tới máy khách. Ngoài ra, Độ dài nội dung là tùy chọn và có nhiều cách khác để lấy nó (từ cơ sở dữ liệu, v.v.).
James Watkins


3

Đăng ký org.springframework.http.converter.json.MappingJacksonHttpMessageConverterlàm trình chuyển đổi thông báo và trả về đối tượng trực tiếp từ phương thức.

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

và bộ điều khiển:

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

Điều này đòi hỏi sự phụ thuộc org.springframework:spring-webmvc.


Vâng, có lẽ là một phương pháp hay nhất, nhưng khi tôi viết, các đối tượng của tôi đã là các chuỗi JSON và tôi chỉ muốn viết chúng ra với đúng kiểu kịch câm.
Sean Patrick Floyd

Các phụ thuộc maven cho các bean mà bạn đang tham khảo là gì?
Ryan Montgomery

1

Tôi không nghĩ bạn có thể làm được, ngoại trừ response.setContentType(..)


Có nghĩa là tôi phải xác định một tham số kiểu HttpServletResponse?
Sean Patrick Floyd

Cố gắng đó nhưng nó đã không làm việc (loại mime vẫn là text / html)
Sean Patrick Floyd

@Sean Patrick Floyd lạ. Tiêu đề "Chấp nhận" yêu cầu của bạn là gì?
Bozho

-2

Phiên bản thực tế của tôi. Đang tải tệp HTML và truyền trực tuyến tới trình duyệt.

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

bạn có biết rằng Spring MVC có thể cung cấp tài nguyên tĩnh không?
Sean Patrick Floyd

Không chỉ là nhiều công việc hơn mức cần thiết, mà bạn còn vi phạm rất nhiều thứ khác mà Spring có thể tự động chăm sóc và quản lý cho bạn, cũng như mở ra các vấn đề bảo mật và các điểm hỏng hóc khác.
BrianC
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.