Camel组件-Rest组件

Rest组件简介

rest组件可以用于开发restful接口、发起接口请求调用等。其作用等同于spring mvc + rest template。在学习过程中可以类比这两个库来学习。类比spring mvc的功能,后续用接口开发来表达;类比rest template的功能,后续用接口调用来表述。


rest组件使用

开发指南

spring boot项目中引入相关依赖:

<dependency>
  <groupId>org.apache.camel.springboot</groupId>
  <artifactId>camel-rest-starter</artifactId>
  <version>x.x.x</version>
  <!-- use the same version as your Camel core version -->
</dependency>

使用eeif框架的直接引入framework-idp即可

接口定义

基本使用格式如下:

语法:
rest:method:path[:uriTemplate]?[options]

示例:
<from uri="rest:get:/v1/user/info:{userId}?host={{app.server.info}}"/>

method支持9种请求方式get, post, put, delete, patch, head, trace, connect, options,在rest ful接口定义中通常使用get, post, put, delete。
path定义遵循rest规范包括路径/请求参数/路径参数的定义规范。请求header、请求body遵循eip整体语法,可以直接在流程中定义。

上面定义好的接口路径 最终的访问地址如下

http(s)://{ip:port}/{contextPath}/{camelContextPath}/v1/user/info/{userId}

这里面要有两个上下文根,一个是spring的,一个是camel的,具体配置参考如下:

########################################################
### 服务基本参数设置
########################################################
server:
    servlet:
        context-path: /idp

########################################################
### Camel配置
########################################################
camel:
  servlet:
    mapping:
      context-path: /api/*

那么最终的接口访问路径就是:

http(s)://{ip:port}/idp/api/v1/user/info/{userId}

接口参数

在学习中参考参考Spring MVC来针对性的看下Rest组件的多种使用方式,Spring MVC中@RequestParam、@RequestBody、@RequestHeader、@PathVariable。
处理的Request的不同内容部分分为四类:(主要讲解常用类型)
A、处理requet uri 部分(这里指uri template中variable,不含queryString部分)的注解: @PathVariable;
B、处理request header部分的注解: @RequestHeader, @CookieValue;
C、处理request body部分的注解:@RequestParam, @RequestBody;
D、处理attribute类型是注解: @SessionAttributes, @ModelAttribute;

  • @PathVariable

当使用@RequestMapping URI template 样式映射时, 即 someUrl/{paramId}, 这时的paramId可通过 @Pathvariable注解绑定它传过来的值到方法的参数上。
camel rest组件中 uriTemplate中定义的

rest:method:path[:uriTemplate]?[options]

# 例如 其中的sessionId userId
<from uri="rest:get:/v1/session/info:{sessionId}/{userId}"/>

# 对应的信息可以在header中读取
<transform>
     <simple>${header.sessionId} ${header.userId}</simple>
</transform>

配置后可以在组件中获取对应的参数信息:

<route id="helloRestApi">
<from uri="rest:get:helloRestApi:/{me}?queryParameters={msg}{age}"/>
<transform>
<simple>{header.msg}{header.me}, age is {header.age}</simple> </transform> <log message="response from server{body}"/>
</route>

  • @RequestParam

A) 常用来处理简单类型的绑定,通过 Request.getParameter() 获取的String可直接转换为简单类型的情况( String--> 简单类型的转换操作由ConversionService配置的转换器来完成);因为使用request.getParameter()方式获取参数,所 以可以处理get 方式中queryString的值,也可以处理post方式中 body data的值;
B)用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容,提交方式GET、POST;
C) 该注解有两个属性: value、required; value用来指定要传入值的id名称,required用来指示参数是否必须绑定;

rest:method:path[:uriTemplate]?[options]

# 例如 其中的sessionId userId
<from uri="rest:get:/v1/session/info?queryParameters={sessionId}{userId}"/>
  • @RequestBody

该注解常用来处理Content-Type: 不是application/x-www-form-urlencoded编码的内容,例如application/json, application/xml。因为Spring MVC配置有FormHttpMessageConverter,所以也可以用来处理 application/x-www-form-urlencoded的内容,处理完的结果放在一个MultiValueMap<String, String>里。

body信息不需要额外定义,就可以直接使用, ${body}获取body内容

  • @RequestHeader

header信息不用单独定义,可以直接${header.xxxx}获取对应的变量值。

另外Camel Message Header中会存放一些其他额外的请求信息,比如请求方式,一般来说Camel消息体的Header内容相对来说比较少。额外更多信息需要自己定义Processor获取,比如想获取请求的源地址。


image.png
  • @ModelAttribute

该注解有两个用法,一个是用于方法上,一个是用于参数上;
用于方法上时:通常用来在处理@RequestMapping之前,为请求绑定需要从后台查询的model;用于参数上时:用来通过名称对应,把相应名称的值绑定到注解的参数bean上;要绑定的值来源于。
该情况在camle中可以用于请求的对象类型进行绑定

数据处理

使用内置组件处理

常见的内置组件包括simple、contant、header、property组件,可以对象属性进行基本的逻辑处理。

simple组件

simple组件
<route id="simple_body_detail">
    <from uri="rest:post:rest/simple/detail?queryParameters={message}"/>
    <process ref="remoteIpProcessor"></process>
    <!-- simple 获取json内容时 注意需要un marshal之后才能操作 ${body[name]} -->
    <unmarshal><json library="Jackson"></json></unmarshal>
    <setBody>
        <simple>
            {
                "code": 200,
                "data": {
                    "message": ${header.message},
                    "name": ${body[name]}
                },
                "remoteIp": ${exchangeProperty.RemoteAddress},
                "name": ${exchangeProperty.name},
                "body": ${body}
            }
        </simple>
    </setBody>
    <log message="${body}"/>
</route>

header语法

property语法

自定义process

routes定义了一系列的处理流程,prceoss是对每一步流程处理的具体逻辑实现。可以通过processor创建、修改processor的exchange对象,包括header、body、exchange property等属性信息。

import com.egoo.eeip.eeif.framework.base.util.server.IdGeneratorUtil;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;

/**
 * com.egoo.eeip.idp.server.process
 *
 * @author egoo
 * @date 2022/9/26
 */
public class DemoProcess implements Processor {
    @Override
    public void process(Exchange exchange) throws Exception {
        exchange.getIn().setHeader("seqNo", IdGeneratorUtil.nextId());
    }
}

在camel context中注册对应processor

<bean id="remoteIpProcessor" class="com.egoo.eeip.eeif.framework.idp.processor.servlet.ServletRemoteIpProcessor"></bean>

路由中定一个和使用对应的processor

<route>
  <processor ref="remoteIpProcessor"> </processor>
</route>

接口调用

报文组装

调用其他接口,就需要按照其他接口的接口格式进行数据包装,包括接口请求的方式method、请求头、请求体、请求的路径参数等进行组装。

示例程序如下:get请求 http://{{host}}/server/api/helloRestApi?msg=nihao&age=13 去调用post请求获取数据并返回结果
[图片上传失败...(image-54e7ab-1667102449354)]
代码配置如下:

<route id="helloRestApiCall">
  <from uri="rest:get:helloRestApiCall?queryString={msg}{age}"/>
  <setBody>
    <simple>{"msg": "${header.msg}", "age": ${header.age}}</simple>
  </setBody>
  <removeHeaders pattern="*"></removeHeaders>
  <setHeader name="CamelHttpMethod">
    <constant>POST</constant>
  </setHeader>
  <setHeader name="Content-Type">
    <constant>application/json;charset=UTF-8</constant>
  </setHeader>
  <log message="rest post ${body}"/>
  <to uri="rest:post:/idp/v1/demo/map?host=http://localhost:9888&amp;bridgeEndpoint=true"/>
</route>

本地post请求:

@RestController
@RequestMapping(value="/v1/demo", produces="application/json")
@Api(value="demo测试接口", description="demo测试接口")
@Slf4j
public class testController {

   @PostMapping("/map")
   @ResponseBody
   public Object post(@RequestBody Map map){
      System.out.println(map.toString());
      map.put("code",0);
      map.put("message","成功了");
      return map;
   }
}

结果处理

通常是对返回的报文进行重新整理满足不同业务前端的接口需求。结果处理通常使用jsonata组件来进行转换。

文件上传

通过MimeMultipart格式转换来处理,注意该组件依赖邮件组件,通过unmarshal将multipart格式转换成不带附件的普通格式,marshal转换程multipart格式。

<route>
  <from uri="rest:post:upload"></from>
  <unmarshal>
    <mimeMultipart></mimeMultipart>
  </unmarshal>
  <to uri="file:download?fileName=test.png"></to>
  <!-- 避免request header中默认的content type作为返回值返回 -->
  <setHeader name="Content-Type"><constant>application/json</constant></setHeader>
  <setBody>
    <simple>{"code": 200, "msg": "upload file success"}</simple>
  </setBody>
</route>

遗留问题:

  • 获取文件名称

一般在转换前要获取到文件名,用于保存的时候使用,mimeMultipart并未提供相关功能。参考自定义processor来处理。

<route id="multipart_file_processor">
    <from uri="rest:post:upload1"></from>
    <process ref="multipartFileProcessor"></process>
    <log message="${headers} ${body}"></log>
    <to uri="file:download/tmp?fileName=${header.CamelFileName}"></to>
    <setHeader name="Content-Type"><constant>application/json</constant></setHeader>
    <setBody>
        <simple>{"code": 200, "msg": "upload file success"}</simple>
    </setBody>
</route>
  • 一次上传多个文件

上传文件通过marshal格式化后,通过body读取相关内容,读取的内容需要根据boundary分割

<route>
  <from uri="rest:post:upload1/{path}"></from>
  <marshal>
    <mimeMultipart></mimeMultipart>
  </marshal>
  <log message="${body}"></log>
</route>
收到的body内容如下所示:每一个item内容Content-Dispositoin,由header中的Content-Type中的boundary进行分割。
Content-Type=multipart/form-data; boundary=--------------------------065816312379776045391551

[图片上传失败...(image-a87caf-1667102449354)]
针对报文编写内容解析来完成报文解析处理。详细参考eeif框架 MultipartFileProcessor。

  • 文件和其他参数同时用multipart方式,建议其他参数用路径参数或者query参数
<route>
  <from uri="rest:post:upload/{path}"></from>
  <unmarshal>
    <mimeMultipart></mimeMultipart>
  </unmarshal>
  <to uri="file:download?fileName=/${header.path}/test.png"></to>
  <setHeader name="Content-Type"><constant>application/json</constant></setHeader>
  <setBody>
    <simple>{"code": 200, "msg": "upload file success"}</simple>
  </setBody>
</route>

接口管理

通过rest-api和swagger ui管理rest api接口

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容