【问题标题】:Accept Strings and XML data from Ruby接受来自 Ruby 的字符串和 XML 数据
【发布时间】:2019-02-10 09:01:02
【问题描述】:

我想创建 REST 服务器,它接受来自 Ruby 代码的 XML 请求和纯文本到不同的控制器。我试图实现这一点:

@SpringBootApplication
public class Application extends SpringBootServletInitializer implements WebMvcConfigurer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
    ..............

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.removeIf(converter -> converter instanceof MappingJackson2XmlHttpMessageConverter);
        converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);
        converters.add(new MappingJackson2XmlHttpMessageConverter(
                ((XmlMapper) createObjectMapper(Jackson2ObjectMapperBuilder.xml()))
                        .enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)));
        converters.add(new MappingJackson2HttpMessageConverter(createObjectMapper(Jackson2ObjectMapperBuilder.json())));
    }

    private ObjectMapper createObjectMapper(Jackson2ObjectMapperBuilder builder) {
        builder.indentOutput(true);
        builder.modules(new JaxbAnnotationModule());
        builder.serializationInclusion(JsonInclude.Include.NON_NULL);
        builder.defaultUseWrapper(false);
        return builder.build();
    }    
}

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        //MyRequestBodyHttpMessageConverter converter = new MyRequestBodyHttpMessageConverter();
        FormHttpMessageConverter converter = new FormHttpMessageConverter();
        //MediaType utf8FormEncoded = new MediaType("application","x-www-form-urlencoded", Charset.forName("UTF-8"));
        //MediaType mediaType = MediaType.APPLICATION_FORM_URLENCODED; maybe UTF-8 is not needed
        converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_FORM_URLENCODED));
        //converter.setSupportedMediaTypes(Arrays.asList(utf8FormEncoded));
        converters.add(converter);
        MappingJackson2HttpMessageConverter conv1 = new MappingJackson2HttpMessageConverter();
        conv1.getObjectMapper().registerModule(new JaxbAnnotationModule());
        converters.add(conv1);

        MappingJackson2XmlHttpMessageConverter conv = new MappingJackson2XmlHttpMessageConverter();
        // required by jaxb annotations
        conv.getObjectMapper().registerModule(new JaxbAnnotationModule());
        converters.add(conv);
    }
}

检查 XML 格式是否正确:

@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
                                                                  HttpHeaders headers, HttpStatus status, WebRequest request) {
        PaymentTransaction response;
        if (ex.getMessage().contains("Required request body")) {
            response = new PaymentTransaction(PaymentTransaction.Response.failed_response, 350,
                    "Invalid XML message: No XML data received", "XML request parsing failed!");
        } else {
            response = new PaymentTransaction(PaymentTransaction.Response.failed_response, 351,
                    "Invalid XML message format", null);
        }
        return ResponseEntity.badRequest().body(response);
    }
}

控制器类:

@RestController()
public class HomeController {

    @Autowired
    public HomeController(Map<String, MessageProcessor> processors, Map<String, ReconcileProcessor> reconcileProcessors,
            @Qualifier("defaultProcessor") MessageProcessor defaultProcessor,
            AuthenticationService authenticationService, ClientRepository repository,
            @Value("${request.limit}") int requestLimit) {
        // Here I receive XML 
    }

    @GetMapping(value = "/v1/*")
    public String message() {
        return "REST server";
    }

    @PostMapping(value = "/v1/{token}", consumes = { MediaType.APPLICATION_XML_VALUE,
            MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_XML_VALUE,
                    MediaType.APPLICATION_JSON_VALUE })
    public PaymentResponse handleMessage(@PathVariable("token") String token,
            @RequestBody PaymentTransaction transaction, HttpServletRequest request) throws Exception {
        // Here I receive XML 
    }

    @PostMapping(value = "/v1/notification")
    public ResponseEntity<String> handleNotifications(@RequestBody Map<String, String> keyValuePairs) {
         // Here I receive key and value in request body
    }

    @PostMapping(value = "/v1/summary/by_date/{token}", consumes = { MediaType.APPLICATION_XML_VALUE,
            MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_XML_VALUE,
                    MediaType.APPLICATION_JSON_VALUE })
    public PaymentResponses handleReconcile(@PathVariable("token") String token, @RequestBody Reconcile reconcile,
            HttpServletRequest request) throws Exception {
         // Here I receive XML 
    }

    @ResponseStatus(value = HttpStatus.UNAUTHORIZED)
    public static class UnauthorizedException extends RuntimeException {
        UnauthorizedException(String message) {
            super(message);
        }
    }
}

正如您所见,在某些方法中我收到 XML,而在其他方法中我收到 key=value&amp;..... 形式的字符串

如何配置 Spring 以接受这两种类型? 我还应该将 Rest 控制器拆分为不同的文件吗?

编辑:

示例 XML 请求:

<?xml version="1.0" encoding="UTF-8"?>
<payment_transaction>
  <transaction_type>authorize</transaction_type>
  <transaction_id>2aeke4geaclv7ml80</transaction_id>
  <amount>1000</amount>
  <currency>USD</currency>
  <card_number>22</card_number>
  <shipping_address>
    <first_name>Name</first_name>    
  </shipping_address>
</payment_transaction>

示例 XML 响应:

<?xml version="1.0" encoding="UTF-8"?>
<payment_response>
    <transaction_type>authorize</transaction_type>
    <status>approved</status>
    <unique_id>5f7edd36689f03324f3ef531beacfaae</unique_id>
    <transaction_id>asdsdlddea4sdaasdsdsa4dadasda</transaction_id>
    <code>500</code>
    <amount>101</amount>
    <currency>EUR</currency>
</payment_response>

示例通知请求:

uniqueid=23434&type=sale&status=33

示例通知响应:它应该只返回 HTTP 状态 OK。

我用:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath />
    </parent>

Java 版本:“10.0.2” 2018-07-17 和 Wildfly 13。

关于我使用的 XML 生成:

@XmlRootElement(name = "payment_transaction")
public class PaymentTransaction {

    public enum Response {
        failed_response, successful_response
    }

    @XmlElement(name = "transaction_type")
    public String transactionType;
    @XmlElement(name = "transaction_id")
    public String transactionId;
    @XmlElement(name = "usage")

POM 配置:https://pastebin.com/zXqYhDH3

【问题讨论】:

  • 请提及目标平台/应用服务器是 Wildfly(以及版本)。正因为如此,您可能需要influence Wildfly 的类加载器才能使用捆绑的库而不是 Wildfly 自己的模块和 jar。
  • 已更新。有什么建议如何实现这个功能?
  • 晚上我会尝试在Wildfly中重现。

标签: java spring spring-boot spring-restcontroller spring-rest


【解决方案1】:

当在 WildFly 上部署为 WAR 应用程序时,Spring Boot 应用程序可能需要一些高级 JDK 反射 API 代理,包含在 sun.reflect 包中。为此,您的 应用程序需要将 jdk.unsupported 列为其依赖项 MANIFEST.MF 文件(请参阅 WildFly wiki)。

source - 所以我先应用了这个。

<plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>3.2.2</version>
    <configuration>
        <packagingExcludes>WEB-INF/web.xml</packagingExcludes>
        <archive>
            <manifestEntries>
                <Dependencies>jdk.unsupported</Dependencies>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

接下来,我修复了日志记录:我应用了 the first comment of the accepted answer here 进行日志记录,并从这里添加了一个 basic logback.xml 到 src/main/resources。

所以我的基本 jboss-deployment-structure.xml 如下:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
    <deployment>
        <exclude-subsystems>
            <subsystem name="logging" />
        </exclude-subsystems>
    </deployment>
</jboss-deployment-structure>

从现在开始,您可以在 Wildfly 控制台中看到发生了什么。

我在两个分布中测试了 POC:

仅 Servlet 分发 (wildfly-servlet-13.0.0.Final.zip)

这适用于基本的 jboss-deployment-structure.xml。

应用服务器分发 (wildfly-13.0.0.Final.zip)

在这个服务器中,我必须添加 org.reactivestreams 模块:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
    <deployment>
        <exclude-subsystems>
            <subsystem name="logging" />
        </exclude-subsystems>
        <dependencies>
            <module name="org.reactivestreams"/>
        </dependencies>
    </deployment>
</jboss-deployment-structure>

你必须把这个文件放到 src/main/webapp/WEB-INF 目录下。

如果您需要支持与 application/x-www-form-urlencoded 不同的内容类型,您可以通过将适当的 MessageConverter 注册到您的内容类型来实现。

在您的情况下,由于请求正文中的查询字符串,FormHttpMessageConverter 将是“正确的”,您必须将您的内容类型指定为 MY_OTHER_CONTENT_TYPE 常量:

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    FormHttpMessageConverter converter = new FormHttpMessageConverter();
    MediaType utf8FormEncoded = new MediaType("application","x-www-form-urlencoded", Charset.forName("UTF-8"));
converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_FORM_URLENCODED, MY_OTHER_CONTENT_TYPE));
    converters.add(converter);
    super.configureMessageConverters(converters);
}

但是使用的转换器应该能够处理它,所以您必须检查转换器的来源(我做过,如果是 FormHttpMessageConverter,它应该以这种方式工作)我认为(或者您可以尝试一下,如果有效,则有效)。有时这不是simple,但我希望它对你来说会以这种方式工作。

如果您无法解决,请给我们有问题的请求的完整示例,我会尽力解决。

测试

请确保您使用的是带有 POST 方法的 Content-Type: application/x-www-form-urlencoded 标头,并且您的参数在请求正文中传输。 (我已经使用 Boomerang Chrome 插件进行测试,并在其中为发布请求定义请求参数时,它作为获取参数添加到请求 URL。这显然导致了错误的结果。)

希望这些对您有所帮助。如果没有,在您应用正确的日志记录设置后,您将能够向我们展示原因(请更新您的帖子,但有例外)。

【讨论】:

  • 非常感谢。有没有办法在没有Content-Type: application/x-www-form-urlencoded的情况下接收数据?
  • 我通过gdrive share分享了我的POC应用程序,你可以检查它的配置,pom等。恐怕我用的是标准的FormHttpMessageConverter。
  • 我已经做到了。它可以工作,但只能在标题中使用Content-Type: application/x-www-form-urlencoded
猜你喜欢
  • 2019-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
相关资源
最近更新 更多