【问题标题】:How to make Spring MVC return CSV as convenient as return JSON如何使 Spring MVC 返回 CSV 与返回 JSON 一样方便
【发布时间】:2014-01-05 19:08:33
【问题描述】:

我知道 Spring MVC 可以轻松返回 Json 格式的模型;但是,我尝试了完全不同的方法来返回 CSV 格式的模型(通过杰克逊),但无法成功。

我该怎么办?

我附上我的模型代码、控制器代码和 gradle.build 如下:

非常感谢!

型号:

@JsonPropertyOrder({ "staffName", "name" })
public class Greeter
{
    String name;
    String staffName[];

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public String[] getStaffName()
    {
        return staffName;
    }

    public void setStaffName(String[] staffName)
    {
        this.staffName = staffName;
    }
}

控制器:

@Controller
public class GreetingController {

    @RequestMapping(value = "/greeter/json", method = RequestMethod.GET)
    public @ResponseBody
    Greeter getGreeterInJSON() {

        Greeter greeter = new Greeter();

        greeter.setName("default");
        greeter.setStaffName(new String[] { "ye", "lichi" });

        return greeter;

    }

    @RequestMapping(value = "/greeter/csv", method = RequestMethod.GET, consumes = "text/csv")
    public @ResponseBody
    Greeter getGreeterInCSV(HttpServletResponse response) {
        Greeter greeter = new Greeter();

        greeter.setName("default");
        greeter.setStaffName(new String[] { "ye", "lichi" });

        CsvMapper mapper = new CsvMapper();
        CsvSchema schema = mapper.schemaFor(Greeter.class);

        ObjectWriter writer = mapper.writer(schema.withLineSeparator("\n"));

        File greeterCSV = new File("greeterCSV.csv");

        try {
            writer.writeValue(greeterCSV, greeter);
        } catch (JsonGenerationException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return greeter;
    }
}

build.gradle 依赖项:

dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'

    compile ('org.springframework:spring-context:4.0.0.RELEASE')
    compile("org.springframework.boot:spring-boot-starter-web:0.5.0.M6")
    compile("org.thymeleaf:thymeleaf-spring3:2.0.17")

    // compile "org.codehaus.jackson:jackson-mapper-asl:1.9.13"
    compile 'com.fasterxml.jackson.core:jackson-databind:2.3.0'
    compile 'com.fasterxml.jackson.core:jackson-core:2.3.0'
    compile 'com.fasterxml.jackson.core:jackson-annotations:2.3.0'

    compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-csv:2.3.0'

    testCompile group: 'junit', name: 'junit', version: '4.+'
    testCompile "org.mockito:mockito-all:1.9.5"
}

编辑:


Tomcat 错误:

HTTP 状态 415 -

输入状态报告

消息

说明服务器拒绝了这个请求,因为请求实体的格式不受所请求方法的请求资源支持。

【问题讨论】:

  • 在什么情况下不能正常工作?请提供任何堆栈跟踪等。

标签: spring spring-mvc gradle jackson


【解决方案1】:

你不想消费而是生产 csv。您拥有它的方式,服务期望输入以 csv 格式提供,这就是它抱怨“请求实体的格式不受支持”的原因。这里的关键是“请求”——它需要一些 csv 格式的输入。 将“消费”更改为“生产”应该可以解决您的问题。

【讨论】:

    【解决方案2】:

    我一直在寻找这个问题的答案,但遗憾的是没有找到,所以我将在这里举个例子。

    您需要添加一个 CSV 消息转换器。这个@Configuration 是这样做的:

    @Configuration
    public class CsvConfiguration {
    
      @Bean
      JacksonToCsvHttpMessageConverter csvMessageConverter() {
        CsvMapper mapper = new CsvMapper();
        // configure mapper here
        mapper.registerModule(new JavaTimeModule());
        return new JacksonToCsvHttpMessageConverter(mapper);
      }
    
    }
    

    还有一个转换器本身:

    请注意,此简化版本不支持过滤器或视图。但是,如果您研究重写方法writeInternal,您应该能够添加这些功能,如果您需要它们。此外,不支持空集合,但如果您想支持空系列,可以使用空数组。

    public class JacksonToCsvHttpMessageConverter extends AbstractJackson2HttpMessageConverter {
    
        protected JacksonToCsvHttpMessageConverter(ObjectMapper objectMapper) {
            super(objectMapper, MediaType.parseMediaType("text/csv"));
        }
    
        @Override
        protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
            if (object instanceof MappingJacksonValue) object = ((MappingJacksonValue) object).getValue();
            CsvMapper mapper = (CsvMapper) getObjectMapper();
            Class<?> klass = getType(object);
            CsvSchema schema = mapper.schemaFor(klass).withHeader();
            JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
            try (CsvGenerator generator = mapper.getFactory().createGenerator(outputMessage.getBody(), encoding)) {
                ObjectWriter writer = mapper.writer().with(schema);
                writer.writeValue(generator, object);
            }
        }
    
        private Class<?> getType(Object object) {
            if (object instanceof Collection) {
                Collection<?> c = (Collection<?>) object;
                return c.iterator().next().getClass();
            }
            if (object.getClass().isArray()) return object.getClass().getComponentType();
            return object.getClass();
        }
    
    }
    

    这将使所有传统的控制器方法能够以Accept 标头的方式返回CSV。 Accept: application/json 将产生 JSON,而 Accept: text/csv 将产生 CSV。当然,所有 CsvMapper 约束都将适用,因此没有嵌套对象等。

    @RestController
    public class CsvController {
    
        @GetMapping("/records*")
        public CsvRecord[] getRecords() {
            return new CsvRecord[] { new CsvRecord(), new CsvRecord() };
        }
    
        @GetMapping("/record")
        public CsvRecord getRecord() {
            return new CsvRecord();
        }
    
        @JsonAutoDetect(fieldVisibility = Visibility.PUBLIC_ONLY)
        public static class CsvRecord {
            public String name = "aa";
        }
    
    }
    

    使用此配置将导致您的应用为 Accept: */* 请求返回 CSV。这很少是有意的,因此您可以通过添加此配置将默认内容类型设置为 JSON:

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

    【讨论】:

      【解决方案3】:

      您正在使用 GET,因此您期望响应是 CSV,因此请确保使用 producer = "text/csv" 而不是 consumes = "text/csv") 这里:-

      @RequestMapping(value = "/greeter/csv", method = RequestMethod.GET, consumes = "text/csv")
          public @ResponseBody
      

      我们在发送一些时使用消耗

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-16
        • 1970-01-01
        • 2015-07-24
        • 2019-10-22
        • 1970-01-01
        • 1970-01-01
        • 2013-11-29
        • 1970-01-01
        相关资源
        最近更新 更多