# 02.6-WebServic and APIs-Lesson6: Consuming SOAP & REST ###### tags: `Udacity` [Toc] # 01 Introduction {%youtube aqxNb_nbNwo%} # 02 Consuming REST {%youtube Gf8IzEXaut0%} There are times when you want to consume (or call) publicly accessible web services or APIs in your application. If there is a web service or API available that provides the data you need, it doesn’t make sense for you to build one from scratch. This allows you to deliver your products and services faster to your end users. [RestTemplate](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html) allows you to consume a REST API programmatically from your code. Identify THREE ways to consume a REST API. * Programmatically via RestTemplate * Postman * cURL # 03 Case Study: Consume REST {%youtube H-Kj9MvADg0%} **REST Case Study** The code can be cloned from [GitLab](https://gitlab.com/videolearning/udacity-java/tree/master/Lesson6-consuming/consuming%20rest%20apis). The publicly accessible Joke API is https://official-joke-api.appspot.com/random_joke. ==ShannonNote== Jackson可以用來序列化和反序列化Json的java開源框架,他已經成為最流行的json解析器之一,spring mvc默認的json解析氣就是jackson,jackson解析大的json文件速度快運行的佔用內存低性能好,還有靈活的API容易擴展和制訂 參考: https://developer.ibm.com/zh/articles/jackson-advanced-application/ ==ShannonNote== ## step1 Create an Entity * Create an Entity class to contain the data * 這個entity的主要功能是match回傳過來的來自REST API的Jason ```java public class Joke { private Long id; private String type; private String setup; private String punchline; public Joke() {} public Joke(Long id, String type, String setup, String punchline) { this.id = id; this.type = type; this.setup = setup; this.punchline = punchline; } ...//get set construction @Override public String toString() { return "Joke{" + "id=" + id + ", type='" + type + '\'' + ", setup='" + setup + '\'' + ", punchline='" + punchline + '\'' + '}'; } } ``` ## step2 增加jackson的dependency ```xml <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> ``` ## step3 ComsumingApplication的設定 * 寫run() 跟 restTemplate() ```java @SpringBootApplication public class ConsumingApplication { private static final Logger log = LoggerFactory.getLogger(ConsumingApplication.class); public static void main(String[] args) { SpringApplication.run(ConsumingApplication.class, args); } @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder.build(); } @Bean public CommandLineRunner run(RestTemplate restTemplate) throws Exception { return args -> { Joke joke = restTemplate.getForObject( "https://official-joke-api.appspot.com/random_joke", Joke.class); log.info("shannon" + joke.toString()); }; } } ``` # 04 SOAP & Spring Web Service **Consuming SOAP** {%youtube zL6rOA0c1Ak%} **Spring Web Services** {%youtube 762L27E_mUo%} There may be times when you want to consume a SOAP-based web service in your applications. Spring Web Services makes this process easy by automatically generating the files you need in order to consume a SOAP-based web service. In order to generate the files, the WSDL for the SOAP service in question is needed. WSDL stands for Web Services Description Language and simply describes the web service: its location and the operations allowed. Spring Web Services is contract-first only. This means that you need to start from a contract definition (XSD or WSDL) to generate the files. **Dependencies** The `spring-boot-starter-web-services` dependency includes the needed dependencies for using Spring Web Services. **Generate Java Files** To generate Java classes from the WSDL in maven, you need the following plugin setup: ```xml <plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <version>0.14.0</version> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <schemaDirectory>${project.basedir}/src/main/resources/wsdl</schemaDirectory> <schemaIncludes> <include>*.wsdl</include> </schemaIncludes> </configuration> </plugin> ``` This plugin uses JAXB, which generates the Java classes and handles the mapping of XML to Java and vice versa. In order to generate the Java files, run the `mvn generate-sources` Maven command. This can easily be done via the command line or through IntelliJ. This results in a number of generated Java classes under `/target/generated-sources/xjc`. Once you have the generated code, you can create a web service client by simply extending the `WebServiceGatewaySupport` class and coding your operations. # 05 Case Study: WSDL {%youtube Mf70O-rqsL8%} **SOAP Case Study** The code can be cloned from [GitLab](https://gitlab.com/videolearning/udacity-java/tree/master/Lesson6-consuming/consuming%20soap). The publicly accessible web service is [NumberToWords](https://www.dataaccess.com/webservicesserver/NumberConversion.wso?op=NumberToWords), implemented by DataFlex, converts numbers to its word version. The [WSDL](https://www.dataaccess.com/webservicesserver/NumberConversion.wso?WSDL) file defines the operations available. ```typescript 2391 ``` ```typescript <string>two thousand three hundred and ninety one </string> ``` ```typescript 53429 ``` ```typescript <string>fifty three thousand four hundred and twenty nine </string> ``` ==ShannonNote== * 步驟 * 到[WSDL](https://www.dataaccess.com/webservicesserver/NumberConversion.wso?WSDL)複製內容,然後在`resources/wsdl`下新增一個檔案叫做`NumberConvertionWsdl.wsdl`,並將內容貼上 # 06 Case Study: Code Generation {%youtube 4RFI9B9tdlo%} ==ShannonNote== 有幾個設定在pom.xml ## step1 新增plugin: 裡面有jaxb,他會產生java class然後還有處理XML和JAVA之間的mapping * 這段會找到WSDL檔案然後產生java code * `<schemaDirectory>${project.basedir}/src/main/resources/wsdl</schemaDirectory>` ```xml <plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <version>0.14.0</version> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <schemaDirectory>${project.basedir}/src/main/resources/wsdl</schemaDirectory> <schemaIncludes> <include>*.wsdl</include> </schemaIncludes> </configuration> </plugin> ``` ## step2 新增jaxb的dependency像是`api`,`core`,`impl`等等,用來協助mapping xml to java ```xml= <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.xml.messaging.saaj</groupId> <artifactId>saaj-impl</artifactId> <version>1.5.0</version> </dependency> <dependency> <groupId>javax.xml.ws</groupId> <artifactId>jaxws-api</artifactId> <version>2.2.6</version> </dependency> ``` ## Step3 右邊有Maven,點擊Execute 跟影片有點小小的不同,請找`mvn jaxb2:generate` ![](https://i.imgur.com/F4DH9ja.png) ## Step4 可以看到左邊project多了target的資料夾 * 有request files and the response files * 當我們implement the client我們會用這些file ![](https://i.imgur.com/IDQUZQN.png) # 07 Case Study: Client {%youtube HroekMKDKjQ%} ## step1 設定NumberClient file extends WebServceGatewaySupport ```java= public class NumberClient extends WebServiceGatewaySupport { public NumberToWordsResponse getWords(String numbers) { //publicly accessible SOAP service 用來查訪SOAP Service的URI String uri = "https://www.dataaccess.com/webservicesserver/NumberConversion.wso"; //set the request 把數字放進去NumberToWords class裡面的setUbiNum方法裡面 NumberToWords numberRequest = new NumberToWords(); numberRequest.setUbiNum(new BigInteger(numbers)); //obtain and return the response NumberToWordsResponse response = (NumberToWordsResponse) getWebServiceTemplate().marshalSendAndReceive(uri,numberRequest); return response; } } ``` ## step2 ComsumingApplication.java * implements CommandLineRunner * Override run method ```java @SpringBootApplication public class ConsumingApplication implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(ConsumingApplication.class, args); } @Override public void run(String... args) throws Exception { NumberClient numberClient = new NumberClient(); //create and setup marshaller //這個會將java序列化跟反序列化XML request Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); //provide location to the ObjectFacory //"com.dataaccess.webservicesserver"會傳入ObjectFactory //objectFactory class是必要的when used to create the objects needed marshaller.setContextPath("com.dataaccess.webservicesserver"); //add marshaller to the client //把marshaller傳給NumberClient透過setMarshaller跟setUnmarshaller numberClient.setMarshaller(marshaller); numberClient.setUnmarshaller(marshaller); //retrieve the word format of the number NumberToWordsResponse response = numberClient.getWords("3454"); //print to screen System.out.println("Response is: " + response.getNumberToWordsResult()); } } ``` # 08 Lab: Consume an API **Lab: Consume an API** Consume a publicly accessible API from Java using one from this [list](https://github.com/public-apis/public-apis/blob/master/README.md). * Step 1: Add the Jackson dependency to the Maven POM file. * Step 2: Use RestTemplate to test a publicly accessible API from the list. We haven't included a solution for this lab due to the vast differences in how you might implement this, both depending on the specific API's functionality as well as whether it is a SOAP vs. REST API. Make sure to check back in the Case Study if you need a refresher! # 09 Recap {%youtube 98LtNbVjglU%}