用过spring或Retrofit的人都知道实现函数和http请求的绑定和解耦非常方便,这里分享一下基于NanoHttpd实现的简单注解框架。
第一步定义注解类:
//http控制类,被该注解的类用来处理http请求
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Controller {
//路径前缀,有此前缀的请求会使用此类处理
String name() default "/";
boolean needPermissonControl() default true;
}
//被该注解的方法用来一个处理http请求
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestMapping {
//匹配的路径
String path() default "";
Method method() default Method.GET;
}
//该注解用于函数的参数,用于绑定请求的参
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Param {
//参数名
String name() default "";
//参数默认值
String value() default "";
}
第二步,实现serverlet时注册Controll类
@Override
public void addController(Class> controller) {
// has controller annotation
if (controller.isAnnotationPresent(Controller.class)) {
Controller controllerAnnotation = controller.getAnnotation(Controller.class);
String name = controllerAnnotation.name();
boolean needPermissionControl = controllerAnnotation.needPermissonControl();
Method[] methods = controller.getDeclaredMethods();
// get all request mapping annotation
for (Method method : methods) {
if (method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String path = requestMapping.path();
org.nanohttpd.protocols.http.request.Method m = requestMapping.method();
// build full path
String fullPath = name + File.separatorChar + path;
// getparams
ArrayList params = new ArrayList<>();
Annotation[][] paramAnnotation = method.getParameterAnnotations();
for (Annotation[] an : paramAnnotation) {
if (an.length > 0) {
Param p = (Param)an[0];
params.add(p);
}
}
RequestMappingParams requestMappingParams = new RequestMappingParams();
requestMappingParams.path = fullPath;
requestMappingParams.handler = controller;
requestMappingParams.method = m;
requestMappingParams.methodReflect = method;
requestMappingParams.params = params;
requestMappingParams.needPermissionControl = needPermissionControl;
addRoute(requestMappingParams);
}
}
}
}
大概思路是在注册时,找到Controller注解的uri路径前缀,还有有RequestMapping注解的函数,将路径对应上函数方法,然后在请求来时匹配到该路径就调用对应用的方法,下面是匹配到路径时的处理:
private Response processController(Object object, Map urlParams, IHTTPSession session) throws InvocationTargetException, IllegalAccessException {
if (requestMappingParams.needPermissionControl) {
if (!AppNanolets.PermissionEntries.isRemoteAllow(session.getRemoteIpAddress())) {
return Response.newFixedLengthResponse("not allow");
}
}
//匹配请求方法
if (requestMappingParams.method != session.getMethod()) {
return Response.newFixedLengthResponse(Status.INTERNAL_ERROR, "text/plain", "method not supply");
}
ArrayList
下面是Controller的一个实例:
@Controller(name = "filemanager")
public class FileManagerHandler {
@RequestMapping(path= "list")
public Response list(@Param(name = "dir", value = "/sdcard") String path) {
if (TextUtils.isEmpty(path)) {
path = Environment.getExternalStorageDirectory().getAbsolutePath();
}
Dir d = new Dir(new java.io.File(path));
String json = JSON.toJSONString(d);
Response response = Response.newFixedLengthResponse(Status.OK, "application/json", json);
response.addHeader("Access-Control-Allow-Origin", "*");
return response;
}
上面的Controller匹配路径前缀是filemanager就是http://host/filemanager/xxx?xxx这样的的请求都会使用此类来处理,然后有个list方法处理http://host/filemanager/list?xxx请求,请求参数只有一个dir又参数,默认值是/sdcard,就是没传参时会使用此值传入函数。
全部代码在github:Enlarge-Android
这里使用注解的方法是调用反射,性能上会有所降低,可以使用另外一种基于注解自动生成代码的方法提高性能。使用注解来绑定http请求好处相当明显,如果不使用注解,那么要处理http请求时需要类实现或继承http请求处理类,并重写请求处理的方法,以达到得到处理的时机再做相关的逻辑处理,这样的代码显然多了很多也不灵活。