Spring Rest 文档。片段生成时 UTF-8 中间字节无效 [重复]

Posted

技术标签:

【中文标题】Spring Rest 文档。片段生成时 UTF-8 中间字节无效 [重复]【英文标题】:Spring Rest Docs. Invalid UTF-8 middle byte while snippets generation [duplicate] 【发布时间】:2016-04-21 07:34:55 【问题描述】:

请帮助我解决我在使用 Spring Rest 文档时遇到的问题。 我已经从 Spring 手册中进行了所有必需的设置。 我写了 Spring MVC 测试。这是代码。奇怪的符号是俄语。

@WebAppConfiguration
@ContextConfiguration(classes = TestConfiguration.class)
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test")
@Slf4j
public class ProductApiControllerTest 

    protected static final long TEST_PRODUCT_ID_1 = 11_071_076_993L;
    protected static final long TEST_PRODUCT_ID_2 = 21_071_076_994L;

    public static final Product TEST_PRODUCT_1 = Product.builder()
            .id(TEST_PRODUCT_ID_1)
            .productId(TEST_PRODUCT_ID_1)
            .name("WRX3000")
            .fullName("WRX3000")
            .regionPickupAvailable(Arrays.asList("a100"))
            .regionDeliveryAvailable(Arrays.asList("b200"))
            .categoryId(100500)
            .categoryName("Телевизоры")
            .categories(Arrays.asList(10L, 20L, 30L))
            .brandId(200300)
            .brandName("Samsung")
            .description("AMOLED HD Телевизор 4 поколения")
            .prices(Arrays.asList(Price.builder().baseStore("b200").priceId(777).priceValue(2990d).oldPriceValue(3490d).build()))
            .build();

    @Mock
    private SearchService searchServiceMock;

    @InjectMocks
    private ProductApiController controller;

    private MockMvc mockMvc;
    private RestDocumentationResultHandler document;


    @Rule
    public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");

    @Before
    public void init() 
        MockitoAnnotations.initMocks(this);
        this.document = document("method-name", preprocessResponse(prettyPrint()));
        mockMvc = MockMvcBuilders.standaloneSetup(controller)
                .apply(documentationConfiguration(this.restDocumentation).snippets().withEncoding("UTF-8"))
                .alwaysDo(document)
                .build();
    

    @Test
    public void testGetProduct() throws Exception 
        log.info("Test getProduct(..) from ProductApiController");
        when(searchServiceMock.findByProductId(TEST_PRODUCT_ID_1, "b200")).thenReturn(TEST_PRODUCT_1);
        this.document.snippets(responseFields(
                fieldWithPath("id").description("Идентификатор для служебных целей поисковой машины."),
                fieldWithPath("name").description("Название продукта"),
                fieldWithPath("fullName").description("Полное название продукта"),
                fieldWithPath("productId").description("Идентификатор (SKU) товара"),
                fieldWithPath("regionPickupAvailable").description("Показывает доступен ли продукт для самовывоза в данном регионе"),
                fieldWithPath("regionDeliveryAvailable").description("Показывает доступен ли продукт для доставки в данном регионе"),
                fieldWithPath("categoryId").description("Идентификатор категории, к которой принадлежит товар"),
                fieldWithPath("categoryName").description("Название категории, к которой принадлежит товар"),
                fieldWithPath("categories").description("Список идентификаторов категорий к которым принадлежит товар"),
                fieldWithPath("brandId").description("Идентификатор бренда товара"),
                fieldWithPath("brandName").description("Название бренда продукта"),
                fieldWithPath("description").description("Описание товара"),
                fieldWithPath("propertyAggregate").description("Какая-то фигня"),
                fieldWithPath("propepropertyAggregatertyMap").description("Дополнительные свойства товара"),
                fieldWithPath("price").description("Цена товара"),
                fieldWithPath("oldPrice").description("Предыдущая цена товара")
        ));
        mockMvc.perform(get(Constants.RestApiV1.ROOT_PATH + "/" + Constants.RestApiV1.GET_PRODUCTS + "/" + TEST_PRODUCT_ID_1 + "?baseStore=b200").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().contentType("application/json;charset=UTF-8"))
                .andExpect(jsonPath("$.productId").value(TEST_PRODUCT_ID_1))
                .andExpect(jsonPath("$.name").value("WRX3000"))
                .andExpect(jsonPath("$.fullName").value("WRX3000"))
                .andExpect(jsonPath("$.regionPickupAvailable").value(false)) // a100 != b200
                .andExpect(jsonPath("$.regionDeliveryAvailable").value(true)) // b200 == b200
                .andExpect(jsonPath("$.categoryId").value(100500))
                .andExpect(jsonPath("$.brandName").value("Samsung"));
    

问题是当我从 IDE (Intellij IDEA) 开始这个测试时,它工作正常并产生所有的 sn-ps。但是当我运行 maven “package” 任务测试失败时 结果:

Tests in error: 
  testGetProduct(ru.eldorado.searchservice.web.controllers.rest.ProductApiControllerTest): com.fasterxml.jackson.core.JsonParseException: Invalid UTF-8 middle byte 0xe5

我已检查问题出在字段描述中。如果我删除这部分包目标执行得很好。

我的项目和所有输出文件的编码都是 UTF-8。在 Maven 中我明确指定了

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

我们将不胜感激任何帮助和想法。

【问题讨论】:

但是,ProductApiControllerTest.java 实际上是否保存在 UTF-8 中?在 Eclipse 中,您可以对任何文件使用不同的编码,Eclipse 知道并编译正常,但外部编译会因混合编码而失败(或产生错误的字符串文字)。我不知道 Intellij,但可能是同样的问题。 是的,它以UTF-8保存。 Intellij 在屏幕右下方显示它,我还检查了项目设置。到处都是 UTF-8。 【参考方案1】:

回答我自己的问题。 问题在于这里的俄语字符

 public static final Product TEST_PRODUCT_1 = Product.builder()
            .id(TEST_PRODUCT_ID_1)
            .productId(TEST_PRODUCT_ID_1)
            .name("WRX3000")
            .fullName("WRX3000")
            .regionPickupAvailable(Arrays.asList("a100"))
            .regionDeliveryAvailable(Arrays.asList("b200"))
            .categoryId(100500)
            .categoryName("Телевизоры")
            .categories(Arrays.asList(10L, 20L, 30L))
            .brandId(200300)
            .brandName("Samsung")
            .description("AMOLED HD Телевизор 4 поколения")
            .prices(Arrays.asList(Price.builder().baseStore("b200").priceId(777).priceValue(2990d).oldPriceValue(3490d).build()))
            .build();

我已经添加了方法

private static String getStringInUtf8(String source) 
    return new String(source.getBytes(StandardCharsets.UTF_8));

并使用它在 TEST_PRODUCT_1 声明中分配包含俄语字符的值

public static final Product TEST_PRODUCT_1 = Product.builder()
        .id(TEST_PRODUCT_ID_1)
        .productId(TEST_PRODUCT_ID_1)
        .name("WRX3000")
        .fullName("WRX3000")
        .regionPickupAvailable(Arrays.asList("a100"))
        .regionDeliveryAvailable(Arrays.asList("b200"))
        .categoryId(100500)
        .categoryName(getStringInUtf8("Телевизоры"))
        .categories(Arrays.asList(10L, 20L, 30L))
        .brandId(200300)
        .brandName("Samsung")
        .description(getStringInUtf8("Super AMOLED HD Телевизор 5 поколения"))
        .prices(Arrays.asList(Price.builder().baseStore("b200").priceId(777).priceValue(2990d).oldPriceValue(3490d).build()))
        .build();

它解决了问题,但还没有找到它的根源。

【讨论】:

【参考方案2】:

我的另一个答案只是一种解决方法。真正的问题在于俄语版本的标准 Windows 编码(Cp-1251)。不知何故,Maven参数并没有解决这个问题。但是环境变量

JAVA_TOOL_OPTIONS = -Dfile.encoding=UTF8

修复一切。 在这里找到答案How to configure encoding in maven

【讨论】:

以上是关于Spring Rest 文档。片段生成时 UTF-8 中间字节无效 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

从 Spring MVC 控制器自动生成 REST api 文档到 RAML

Api管理工具(spring-rest-docs)

使用 Spring REST Docs 生成 Swagger 客户端

Angular/Spring Boot Rest API下载Word文档

SpringBoot+rest接口+swagger2生成API文档+validator+mybatis+aop+国际化

使用Swagger生成Spring Boot REST客户端(支持Feign)(待实践)