美文网首页
第四课:RESTful Webservice 编程

第四课:RESTful Webservice 编程

作者: MarkOut | 来源:发表于2016-09-27 23:53 被阅读0次

课程网站:

相关文章:

Restful 服务端编程


Restful简介

RPC(Remote Procedure Call Protocol),即远程过程调用,是一种进程之间通信的技术。在一开始,电脑的各个进程之间是无法通讯的。为了让进程之间能够互相通讯,于是就发明了IPC(Inter-process communication,单机中运行的进程之间的相互通信)技术。随着互联网的出现,人们又将IPC扩展成为了RPC,于是实现了不同机器之间进程的通讯。

但是,实现RPC是很麻烦的。为了让RPC变得简单,程序员发明了各种各样的框架。Restful就是其中一类高效以及简洁易用的架构风格。

REST是一种架构风格,其核心是面向资源,REST专门针对网络应用设计和开发方式,以降低开发的复杂性,提高系统的可伸缩性。REST提出设计概念和准则为:
1. 网络上的所有事物都可以被抽象为资源(resource)
2. 每一个资源都有唯一的资源标识(resource identifier),对资源的操作不会改变这些标识
3. 所有的操作都是无状态的
REST简化开发,其架构遵循CRUD原则,该原则告诉我们对于资源(包括网络资源)只需要四种行为:创建,获取,更新和删除就可以完成相关的操作和处理。您可以通过统一资源标识符(Universal Resource Identifier,URI)来识别和定位资源,并且针对这些资源而执行的操作是通过 HTTP 规范定义的。其核心操作只有GET,PUT,POST,DELETE。
由于REST强制所有的操作都必须是stateless的,这就没有上下文的约束,如果做分布式,集群都不需要考虑上下文和会话保持的问题。极大的提高系统的可伸缩性。
对于SOAP Webservice和Restful Webservice的选择问题,首先需要理解就是SOAP偏向于面向活动,有严格的规范和标准,包括安全,事务等各个方面的内容,同时SOAP强调操作方法和操作对象的分离,有WSDL文件规范和XSD文件分别对其定义。而REST强调面向资源,只要我们要操作的对象可以抽象为资源即可以使用REST架构风格。

参考资料:
http://blog.csdn.net/lilongsheng1125/article/details/41643665
https://www.zhihu.com/question/25536695

CXF Restful Service 基础

在下载程序包,例如最新的apache-cxf-3.1.7-src.zip。然后解压到自己喜欢的文件夹。例如我是放在了桌面。

apache-cxf-3.1.7-src

这里面的文件特别多。我们要找的是一个sample。我们打开神奇的IntelliJ IDEA,然后import project。找到apache-cxf-3.1.7-src\distribution\src\main\release\samples\jax_rs\basic这个路径。然后双击pom.xml。然后一直按next就可以打开这个项目了。

打开项目

然后和上次一样,在右边找到框,输入命令:

  • install:安装,你会看到制品放入本地仓库
  • -Pserver: 运行服务器,程序会自动启动 jetty
  • -Pclient: 运行客户端程序

我们只需要分别运行 install 和 -Pserver。 然后,在浏览器中输入 URL:http://localhost:9000/customerservice/customers/123 。我们就看到了运行的结果。

运行结果

返回的是万能的xml。于是简单的程序体验就到这里了。

CXF 与 spring 集成

maven创建web项目:选择maven-archetype-webapp,然后输入:

groupId: me.cxf-sample
artifactId: cxf-rs-spring

项目名为cxf-rs-spring。

然后我们创建在main文件夹里面创建java文件夹,然后把它设置为Source Root。接着加入Java包,命名为demo.jaxrs.server。

加入Java包

然后我们将刚刚Demo中的Customer.java, CustomerService.java, Product.java, Order.java拖入到该包。

这个时候,发现没有rs包。

没有rs包

我们添加依赖:

<dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.0.1</version>
</dependency>

按系统文档配置src/main/webapp/WEB-INF/web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>cxf-rs-spring Maven Webapp</display-name>

  <!-- 设置Spring容器加载配置文件路径 -->
  <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/beans.xml</param-value>
  </context-param>
  <!-- 加载Spring容器配置 -->
  <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <listener>
        <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
  </listener>

  <!-- 加载CXFServlet容器配置 -->
  <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
  </servlet>
  <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/*</url-pattern>
  </servlet-mapping>

</web-app>

再在同一文件夹下,创建beans.xml,然后继续配置

创建beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jaxrs="http://cxf.apache.org/jaxrs"
  xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://cxf.apache.org/jaxrs
        http://cxf.apache.org/schemas/jaxrs.xsd">

  <!-- do not use import statements if CXFServlet init parameters link to this beans.xml -->

  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

  <jaxrs:server id="customerService" address="/service1">
        <jaxrs:serviceBeans>
          <ref bean="customerBean" />
        </jaxrs:serviceBeans>
  </jaxrs:server>

  <bean id="customerBean" class="demo.jaxrs.server.CustomerService" />
</beans>

最后,我们配置pom.xml。然后导入需要的包。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>me.cxf-sample</groupId>
  <artifactId>cxf-rs-spring</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>cxf-rs-spring Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <!-- 申明常用软件的版本 -->
  <properties>
    <jettyVersion>9.3.7.v20160115</jettyVersion>
    <cxf.version>3.1.6</cxf.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.ws.rs</groupId>
      <artifactId>javax.ws.rs-api</artifactId>
      <version>2.0.1</version>
    </dependency>
    <!-- CXF jaxrs 依赖 -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-transports-http</artifactId>
      <version>${cxf.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxrs</artifactId>
      <version>${cxf.version}</version>
    </dependency>

    <!-- CXF jaxrs description 依赖 -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-rs-service-description</artifactId>
      <version>${cxf.version}</version>
    </dependency>

    <!-- spring 依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>3.2.8.RELEASE</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>cxf-rs-spring</finalName>
    <plugins>
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>${jettyVersion}</version>
      </plugin>
    </plugins>
  </build>
</project>

最后用jetty:run启动服务器。然后,在浏览器输入:http://localhost:8080/http://localhost:8080/service1/customerservice/customers/123 看效果就行了。

如果我们输入http://localhost:8080/service1?_wadl 就可以看到该服务接口的定义。

我做的时候,出现了下面这个问题,服务器启动不了。找了很久,原来是之前搞apache的时候,服务器没有关闭。于是关闭服务器就好了。

[INFO] Jetty server exiting.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.110s
[INFO] Finished at: Tue Sep 27 22:13:51 CST 2016
[INFO] Final Memory: 25M/249M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.eclipse.jetty:jetty-maven-plugin:9.3.7.v20160115:run (default-cli) on project cxf-rs-spring: Failure: Address already in use: bind -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

JAX-RS 服务端编程

将cxf-rs-spring文件夹复制出来,改名为cxf-rs-spring-test。然后进入pom.xml,将原来<artifactId>cxf-rs-spring</artifactId>改为<artifactId>cxf-rs-spring-test</artifactId>

然后我们用IntelliJ IDEA打开这个文件。在java文件夹下,创建org.me.examples.helloworld,然后在这个包下面新建Hello.java。

使用下面代码:

package org.me.examples.helloworld;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

@Path("/hello/{username}")
public class Hello {
    @GET
    @Produces("text/xml")
    public String sayHello(@PathParam("username") String userName) {
        return "hello " + userName;
    }
}

再将beans.xml文件下的关键部分稍作修改。

<jaxrs:server id="customerService" address="/service1">
        <jaxrs:serviceBeans>
            <ref bean="customerBean" />
            <ref bean="Hello" />
        </jaxrs:serviceBeans>
    </jaxrs:server>

    <bean id="customerBean" class="demo.jaxrs.server.CustomerService" />
    <bean id="Hello" class="org.me.examples.helloworld.Hello" />

然后我们使用命令行,输入命令:curl http://localhost:8080/service1/hello/Monkey

效果如下:

效果图

返回了“hello Monkey”,说明成功了。

习题部分


RESTful Webservice 作为远程调用(RPC),它的输入是什么?输入是什么?

REST核心是URL和面向资源,用URL代替了原来复杂的操作方法,把它简化成了GET,PUT,POST,DELETE四个核心操作。一个URL对应着一个资源。因此我们可以认为,输入是一段URL,输出是一段资源(例如Json或者xml)。

有一个 Java 远程函数声明 public Customer getCustomer(String id),把 HTTP 协议输入/输出映射到该函数,至少要申明(annotation)哪些信息?请写出这些 annotation,并简单解释协议与函数的关系。

@GET
@Path("/customers/{id}/")

修改 实验1 的 @Path 为 @Path(“/myservice/”),请用 curl -v 给出结果

我们将@Path("/customerservice/")改为@Path(“/myservice/”)。我们知道,@Path("/customerservice/")表示项目根访问该类的URI路径。如果我们将其换成myservice,那么URL也要换成http://localhost:9000/myservice/customers/123。同理,如果我们换的是@Path("/customers/…")部分,对应的URL也要改变。否则就会显示404。实验结果如下:

实验结果

简述 GET 与 POST 的区别

Http获取资源的核心操作有四个:GET,POST,PUT,DELETE,对应着对这个资源的查,改,增,删4个操作。因此原理上,GET方法得到资源是不能修改信息的,而POST代表资源有可能会被修改。

在表面上,GET和POST的区别表现在以下方面:

  1. GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),POST把提交的数据则放置在是HTTP包的包体中。
  2. GET方式提交的数据最多只能是1024字节(因为GET用URL提交数据,所有关键在于浏览器对URL的支持),理论上POST没有限制,可传较大量的数据(实际为了安全起见,也有对应的限制)。
  3. 在用不同技术编程的时候,GET和POST的使用方法不一样。
  4. POST的安全性要比GET的安全性高。比如:通过GET提交数据,用户名和密码将明文出现在URL上,因为(1)登录页面有可能被浏览器缓存,(2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用GET提交数据还可能会造成Cross-site request forgery攻击。

参考资料:
http://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html

在实际应用中 Map<Long, Customer> customers 使用会产生副作用吗?为什么?

在多线程编程的时候,会产生副作用。有可能两个用户同时请求同一个ID。因此要尽量避免这种情况。

Spring IOC 与 DI 是 java 编程核心内容之一。阅读Spring IoC 使用详解用自己的语言解释 IoC 解耦原理。

IoC通过依赖注入,使用set或者构造方法实现了解耦。就是本来要修改一个接口的实现,必须要修改里面的内容,用依赖注入,就只需要set一下,就可以了。

(!)使用 Maven 构建(6)给出的案例,项目名称 IOC_test

……

在 Spring 试验中, 阅读 beans.xml, 写出申明根资源的关键内容

<jaxrs:server id="customerService" address="/service1">
    <jaxrs:serviceBeans>
        <ref bean="customerBean" />
    </jaxrs:serviceBeans>
</jaxrs:server>

<bean id="customerBean" class="demo.jaxrs.server.CustomerService" />

(!)尝试写一个根资源类 Hello,用 GET 方法实现 String SayHello() 远程服务。请给出 Hello类代码,增加根资源的配置(不是修改)XML内容,curl测试结果。

package org.me.examples.helloworld;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

@Path("/hello/{username}")
public class Hello {
    @GET
    @Produces("text/xml")
    public String sayHello(@PathParam("username") String userName) {
        return "hello " + userName;
    }
}
<jaxrs:server id="customerService" address="/service1">
        <jaxrs:serviceBeans>
            <ref bean="customerBean" />
            <ref bean="Hello" />
        </jaxrs:serviceBeans>
    </jaxrs:server>

    <bean id="customerBean" class="demo.jaxrs.server.CustomerService" />
    <bean id="Hello" class="org.me.examples.helloworld.Hello" />

WADL 的全称。从程序结果返回中截取一段 WADL 内容。

WADL全称为Web Application Description Language,即Web 应用程序描述语言。

实验返回的WADL

输出对象格式协商是Resquest的Header段的哪个 Field 决定的?

Accept:application/xml

JAX-RS 的全称

JAX-RS即Java API for RESTful Web Services,是一个Java 编程语言的应用程序接口,支持按照表述性状态转移(REST)架构风格创建Web服务。

@Produces 和 @Consumes 的作用

@Produces:
用于指定输出格式,参数可以是字符串数组。 如果是一个数组,第一个是默认输出格式。如果客户端指定输出,则会匹配其他输出。 例如:

@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})

@Consumes:
指定处理输入媒体的类型,例如 “Content-Type: application/json”。 如果你的服务中函数中带一个自由的参数,规定用合适的 输入 provider ,使用 body 中内容,实例化这个参数。例如:

@POST
@Consumes("text/plain")
@Produces("text/xml")
public String sayHello(@PathParam("username") String userName, String letter) {
        return "hello " + userName + ":" + letter;
}

(!)请完成 实验3 的每个参数处理的内容,然后在jesery上完成类似工作。请描述两个平台是否完全兼容?简述不兼容的内容。

……

相关文章

网友评论

      本文标题:第四课:RESTful Webservice 编程

      本文链接:https://www.haomeiwen.com/subject/aytfyttx.html