安卓 andserver 全域轉發(部分介面在本地)的實作思路
- 前言
- 參考資料
- 實作思路
- 業務需求
- 瓶頸
- 思考
- 最終解決
- 升級版本
前言
AndServer 是 Android 平臺的 Web Server 和 Web Framework, 它基于編譯時注解提供了類似 SpringMVC 的注解和功能,如果您熟悉 SpringMVC,則可以非常快速地掌握它,----yanzhenjie
參考資料
鏈接:原始碼地址 https://github.com/yanzhenjie/AndServer/.
鏈接:檔案地址 https://yanzhenjie.github.io/AndServer/.
鏈接:舊版檔案 https://yanzhenjie.github.io/AndServer/1.x/.
實作思路
業務需求
專案采用了andserver的架構來實作一個安卓端的網路框架,由一個webview套一個網頁發請求到andserver,但是很多介面是需要中臺處理的,
安卓端代碼大概是這樣的:
專案前期在andserver的介面接收到請求后,使用retrofit轉發給中臺,接收到中臺的請求后,再由andserver回傳給網頁;甚至有的介面需要在安卓端做一些處理然后再轉發,
@RequestMapping(method = {RequestMethod.OPTIONS, RequestMethod.GET}, path = "/test")
public AndroidResponse test(@RequestParam("organizationId") String store, @RequestParam("customerId") String customer) throws IOException {
AndroidResponse response = new AndroidResponse();
//這里可以自己做處理(請求前)
Call<AndroidResponse> call = api.test(store, customer);
Response<AndroidResponse> responseResponse = call.execute();
if (!responseResponse.isSuccessful() || responseResponse.errorBody() != null) {
Logger.err("Unable to sysLookupItem request:isSuccessful" + responseResponse.isSuccessful() + ",errorBody:" + responseResponse.errorBody());
// throw new RuntimeException("Unable to reqeust");
List<LookupitemModel> lookupitemModels = offlineLookupitem();
if (lookupitemModels != null) {
response .setData(lookupitemModels);
}
return response ;
}
response = responseResponse.body();
//這里可以自己做處理(請求后)
return response ;
}
瓶頸
前面寫的挺順暢的,看到這兒大家可能都覺得沒什么問題,這確實是一個能解決問題的方案,但是有一天前后臺的介面因為業務需求進行了一些改動,比如加了個入參之類的,這個時候安卓端不僅需要改動andserver的api,還需要改動retrofit的api,改一個介面沒有問題,改兩個介面也沒有問題,但是當改動的介面涉及到整個專案,且專案比較趕的時候,這個問題就很大了,
架構師說:“我們必須要有一個全域的轉發,然后還不能全轉發,一些調硬體的介面得你自己處理,一些登錄的初始化你的攔截做了處理然后發中臺…”
我的內心:“/-+!@#¥%&**”
我的回答:“好的,我下去看看”

思考
然后就是查資料哇,面向百度編程嘛,開始我就想,既然andserver提供了類似 SpringMVC 的注解和功能,我是不是可以通過Interceptor攔截器來實作然后把檔案墻放好,一頭撞了上去
andserver的檔案是這么解釋的:
鏈接: HandlerInterceptor https://yanzhenjie.com/AndServer/class/HandlerInterceptor.html.
import com.aw.ccpos.client.logger.Logger;
import com.yanzhenjie.andserver.annotation.Interceptor;
import com.yanzhenjie.andserver.framework.HandlerInterceptor;
import com.yanzhenjie.andserver.framework.handler.RequestHandler;
import com.yanzhenjie.andserver.http.HttpRequest;
import com.yanzhenjie.andserver.http.HttpResponse;
import androidx.annotation.NonNull;
@Interceptor
public class SysInterceptor implements HandlerInterceptor {
/**
* Intercept the execution of a handler.
*
* @param request current request.
* @param response current response.
* @param handler the corresponding handler of the current request.
* @return true if the interceptor has processed the request and responded.
*/
@Override
public boolean onIntercept(@NonNull HttpRequest request, @NonNull HttpResponse response, @NonNull RequestHandler handler) throws Exception {
RequestModel requestModel = new RequestModel();
String app = request.getHeader("app");
requestModel.setApp(app);
String token = request.getHeader("x-token");
requestModel.setToken(token);
String storeId = request.getHeader("storeId");
requestModel.setStoreId(storeId);
String terminalId = request.getHeader("terminalId");
requestModel.setTerminalId(terminalId);
String terminalNo = request.getHeader("terminalNo");
requestModel.setTerminalNo(terminalNo);
String organizationId = request.getHeader("organizationId");
requestModel.setOrganizationId(organizationId);
String userid = request.getHeader("userid");
requestModel.setUserid(userid);
String username = request.getHeader("username");
requestModel.setUsername(username);
String displayName = request.getHeader("displayname");
requestModel.setDisplayName(displayName);
String customerId = request.getHeader("terminalCustomerId");
requestModel.setCustomerId(customerId);
String storeCode = request.getHeader("storeCode");
requestModel.setStoreCode(storeCode);
String sName = request.getHeader("storeName");
requestModel.setStoreNameOriginal(sName);
SysUtil.create(requestModel);
Logger.d("SysInterceptor check : requestModel " + requestModel);
//外部鏈接拒絕訪問
// String ip = request.getHeader("Host");
// if (!ip.equals("0.0.0.0:8080")){
// Logger.w("External address access, blocked");
// return true;
// }
return false;
}
}
示例中寫的功能和我們需要轉發的介面功能差不多,看到過后我就開擼代碼,擼完一跑,確實能攔截到,可以轉發,就是所有的介面都被攔截了,我一個介面都別想處理,有的同學就說了,你每一個要處理的url判斷一下放過去不就行了?

確實可以,沒有問題,但是這個方法和第一種有什么區別呢?所以還是另尋它法吧
最終解決
ExeceptionResolver
鏈接: ExeceptionResolverhttps://yanzhenjie.com/AndServer/class/ExceptionResolver.html.
andserver的檔案是這么解釋的:
ExeceptionResolver用來處理所有請求Http Api時發生的例外,默認情況下會輸出例外的Message到客戶端,
這個時候又有同學要說了,看起來和我們需求沒關系啊

別著急,我給你講講我清晰的腦回路,當我們本地有介面的時候,andserver正常走介面,我們正常處理然后回傳,需要在介面中請求中臺就使用retrofit,介面有改動安卓端必須跟著改,這個是無法避免的,但是如果是直接轉發走的介面,我們自定義的ExeceptionResolver就會捕獲到例外然后從新轉發出去就好了
代碼如下:
import com.aw.ccpos.client.Client;
import com.aw.ccpos.client.logger.Logger;
import com.yanzhenjie.andserver.annotation.Resolver;
import com.yanzhenjie.andserver.error.MethodNotSupportException;
import com.yanzhenjie.andserver.error.NotFoundException;
import com.yanzhenjie.andserver.framework.ExceptionResolver;
import com.yanzhenjie.andserver.framework.body.JsonBody;
import com.yanzhenjie.andserver.http.HttpMethod;
import com.yanzhenjie.andserver.http.HttpRequest;
import com.yanzhenjie.andserver.http.HttpResponse;
import com.yanzhenjie.andserver.http.RequestBody;
import java.util.HashMap;
import java.util.Map;
import androidx.annotation.NonNull;
/**
* @ProjectName :
* @Author : yifeng_zeng
* @Time : 2021/2/5 10:09
* @Description : Andserver GlobalExceptionSolver
*/
@Resolver
public class GlobalExceptionSolver implements ExceptionResolver {
@Override
public void onResolve(@NonNull HttpRequest request, @NonNull HttpResponse response, @NonNull Throwable e) {
HttpMethod method = request.getMethod();
//GlobalException后面再說
if (!(e instanceof NotFoundException) && !(e instanceof MethodNotSupportException)
&& !(e instanceof GlobalException)) {
Logger.err("handle request failed", e);
return;
}
String uri = request.getURI();
uri = uri.replace("scheme:", "");
// uri = uri.replace("/spin/user","/user");
if (uri.startsWith("/")) {
uri = uri.substring(1);
}
Logger.i("redirect with URI " + uri);
String url = Client.i().getServerUrl() + uri;
Logger.i("redirect to url " + url);
String terminalId = request.getHeader("terminalId");
SysUtil.requestModelLocal.setTerminalId(terminalId);
String token = request.getHeader("x-token");
SysUtil.requestModelLocal.setToken(token);
String terminalNo = request.getHeader("terminalNo");
SysUtil.requestModelLocal.setTerminalNo(terminalNo);
String organizationId = request.getHeader("organizationId");
SysUtil.requestModelLocal.setOrganizationId(organizationId);
switch (method) {
case POST:
try {
RequestBody body = request.getBody();
String string = body.string();
if (e instanceof GlobalException){
string = e.getMessage();
}
Map<String, String> headers = new HashMap<>();
for (String header : request.getHeaderNames()) {
headers.put(header, request.getHeader(header));
}
// List<Cookie> cookies= request.getCookies();
String result = HttpUtils.postSync(url, string, headers);
response.setBody(new JsonBody(result));
} catch (Exception ioException) {
Logger.err("Exception redirect post", ioException);
}
break;
case GET:
try {
Map<String, String> parameters = new HashMap<>();
// for (String parameter : request.getParameterNames()) {
// parameters.put(parameter, request.getParameter(parameter));
// }
Map<String, String> headers = new HashMap<>();
for (String header : request.getHeaderNames()) {
headers.put(header, request.getHeader(header));
}
String result = HttpUtils.getSync(url, headers, parameters);
response.setBody(new JsonBody(result));
} catch (Exception ioException) {
Logger.err("Exception redirect get", ioException);
}
break;
case DELETE:
try {
Map<String, String> parameters = new HashMap<>();
for (String parameter : request.getParameterNames()) {
parameters.put(parameter, request.getParameter(parameter));
}
Map<String, String> headers = new HashMap<>();
for (String header : request.getHeaderNames()) {
headers.put(header, request.getHeader(header));
}
if (parameters.size() == 0) {
RequestBody body = request.getBody();
String string = body.string();
if (e instanceof GlobalException){
string = e.getMessage();
}
String result = HttpUtils.deleteSync(url, string, headers);
response.setBody(new JsonBody(result));
} else {
String result = HttpUtils.deleteSync(url, headers, parameters);
response.setBody(new JsonBody(result));
}
} catch (Exception ioException) {
Logger.err("Exception redirect DELETE", ioException);
}
break;
case PUT:
try {
RequestBody body = request.getBody();
String string = body.string();
if (e instanceof GlobalException){
string = e.getMessage();
}
Map<String, String> headers = new HashMap<>();
for (String header : request.getHeaderNames()) {
headers.put(header, request.getHeader(header));
}
String result = HttpUtils.putSync(url, string, headers);
response.setBody(new JsonBody(result));
} catch (Exception ioException) {
Logger.err("Exception redirect PUT", ioException);
}
}
}
}
有伸手黨就要說了,我沒有你轉發這個HttpUtils,我又難得找,你想不想要贊啊,emmm

import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class HttpUtils {
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
public static OkHttpClient client = new OkHttpClient.Builder().
connectTimeout(300, TimeUnit.SECONDS).
readTimeout(300, TimeUnit.SECONDS)
.sslSocketFactory(HttpsUtils.getSslSocketFactory(null,null,null))
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}).writeTimeout(300, TimeUnit.SECONDS).addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request.Builder builder = chain.request()
.newBuilder();
if (SysUtil.get().getToken() != null) {
builder.addHeader("x-token", SysUtil.get().getToken());
}
if (SysUtil.get().getOrganizationId() != null) {
builder.addHeader("organizationId", SysUtil.get().getOrganizationId());
}
if (SysUtil.get().getTerminalId() != null) {
builder.addHeader("terminalId", SysUtil.get().getTerminalId());
}
if (SysUtil.get().getTerminalNo() != null) {
builder.addHeader("terminalNo", SysUtil.get().getTerminalNo());
}
Request request = builder.build();
return chain.proceed(request);
}
}).build();
/*
*�?http post����
*/
public static void postAsync(String url, String body) throws Exception {
RequestBody params = RequestBody.create(JSON, body);
Request request = new Request.Builder().addHeader("Content-Type", "application/json").url(url).post(params).build();
try {
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
response.body().string();
}
}
});
} catch (Exception e) {
throw e;
}
}
/*
* �?http get����
*/
public static void getAsync(String url, String body) throws Exception {
Request request = new Request.Builder().addHeader("Content-Type", "application/json").url(url).get().build();
try {
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
response.body().string();
}
}
});
} catch (Exception e) {
throw e;
}
}
/*
* ?��http post����
*/
public static String postSync(String url, String body, Map<String, String> headers) throws Exception {
String returnMessage = "";
RequestBody params = RequestBody.create(JSON, body);
Request.Builder builder = new Request.Builder();
builder.addHeader("Content-Type", "application/json");
for (Map.Entry<String, String> header : headers.entrySet()) {
builder.addHeader(header.getKey(), header.getValue());
}
Request request = builder.url(url).post(params).build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
returnMessage = response.body().string();
} else {
Exception e = new Exception("�������?��?��" + response.code() + ":" + response.message());
throw e;
}
} catch (UnknownHostException e) {
throw e;
} catch (ConnectException e) {
throw e;
} catch (SocketTimeoutException e) {
throw e;
} catch (Exception e) {
throw e;
}
return returnMessage;
}
/*
* ?��http get����
*/
public static String getSync(String url,Map<String, String> headers,Map<String, String> params) throws Exception {
String returnMessage = "";
HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder();
if (params != null) {
for (Map.Entry<String, String> param : params.entrySet()) {
httpBuilder.addQueryParameter(param.getKey(), param.getValue());
}
}
Request.Builder builder = new Request.Builder();
builder.addHeader("Content-Type", "application/json");
for (Map.Entry<String, String> header : headers.entrySet()) {
builder.addHeader(header.getKey(), header.getValue());
}
Request request = builder.url(httpBuilder.build())
.build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
returnMessage = response.body().string();
} else {
Exception e = new Exception("" + response.code() + ":" + response.message());
throw e;
}
} catch (UnknownHostException e) {
throw e;
} catch (ConnectException e) {
throw e;
} catch (SocketTimeoutException e) {
throw e;
} catch (Exception e) {
throw e;
}
return returnMessage;
}
/*
* 同步put請求*
/
*/
public static String putSync(String url, String body,Map<String, String> headers) throws Exception {
String returnMessage = "";
RequestBody params = RequestBody.create(JSON, body);
Request.Builder builder = new Request.Builder();
builder.addHeader("Content-Type", "application/json");
for (Map.Entry<String, String> header : headers.entrySet()) {
builder.addHeader(header.getKey(), header.getValue());
}
Request request = builder.url(url).put(params).build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
returnMessage = response.body().string();
} else {
Exception e = new Exception("�������?��?��" + response.code() + ":" + response.message());
throw e;
}
} catch (UnknownHostException e) {
throw e;
} catch (ConnectException e) {
throw e;
} catch (SocketTimeoutException e) {
throw e;
} catch (Exception e) {
throw e;
}
return returnMessage;
}
/*
* 同步delete請求,body
* */
public static String deleteSync(String url, String body,Map<String, String> headers) throws Exception {
String returnMessage = "";
RequestBody params = RequestBody.create(JSON, body);
Request.Builder builder = new Request.Builder();
builder.addHeader("Content-Type", "application/json");
for (Map.Entry<String, String> header : headers.entrySet()) {
builder.addHeader(header.getKey(), header.getValue());
}
Request request = builder.url(url).delete(params).build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
returnMessage = response.body().string();
} else {
Exception e = new Exception("�������?��?��" + response.code() + ":" + response.message());
throw e;
}
} catch (UnknownHostException e) {
throw e;
} catch (ConnectException e) {
throw e;
} catch (SocketTimeoutException e) {
throw e;
} catch (Exception e) {
throw e;
}
return returnMessage;
}
/*
* 同步delete請求,body
* */
public static String deleteSync(String url,Map<String, String> headers,Map<String, String> params) throws Exception {
String returnMessage = "";
HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder();
if (params != null) {
for (Map.Entry<String, String> param : params.entrySet()) {
httpBuilder.addQueryParameter(param.getKey(), param.getValue());
}
}
Request.Builder builder = new Request.Builder();
builder.addHeader("Content-Type", "application/json");
for (Map.Entry<String, String> header : headers.entrySet()) {
builder.addHeader(header.getKey(), header.getValue());
}
Request request = builder.addHeader("Content-Type", "application/json")
.url(httpBuilder.build())
.delete()
.build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
returnMessage = response.body().string();
} else {
Exception e = new Exception("�������?��?��" + response.code() + ":" + response.message());
throw e;
}
} catch (UnknownHostException e) {
throw e;
} catch (ConnectException e) {
throw e;
} catch (SocketTimeoutException e) {
throw e;
} catch (Exception e) {
throw e;
}
return returnMessage;
}
}
升級版本
有細心的同學發現我們還有一個GlobalException的例外,這個東西是我們在andserver接收到請求,對請求處理過后不需要處理回傳的資訊的時候使用的自定義Exception;可以根據你們自己的業務需求取用
/**
*
* @Author : yifeng_zeng
* @Time : 2021/11/5 14:22
* @Description : 全域轉發例外,為了把body傳給GlobalExceptionSolver
*/
public class GlobalException extends BasicException {
private static int statusCode = 100;
public GlobalException(DataRequest request) {
super(statusCode,JSON.toJSONString(request));
}
public GlobalException(String request) {
super(statusCode,request);
}
}
使用
@Override
@RequestMapping(method = {RequestMethod.OPTIONS, RequestMethod.POST}, path = "/finishOrderSale")
public Response finishOrderSale(@RequestBody Request requestModel)throws IOException{
if (requestModel.getData()!=null){
//這里可以對requestModel進行處理
throw new GlobalException(requestModel);
}
Response response = new Response();
return response;
}
這個時候,坐在螢屏前的你是不是應該說一句:“秒啊!”

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/389201.html
標籤:其他
