@[toc]
1、应用场景
由于本软件使用sprongboot前后端分离框架开发,前后端接口都是通过http协议交互的。如果搭建webService服务,太麻烦了,不想弄。因此,如何在原有的基础上,可以发送和接收webService请求呢?这要从webService接口的本质出发,解决问题。
作为javaweb开发者,http协议是最熟悉的协议了。但是,在开发医院系统的时候,业务往往需要和lis、his系统或者医疗设备对接,因此,就会接触一些http以外的协议,比如hl7协议、soup协议等。了解每个协议的本质,开发业务才能得心应手。
2、http协议简述
http协议是最熟悉的了,超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。通常包含请求头、请求体,我们一般发送get请求或者post请求,在前后端分离模式中,数据通过json的格式发送。分别使用HttpServletRequest和HttpServletResponse来解析数据并做出响应,这是Servlet相关的内容,在接收webService请求的时候需要用到。
3、webService协议/soup简述
http 和 webservice 都是基于TCP/IP协议的应用层协议,soup是webService的基础通信协议。
对于新手来说只要记住这些就行了:
webservice=soap=http+xml,webservice协议就是有http+xml组成的,其中xml中会用到wsdl,wsdl是描述语言xml中的一种格式。Soap建立在http上,说白了就是用http传送xml。
之所以使用webService协议是为了实现跨编程语言和跨操作系统平台,webService就是现在微服务最初模样,相当于微服务间的远程调用,除了WebService外,常见的远程调用技术还有RMI(Remote method invoke)和CORBA,由于WebService的跨平台和跨编程语言特点,因此比其他两种技术应用更为广泛,但性能略低。有兴趣的可以了解一下,下面开始上代码!
4、发送webService请求
读完上面我们知道了webService接口就是通过http协议发送xml内容,换句话说,发送一个webService请求,就是http+xml+post。将xml内容放到请求体中,以post的方式发送,这就是一个webService请求了。
4.1建立HttpUtils工具类,来发送post请求
HttpUtils工具类,注意,这里的Content-Type设置为text/xml; charset=utf-8,或者设置为application/xml也可以。
public class HttpUtils
{
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 xml 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param)
{
PrintWriter out = null;
BufferedReader in = null;
StringBuilder result = new StringBuilder();
try
{
log.info("sendPost - {}", url);
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
out.print(param);
out.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
String line;
while ((line = in.readLine()) != null)
{
result.append(line);
}
log.info("recv - {}", result);
}
catch (ConnectException e)
{
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
}
catch (SocketTimeoutException e)
{
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
}
catch (IOException e)
{
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
}
catch (Exception e)
{
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
}
finally
{
try
{
if (out != null)
{
out.close();
}
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}
}
4.2 调用医院给的url和xml内容
比如要获取病人id为11111111的信息,前端输入病人id/或者住院号之类的,后端拿到id后直接向lis发送webService请求:
请求地址:
url=http://127.0.0.1:8080/webService/soup.aspx?s=patientService
xml内容:
<?xml version="1.0" encoding="gbk"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<getPatient xmlns="http://tempuri.org/">
<patient><![CDATA[{"patiend_id":"11111111"}]]></patient>
</getPatient>
</soap:Body>
</soap:Envelope>
响应示例(仅供参考):
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<LisServiceResponse xmlns="http://tempuri.org/">
<LisServiceResult>
{
"code": "200",
"data":{
"住院号": "11111111",
"姓名": "胡思源",
"手术费": "1000",
"年龄": "18"
}
}
</LisServiceResult>
</LisServiceResponse>
</soap:Body>
</soap:Envelope>
我们就可以在Controller中写如下代码:
@GetMapping("/{patientId}")
@ApiOperation("根据患者id新增患者信息")
public AjaxResult add(@PathVariable(value = "patientId") Long patientId)
{
//从前端拿到患者id后,直接发送webService请求
String url = "http://127.0.0.1:8080/webService/soup.aspx?s=patientService";
String xml = "<?xml version=\"1.0\" encoding=\"gbk\"?>\n" +
"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
" <soap:Body>\n" +
" <getPatient xmlns=\"http://tempuri.org/\">\n" +
" <patient><![CDATA[{\"patiend_id\":\"" + patientId+ "\"}]]></patient>\n" +
" </getPatient>\n" +
" </soap:Body>\n" +
"</soap:Envelope>";
//使用工具类发送webService请求
String responseBody = HttpUtils.sendPost(url, xml);
//拿到响应后,是一个xml内容的字符串,患者的信息都在xml内容中,对xml解析就行了
//我这里和医院商量好,将患者内容以json的方式包到了xml中,所以,我直接使用截取json字符串后,采用阿里的fastjson解析成患者对象的。
//这里按照上面提供的响应示例,供大家参考。
responseBody = "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
" <soap:Body>\n" +
" <LisServiceResponse xmlns=\"http://tempuri.org/\">\n" +
" <LisServiceResult>\n" +
"\t\t\t{\n" +
"\t\t\t\"code\": \"200\",\n" +
"\t\t\t\"data\":{\n" +
"\t\t\t\t\t \"住院号\": \"11111111\",\n" +
"\t\t\t \"姓名\": \"胡思源\",\n" +
"\t\t\t \"手术费\": \"1000\",\n" +
"\t\t\t \"年龄\": \"18\"\n" +
"\t\t\t }\n" +
"\t\t\t}\n" +
"</LisServiceResult>\n" +
" </LisServiceResponse>\n" +
" </soap:Body>\n" +
"</soap:Envelope>";
//将xml解析成json
int startIndex = responseBody .indexOf("{");
int endIndex = responseBody .lastIndexOf("}");
//获取json字符串
String substring = responseBody .substring(startIndex, endIndex + 1);
//去除所有空白
String whitespace = StringUtils.deleteWhitespace(substring);
//去除字符串中的"\n", "\r", or "\r\n"
org.apache.commons.lang3.StringUtils.chomp(whitespace);
//在使用阿里的fastjson解析成java对象
JSONObject jsonObject = (JSONObject) JSON.parse(jsonString);
//获取code
String code = jsonObject.get("code").toString();
if ("200".equals(code)){
//获取data信息
String data = jsonObject.get("data").toString();
//这里就剩解析成java对象,进行业务逻辑的编写了,就不再赘述了!
..............
}else {
//这里处理响应不为200的情况
...............
}
}
5、接收webService请求
想要接收lis系统以webService接口发送的请求,需要有webService服务,但是在Springboot+http协议外,再搭建webService服务,多少有点冗余,主要的是和lis系统对接的接口并不多,主要还是自身的业务逻辑多,这时候怎么处理呢?
根据上面我们了解到的,webService接口的本质就是http+xml+post。从这上面出发,我们本身已经是http协议的了,post请求也可以发送和接收,唯一不同的点就是xml内容了。
这里我们以servlet解决这个问题,servlet下有ServletRequest和ServletResponse,ServletRequest又可以直接获取请求体,而xml内容又在请求体中,这样我们就可以拿到lis系统请求中所带的内容了,问题也就解决了,不需要重新搭建webService服务。下面上代码。
5.1 创建HttpHelper工具类
这里需要注意的一点是编码问题,一定要和对方的编码方式一致,例如:StandardCharsets.UTF_8
。
/**
* 通用http工具封装
*
* @author ruoyi
*/
public class HttpHelper
{
private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class);
public static String getBodyString(ServletRequest request)
{
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try (InputStream inputStream = request.getInputStream())
{
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
String line = "";
while ((line = reader.readLine()) != null)
{
sb.append(line);
}
}
catch (IOException e)
{
LOGGER.warn("getBodyString出现问题!");
e.printStackTrace();
}
finally
{
if (reader != null)
{
try
{
reader.close();
}
catch (IOException e)
{
LOGGER.error(ExceptionUtils.getMessage(e));
e.printStackTrace();
}
}
}
return sb.toString();
}
}
5.2 在Controller中写业务逻辑
/**
* 添加病人化验结果(对方发出webService请求)
* @param request
* @param response
*/
@PostMapping("/put")
@ApiOperation("添加病人化验结果")
public void putBloodStorage(ServletRequest request, ServletResponse response){
//获取bodyString
String bodyString = HttpHelper.getBodyString(request);
//解析soap的请求体
//这里的做法和上面一样都是借用fastjson解析成java对象的,就不详细写了
.................
//如果解析成功,则执行你的插入逻辑
.................
//webService接口响应
try {
//返回soap的xml响应
response.setStatus(200);
response.setContentType("text/xml; charset=utf-8");
response.setCharacterEncoding("utf-8");
String resp = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
" <soap:Body>\n" +
" <getAddResponse xmlns=\"http://tempuri.org/\">\n" +
" <getAddResult>\n" +
" {\"code\": \"200\",\n" +
" \"message\": \"ok\"}\n" +
" </getAddResult>\n" +
" </getAddResponse>\n" +
" </soap:Body>\n" +
"</soap:Envelope>";
response.getWriter().print(resp);
} catch (IOException e) {
e.printStackTrace();
}
}
5.3 用soupUI测试自己的webService接口是否可以成功调用
这里就不赘述了,直接放个我测试成功的图片了!