如何使用 Spring 引导和 Java 库在 google 上创建操作

How to create actions on google using Spring Boot & Java Library

我正在尝试使用 Spring Boot & Dialogflow 在 google 上创建操作。我在其中尝试使用可用的 java 库 https://github.com/actions-on-google/actions-on-google-java

但无法理解我应该如何在我的 Spring 引导应用程序中实现这些注释。例如:@ForIntent

我已经尝试使用 App Engine 入口点的样板代码 https://github.com/actions-on-google/dialogflow-webhook-boilerplate-java 我能够 运行 这段代码,但无法理解它在 Spring 启动应用程序中的实现。

在Spring引导: 我们在应用程序中使用@RestController 来映射请求

但是在 google 上执行操作,将只有一个请求 link,我们可以提供作为 Fulfillment webhook。那么我应该在我的代码中的什么地方使用@ForIntent 来识别 Intent 并更改 Request body 和 Response body。

我不知道 Google 上的 Action 是如何工作的,但从更高的层次来看,在示例应用程序中,请求是通过 ActionServlet

处理的

https://github.com/actions-on-google/dialogflow-webhook-boilerplate-java/blob/master/src/main/java/com/example/ActionsServlet.java

ActionServlet.javaclass,扩展了称为 HttpServlet 的普通旧 java servlet,如果您查看 doPost() 方法:

private final App actionsApp = new MyActionsApp(); ----> Bullet Point - 1

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {
    String body = req.getReader().lines().collect(Collectors.joining());
    LOG.info("doPost, body = {}", body);

    try {
      String jsonResponse = actionsApp.handleRequest(body, getHeadersMap(req)).get(); ----> Bullet Point - 2
      LOG.info("Generated json = {}", jsonResponse);
      res.setContentType("application/json");
      writeResponse(res, jsonResponse);
    } catch (InterruptedException e) {
      handleError(res, e);
    } catch (ExecutionException e) {
      handleError(res, e);
    }
  }

所以你的问题是如何在 Spring Boot. Spring 使用 DispatcherServlet 将传入请求映射到控制器,因此在内部 spring 使用类似的机制来映射传入请求。要回答您的问题,在 Spring 引导术语中,您想执行如下操作:

@RestController("/action")
public class ActionController{

    @Autowired
    private App actionsApp;

    @PostMapping("/")
    public void handleActions(){
       // Similar method call like Bullet Point 2
       // I assume handleRequest() method will call the @ForIntent method here ?? No idea
       actionsApp.handleRequest(.....);
    }
}

并配置 MyActionsApp.java 如下:

 @Service
public class MyActionsApp extends DialogflowApp {

  private static final Logger LOGGER = LoggerFactory.getLogger(MyActionsApp.class);

  @ForIntent("Default Welcome Intent")
  public ActionResponse welcome(ActionRequest request) {
    LOGGER.info("Welcome intent start.");
    ResponseBuilder responseBuilder = getResponseBuilder(request);
    ResourceBundle rb = ResourceBundle.getBundle("resources");
    User user = request.getUser();

    if (user != null && user.getLastSeen() != null) {
      responseBuilder.add(rb.getString("welcome_back"));
    } else {
      responseBuilder.add(rb.getString("welcome"));
    }

    LOGGER.info("Welcome intent end.");
    return responseBuilder.build();
  }

  @ForIntent("bye")
  public ActionResponse bye(ActionRequest request) {
    LOGGER.info("Bye intent start.");
    ResponseBuilder responseBuilder = getResponseBuilder(request);
    ResourceBundle rb = ResourceBundle.getBundle("resources");

    responseBuilder.add(rb.getString("bye")).endConversation();
    LOGGER.info("Bye intent end.");
    return responseBuilder.build();
  }
}

PS:我假设 actionsApp.handleRequest(..) 方法将根据其他一些配置调用 @ForIntent。但是,如果您想将 ActionServlet 实现移植到 SpringBoot,这就是实现该操作的方法。

让我知道这是否有效。

我实际上曾经这样做过,并基于 Silly Name Maker 示例。我基于 Spring 引导规范示例之一,所以我不保证这是 'optimal',但它是一个非常干净的实现。

您可以保持 SillyNameMakerApp 不变,不加修改。您可以像这样创建 Spring 引导包装器,而不是 ActionsServlet:

@SpringBootApplication
public class HelloworldApplication {
    private static final Logger LOG = LoggerFactory.getLogger(SillyNameMakerApp.class);
    private final App actionsApp = new SillyNameMakerApp();

    @Value("${TARGET:World}")
    String message;

    @RestController
    class HelloworldController {
        @GetMapping("/")
        String serveAck() {
            return "App is listening but requires valid POST request to respond with Action response.";
        }

        @RequestMapping(value = "/", method = RequestMethod.POST, produces = { "application/json" })
        String serveAction(@RequestBody String body, @RequestHeader Map<String, String> headers) {
            try {
                return actionsApp.handleRequest(body, headers).get();
            } catch (InterruptedException | ExecutionException e) {
                return handleError(e);
            }
        }

        private String handleError(Exception e) {
            e.printStackTrace();
            LOG.error("Error in App.handleRequest ", e);
            return "Error handling the intent - " + e.getMessage();
        }
    }

    public static void main(String[] args) {
        SpringApplication.run(HelloworldApplication.class, args);
    }
}