当前位置:网站首页>Customization of openfeign
Customization of openfeign
2022-06-23 03:22:00 【eeaters】
feign Custom use
- Project background
- Basic patterns and processes
- introduce OpenFeign
- Feign The custom of
- Encoder( The signature of the )
- RequestInterceptor(Header Pass on )
- Decoder( Unified decoding )
Project background
The company's original model was to provide customers with unified functions ; But demand cannot meet the needs of all customers ; Because all walks of life are very voluminous , The customer feels that they are not valued , The customer will quit , So the company changed its game : The function is open to the outside world , Customers don't think your requirements are simple , you can you up, no can no bibi;
Basic patterns and processes
Is to provide the current business capabilities , Expose one maven Dependence , Customer developers can have default business functions by introducing dependencies ;
The basic process is : Front end calls ( Can be customized ) → Customer service ( As the back-end , Mainly for the first layer ) → Call the midrange gateway ( New layer 1 Gateway ) → Internal service
introduce OpenFeign
- The internal services of the company and the calls between services use OpenFeign
- Use OpenFeign Time code is simple enough
- Company developers use OpenFeign Thief slip ( Although they don't know contextId What is it? , Encounter two services with the same name FeignClient I can't help it )
It may be based on the above reasons , It may also be that the project time is tight , The person who claps the board just slaps his head , So use OpenFeign The matter was settled happily ;
Feign The custom of
But there are some problems in the process of using , Because I used to OpenFeign I also have a certain understanding , Therefore, it solves some problems encountered in the process of use OpenFeign The problem of ; Take a brief note of ;
Encoder( The signature of the )
If the interface is exposed to the public, it will be signed , You need to have a key when connecting to a third party /token And so on. ; Both identify the caller as much as possible , To prevent malicious attacks
Purpose of use
Use FeignClient when , Parameters need a package layer when called , Look directly at the entity class
public class AppBaseRequest {
private String ver;
private String partnerId;
private String appId;
// The object you think you are just passing is actually a property field in the object
private String requestBody;
private String sign;
}Solution
Because the first time I met the parameter transfer, I changed the parameter completely , therefore debug It was a coincidence , It may not be a good plan , But it works well
@Configuration
@EnableConfigurationProperties(SignatureProperties.class)
public class AppletAutoConfiguration {
@Bean
public EdenSignature edenSignature(SignatureProperties signatureProperties) {
return new DefaultEdenSignature(signatureProperties);
}
// default SpringEncoder Injection has ConditionOnMissionBean; With this, the default is gone
@Bean
public AppletBossEncoder feignEncoder(ObjectFactory<HttpMessageConverters> messageConverters, EdenSignature edenSignature){
return new AppletBossEncoder(messageConverters, edenSignature);
}
}
public class AppletBossEncoder extends SpringEncoder {
// The extra lot was drawn ; If RestTemplate Calls can also be used to
private final EdenSignature edenSignature;
public AppletBossEncoder(ObjectFactory<HttpMessageConverters> messageConverters,
EdenSignature edenSignature) {
super(messageConverters);
this.edenSignature = edenSignature;
}
@Override
public void encode(Object requestBody, Type bodyType, RequestTemplate request) throws EncodeException {
if (!AppBaseRequest.class.getName().equals(bodyType.getTypeName())) {
//requestBody The original type is T ; After signing, it must be AppBaseRequest
requestBody = edenSignature.sign(requestBody);
bodyType = requestBody.getClass();
}
super.encode(requestBody, bodyType, request);
}
}RequestInterceptor(Header Pass on )
Because a lot of information on the front end was put into header What's going on inside ; One more layer of services , that header May be lost ; because ServletRequest It is stored in a local thread variable ; Then we'll just take it out and pass it back
The code is simple ; But there is one detail to understand : Feign When initializing, first find your own context Config ; If so, use your own , If not, just call parent Contextual ( The parent context is the global configuration ) ; The following code must be placed in a global configuration ; namely : This Bean You need to mark on the class @Configuration
@Bean
public RequestInterceptor headerPassInterceptor() {
return requestTemplate -> {
HttpServletRequest servletRequest = RequestUtils.currentServletRequest();
// Allow to return null ; Prevent this feign The call is not passed from the front end ; Such as running a scheduled task , There is no context at all ServletRequest
if (servletRequest != null) {
Enumeration<String> headerNames = servletRequest.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
if (!EXCLUDE_HEADER.contains(headerName)) {
Enumeration<String> headValue = servletRequest.getHeaders(headerName);
requestTemplate.header(headerName, Collections.list(headValue));
}
}
}
};
}Decoder( Unified decoding )
This is a problem encountered in previous projects ; We have connected more than a dozen services , Many of their return messages are different ; such as
- The error codes are code,statusCode,status;
- The returned entity classes are result,data
- The returned error messages are message, Yes errorMessage
So ,
- Every time an old project executes a remote call, it must judge the success in the business code ? When it succeeds, the entity class is retrieved ,
- The new project draws a layer , Package names vary , Is to deal with this judgment , Only successful entity objects are returned
Uncle can bear it , My aunt couldn't bear it ; Just read the decoded code , Try to change it :
The effect of hope
A lot of services have been connected ; But I can use one Result<T> Accept , Isn't it more comfortable to use .( Actually write code , I found it could be a little more awesome )
Uniform code extraction
Because every one of them FeignClient May have different return values , So a Decoder I can't go all the way to fill in
public abstract class AbstractCustomDecoder<T> extends Decoder.Default {
protected static final String DEFAULT_SUCCESS_CODE = "200";
private final Class<T> tClass;
protected AbstractCustomDecoder() {
tClass = retrieveAnnotationType();
if (tClass == null || ResponseEntity.class.equals(tClass)) {
throw new EdenAbstractException();
}
}
@Override
public Object decode(Response response, Type type) throws IOException {
return customDecoder(response, type);
}
protected Object customDecoder(Response response, Type type) throws IOException {
Class<?> rawClass = ResolvableType.forType(type).getRawClass();
String body = Util.toString(response.body().asReader());
T obj = JSONObject.parseObject(body, tClass);
if (rawClass.equals(tClass)) {
return obj;
}
Object data = getData(obj).get();
if (type instanceof ParameterizedTypeImpl) {
ParameterizedTypeImpl parameterizedType = (ParameterizedTypeImpl) type;
if (parameterizedType.getActualTypeArguments() != null) {
Type typeArgument = parameterizedType.getActualTypeArguments()[0];
data = JSONObject.parseObject(JSONObject.toJSONString(getData(obj).get()), typeArgument);
}
}
if (String.class.equals(type)) {
return String.valueOf(data);
}
String code = getCode(obj).get();
String message = getMessage(obj).get();
Boolean status = getStatus(obj).get();
return convertResult(rawClass, message, data, status, code);
}
protected Object convertResult(Class<?> rawClass, String message, Object data, Boolean status,String code) {
if (Result.class.equals(rawClass)) {
return new Result<>(status, message, data, code);
}
return JSONObject.parseObject(JSONObject.toJSONString(data), rawClass);
}
protected abstract Supplier<Object> getData(T obj);
protected abstract Supplier<String> getCode(T obj);
protected abstract Supplier<String> getMessage(T obj);
protected Supplier<Boolean> getStatus(T obj) {
return () -> {
String code = getCode(obj).get();
return DEFAULT_SUCCESS_CODE.equals(code);
};
}
/**
* Retrieve the actual type corresponding to the generic
* @return
*/
private Class<T> retrieveAnnotationType(){
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
for (Type actualTypeArgument : parameterizedType.getActualTypeArguments()) {
if (actualTypeArgument instanceof Class) {
return (Class) actualTypeArgument;
}
}
}
return null;
}
}Demo Of config
Be sure to pay attention to , The method here has @Bean; But there is no class @Configuration; In this way, the user can configure it
public class CustomDecoderConfig {
@Data
public static class BaiduResult<T>{
private String status;
private T result;
}
@Bean
public Decoder decoder() {
return new AbstractCustomDecoder<BaiduResult>() {
@Override
protected Supplier<Object> getData(BaiduResult obj) {
return obj::getResult;
}
@Override
protected Supplier<String> getCode(BaiduResult obj) {
return obj::getStatus;
}
@Override
protected Supplier<String> getMessage(BaiduResult obj) {
return () -> JSONObject.toJSONString(obj.getResult());
}
@Override
protected Supplier<Boolean> getStatus(BaiduResult obj) {
return () -> "OK".equals(obj.getStatus());
}
@Override
protected Object convertResult(Class<?> rawClass, String message, Object data, Boolean status, String code) {
if (BaiduResult.class.equals(rawClass)) {
BaiduResult cityResponse = new BaiduResult();
cityResponse.setStatus(code);
cityResponse.setResult(data);
return cityResponse;
}
return super.convertResult(rawClass, message, data, status, code);
}
};
}
}Effect display
@EnableFeignClients(clients = AlaBossDecoderExample.BaiduLbsClient.class)
public class AlaBossDecoderExample {
@FeignClient(contextId = "demo.baiduLbs",
name = "baiduLbs",
url = "http://api.map.baidu.com",
configuration = CustomDecoderConfig.class)
public interface BaiduLbsClient {
/**
* Native writing
*/
@GetMapping("/geocoder")
CustomDecoderConfig.BaiduResult<LocationInfo> resultGeocoder(@RequestParam("location") String location, @RequestParam("output") String output);
/**
* String Accept
*/
@GetMapping("/geocoder")
String stringGeocoder(@RequestParam("location") String location, @RequestParam("output") String output);
/**
* We only accept Data Internal required entity objects
*/
@GetMapping("/geocoder")
LocationInfo dataGeocoder(@RequestParam("location") String location, @RequestParam("output") String output);
/**
* Standard acceptance method
*/
@GetMapping("/geocoder")
Result<LocationInfo> standardGeocoder(@RequestParam("location") String location, @RequestParam("output") String output);
}
@Test
public void test() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AlaBossDecoderExample.class,
FeignAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class
);
context.refresh();
AlaBossDecoderExample.BaiduLbsClient lbsClient = context.getBean(AlaBossDecoderExample.BaiduLbsClient.class);
String rest = lbsClient.stringGeocoder("39.983424,116.322987", "json");
System.out.println("rest = " + rest);
LocationInfo json = lbsClient.dataGeocoder("39.983424,116.322987", "json");
System.out.println("json = " + json);
CustomDecoderConfig.BaiduResult<LocationInfo> json1 = lbsClient.resultGeocoder("39.983424,116.322987", "json");
System.out.println("json1 = " + json1);
Result<LocationInfo> json2 = lbsClient.standardGeocoder("39.983424,116.322987", "json");
System.out.println("json2 = " + ToStringBuilder.reflectionToString(json2));
context.close();
}
}Show the effect
Actually exceeded expectations , A new way to play , Casually accept ….
- It is physically acceptable
- The original data format is acceptable
- The specified data format can be consigned
- String acceptable
rest = {"formatted_address":" Zhongguancun Street, Haidian District, Beijing 27 Number 1101-08 room ","business":" Zhongguancun , Renmin University of China , Suzhou Street ","cityCode":131,"location":{"lng":116.322987,"lat":39.983424},"addressComponent":{"distance":"7","province":" The Beijing municipal ","city":" The Beijing municipal ","street":" Zhongguancun Street ","district":" Haidian District ","street_number":"27 Number 1101-08 room ","direction":"near"}}
json = AlaBossDecoderExample.LocationInfo(location=AlaBossDecoderExample.LocationInfo.Location(lng=116.322987, lat=39.983424), formatted_address= Zhongguancun Street, Haidian District, Beijing 27 Number 1101-08 room , business= Zhongguancun , Renmin University of China , Suzhou Street , cityCode=131, addressComponent=AlaBossDecoderExample.LocationInfo.AddressComponent(city= The Beijing municipal , direction= near , distance=7, district= Haidian District , province= The Beijing municipal , street= Zhongguancun Street , street_number=27 Number 1101-08 room ))
json1 = CustomDecoderConfig.BaiduResult(status=OK, result={"formatted_address":" Zhongguancun Street, Haidian District, Beijing 27 Number 1101-08 room ","business":" Zhongguancun , Renmin University of China , Suzhou Street ","cityCode":131,"location":{"lng":116.322987,"lat":39.983424},"addressComponent":{"distance":"7","province":" The Beijing municipal ","city":" The Beijing municipal ","street":" Zhongguancun Street ","district":" Haidian District ","street_number":"27 Number 1101-08 room ","direction":"near"}})
json2 = [email protected][status=true,message={"formatted_address":" Zhongguancun Street, Haidian District, Beijing 27 Number 1101-08 room ","business":" Zhongguancun , Renmin University of China , Suzhou Street ","cityCode":131,"location":{"lng":116.322987,"lat":39.983424},"addressComponent":{"distance":"7","province":" The Beijing municipal ","city":" The Beijing municipal ","street":" Zhongguancun Street ","district":" Haidian District ","street_number":"27 Number 1101-08 room ","direction":" near "}},result={"formatted_address":" Zhongguancun Street, Haidian District, Beijing 27 Number 1101-08 room ","business":" Zhongguancun , Renmin University of China , Suzhou Street ","cityCode":131,"location":{"lng":116.322987,"lat":39.983424},"addressComponent":{"distance":"7","province":" The Beijing municipal ","city":" The Beijing municipal ","street":" Zhongguancun Street ","district":" Haidian District ","street_number":"27 Number 1101-08 room ","direction":" near "}},statusCode=OK]边栏推荐
- PowerShell automated reinstallation of cloudbase init to version 1.1.2
- The cloud disk has been offline from the internal machine, but the console uninstall failed
- Configuring multi cluster management using kubectl
- Pytest common summary
- 2022 opening H5 mobile page special effects
- "Tianzhou II" successfully docked! Three minutes to understand the shocking black technology on "Tianzhou II"! Headlines
- [quick view] Analysis on the development status and future development trend of the global and Chinese diamond cultivation industry in 2021 [figure]
- Analysis of China's integrated circuit industry chain in 2021: huge downstream market demand [figure]
- The performance of the new Tokio scheduler is improved by 10 times
- To implement a task scheduling system, it is enough to read this article
猜你喜欢

Analysis on the development of China's satellite navigation industry chain in 2021: satellite navigation is fully integrated into production and life, and the satellite navigation industry is also boo
![Analysis on development history, industrial chain, output and enterprise layout of medical polypropylene in China in 2020 [figure]](/img/28/ebfc25ec288627706e15a07e6bdb77.jpg)
Analysis on development history, industrial chain, output and enterprise layout of medical polypropylene in China in 2020 [figure]

Encryption related to returnee of national market supervision public service platform

Analysis on the development of duty-free industry in Hainan Province in 2021: the implementation of the new policy makes the duty-free market in Hainan more "prosperous" [figure]

Analysis on the development of China's graphene industry chain in 2021: with the support of energy conservation and environmental protection policies, the scale of graphene industry will continue to e
![Analysis on demand and market scale of China's steamed stuffed bun industry in 2020 [figure]](/img/4b/dd272f98b89a157180bf68570d2763.jpg)
Analysis on demand and market scale of China's steamed stuffed bun industry in 2020 [figure]
![Analysis on the development prospect of China's brain computer interface industry in 2021: wide application prospect, sustained and rapid growth of market scale [figure]](/img/84/192d152ceb760264b6b555b321f129.jpg)
Analysis on the development prospect of China's brain computer interface industry in 2021: wide application prospect, sustained and rapid growth of market scale [figure]
![Analysis on the development status of China's watch industry in 2021: a large number of electric watches are imported [figure]](/img/ca/672bfe49c8123da8679b2abeb43a2e.jpg)
Analysis on the development status of China's watch industry in 2021: a large number of electric watches are imported [figure]
![Analysis of the number of urban residents covered by basic medical insurance, their treatment and medical treatment in other places in China in 2021 [figure]](/img/81/4d3cb059f700dd9243645e64023be7.jpg)
Analysis of the number of urban residents covered by basic medical insurance, their treatment and medical treatment in other places in China in 2021 [figure]
![Analysis of China's integrated circuit industry chain in 2021: huge downstream market demand [figure]](/img/de/d73805aaf4345ca3d2a7baf85aab8d.jpg)
Analysis of China's integrated circuit industry chain in 2021: huge downstream market demand [figure]
随机推荐
Bi skills - authority control
Mybatties plus batch warehousing
Build a weather forecast applet using a widget
Redis source code reading (I) general overview
Using promise to process asynchronous operations
PHP composer yii2 installation
Weekly Postgres world news 2022w03
Decentralized networks are not decentralized
Installing serverstatus probe using pagoda
Xiamen's hidden gaopuge smart park has finally been uncovered
Exploration on the framework of stream batch integration technology and its practice in kangaroo cloud number stack
DAAS architecture and Implementation (I)
CFS topics
Win11 client remote ECS black screen
Operating wechat cache by using wechat low code
The performance of the new Tokio scheduler is improved by 10 times
Flowable refactoring process editor to obtain user information
Wi Fi 6 is coming - larger capacity, lower latency, faster network speed and more security
Postman calls relevant interfaces of CLS API 3.0
Wwdc21 - App store server API practice summary