1.       HTTP数据处理API

1.1.   概述

本架构基于Java EE 7.0封装了一套类restful风格的HTTP协议网络数据处理API

API中,文件上传基于Servlet 3.1

其设计目的只在于提供简单易于使用的HTTP服务端程序

1.2.   UML

1.2.1.        类图

1.3.   使用方式

1.3.1.        指定可配置项

HTTP数据处理API完全基于Servlet,所以使用之前必须在网站的 web.xml中配置如下

 

    <servlet>

       <servlet-name>CentralController</servlet-name>

       <servlet-class>org.jtry.framework.net.http.CentralController</servlet-class>

       <init-param>

           <param-name>controllerConfigClass</param-name>

           <param-value>com.test.init.WebConfig</param-value>

       </init-param>

       <load-on-startup>1</load-on-startup>

    </servlet>

    <servlet-mapping>

       <servlet-name>CentralController</servlet-name>

       <url-pattern>/</url-pattern>

    </servlet-mapping>

 

其中servlet节点配置

servlet-class 使用org.jtry.framework.net.http.CentralController

url-pattern 指定前端数据处理程序的URL根路径

再设置该servlet在服务发布时的初始化顺序

最后配置初始化参数(init-param ,参数名(param-name)使用controllerConfigClass,值(param-value)使用编写好的配置类包路径

 

配置类由使用者自己定义,但必须实现org.jtry.framework.net.http.entity.ControllerConfig接口也可以继承org.jtry.framework.net.http.entity.DefaultControllerConfig

再根据实际需要,实现或重写如下方法

 

    String getCharacterEncoding();

 

    String getPrefix();

 

    String getSuffix();

 

    String[] getControllersPackages();

 

    Class<? extends ControllerDataChecker> getDataCheckerClass();

 

    Class<? extends TypeProcessor<?>>[] getTypeProcessorClasss();

 

getCharacterEncoding 返回要设定的网络传输数据字符集

getPrefix 返回全局网络URL前缀

getSuffix 返回全局网络URL后缀

getControllersPackages 返回前端数据处理程序所在包路径数组

getDataCheckerClass 返回全局网络数据检查器的Class

getTypeProcessorClasss 返回类型处理器的Class

 

实际使用例子如下

 

public class WebConfig extends DefaultControllerConfig {

 

    @Override

    public String getCharacterEncoding() {

       return "GBK";

    }

 

    @Override

    public String getPrefix() {

       return "/admin/html/";

    }

 

    @Override

    public String getSuffix() {

       return ".html";

    }

 

    @Override

    public String[] getControllersPackages() {

       return new String[] { "com.test.controller", "com.test.web.controller" };

    }

   

    @Override

    public Class<? extends ControllerDataChecker> getDataCheckerClass() {

       return MyControllerDataChecker.class;

    }

   

    @SuppressWarnings("unchecked")

    @Override

    public Class<? extends TypeProcessor<?>>[] getTypeProcessorClasss() {

       Class<?>[] typeProcessorClasss = new Class<?>[]{

           XmlTypeProcessor.class,

           JsonTypeProcessor.class,

           UserTypeProcessor.class

       };

       return (Class<? extends TypeProcessor<?>>[]) typeProcessorClasss;

    }

}

1.3.2.        接收HTTP请求

web.xml配置完毕之后,在前端数据处理程序使用@Path注解如下指定就可以接收HTTP请求

@Path("user")

public class Passport {

    UserService userService;

 

    @Path("login")

    public String login(User user) throws SQLException {

       if (userService.login(user.getName())) {

           return "index";

       } else {

           return "login";

       }

    }

 

例子中login方法的请求的URLhttp://IP:端口/网站根路径/user/login

如果要为参数User赋值请直接使用HTTP协议定义的查询字符串键值对的形式

 

比如User类的属性如下

 

public class User {

    private String name;

    private Integer age;

    private Double money;

    private String phone;

    private String regTime;

 

那么请求URL可以写为

http://IP:端口/网站根路径/user/login?name=text&age=26

这样参数Username属性与age属性都会被赋值

 

如果login的参数为基本类型则需要在参数上加上@Mark注解标注查询字符串的键

 

    @Path("login")

    public String login(@Mark("name")String name) throws SQLException {

       if (userService.login(name)) {

           return "index";

       } else {

           return "login";

       }

    }

 

如果传入login的是一个Json则需要在参数上加上@Mark注解并指定isJson true这时value标注的将是一个json路径

比如传入的json格式为{"user":{"name":"test","age":18}},那么可以如下指定

 

@Path("login")

    public String login(@Mark(isJson = true, value = "user.age") int age) throws SQLException {

       if (age > 18) {

           return "index";

       } else {

           return "login";

       }

    }

 

也可以这样指定

 

    @Path("login")

    public String login(@Mark(isJson = true, value = "user") User user) throws SQLException {

       if (userService.login(user.getName())) {

           return "index";

       } else {

           return "login";

       }

    }

 

如果传入的json是一个数组{"users":[{"name":"test","age":18},{"name":"test2","age":20}]},那么可以指定具体下标对象

 

@Path("login")

    public String login(@Mark(isJson = true, value = "users.0") User user) throws SQLException {

       if (userService.login(user.getName())) {

           return "index";

       } else {

           return "login";

       }

    }

 

也可以这样指定具体下标的对象具体属性

 

    @Path("login")

    public String login(@Mark(isJson = true, value = "users.0.age") int age) throws SQLException {

       if (age > 18) {

           return "index";

       } else {

           return "login";

       }

    }

 

总之只要知晓传入的json格式并使用正确的json路径,那么就可以直接取到任何深度任何类型的值

 

不同的前端数据处理程序类可以标注同一个@Path值,如下所示

 

@Path("user")

public class Passport {

 

@Path("user")

public class AdminController {

 

但必须这两个类方法中的@Path值不能存在重复

因为得保证类的@Path值与方法上的@Path值组合起来在全局是唯一的,如果有重复那么只能访问其中的一个

 

此外还可以在类或方法上使用@Path标注访问类型

 

@Path(value = "user", type = RequestMethod.POST)

public class Passport {

 

    @Path(value = "login",type = RequestMethod.GET)

    public String login(@Mark("name") String name) throws SQLException {

       if (userService.login(name)) {

           return "index";

       } else {

           return "login";

       }

    }

 

如上标注之后该类的全部方法都只允许POST方式访问,但login方法可以使用GET方式访问

 

前端数据处理程序类与方法可以不使用@Path注解,那么将会使用类名与方法名的全小写字符替代

 

public class Passport {

    UserService userService;

 

    public String login(User user) throws SQLException {

       if (userService.login(user.getName())) {

           return "index";

       } else {

           return "login";

       }

    }

 

上面例子的访问URL

http://IP:端口/网站根路径/passport/login

 

如果前端数据处理程序类包含一个名称为index的方法

那么直接使用

http://IP:端口/网站根路径/passport

就可以访问到该方法

 

public class Passport {

    UserService userService;

 

    public String index() {

       return "/login.html";

    }

 

如果要获取到HttpServletRequestHttpServletResponseHttpSession这些Servlet对象,请直接在参数中定义

 

    @Path("login")

    public String login(HttpServletRequest request,HttpServletResponse response,HttpSession session,User user) throws SQLException {

       if (userService.login(user.getName())) {

           return "index";

       } else {

           return "login";

       }

    }

 

当进入到该方法的时候,这些参数将会被赋值

 

前端数据处理程序类中接收前段请求的方法的返回值类型应该被定义为String

因为其返回的是一个页面的URL或者另一个前端数据处理程序类的方法URL路径

但如果该方法指定了@Ajax注解,那么返回值可以是任何类型,最终返回的结果会转换成JSON或者XML格式输出到客户端。

 

    @Ajax

    @Path("login")

    public User login(User user) throws SQLException {

       if (userService.login(user.getName())) {

           return user;

       } else {

           return null;

       }

    }

 

前段数据处理程序可以继承org.jtry.framework.net.http.HttpBaseController

该类可以获取到一些Servlet的基础对象信息

 

@Path("user")

public class Passport extends HttpBaseController{

1.3.3.        定义网络数据检查器

要使用网络数据检查器可以实现org.jtry.framework.net.http.entity.ControllerDataChecker接口

 

public class MyControllerDataChecker implements ControllerDataChecker {

 

    @Override

    public boolean inCheck(HttpServletRequest httpRequest, HttpServletResponse httpResponse, ExecuteResult executeResult)

           throws ServletException, IOException {

       String userName = httpRequest.getParameter("userName");

       if (StringUtil.isEmpty( userName)) { // 如果请求参数没有用户名那么不允许访问

           return false;

       }

       return true;

    }

 

    @Override

    public boolean outCheck(HttpServletRequest httpRequest, HttpServletResponse httpResponse, ExecuteResult executeResult)

           throws ServletException, IOException {

       if (executeResult.getException() instanceof SQLException) {// 如果处理方法发生SQL异常那么直接输出“Error”字符串

           executeResult.setResult("Error");

       }

       if (executeResult.getResult() == null) {// 如果处理方法没有返回值那么就不需要处理网络响应

           return false;

       }

       return true;

    }

}

 

接口包含inCheckoutCheck方法

 

inCheck方法在请求被处理之前被调用

inCheck方法返回为true那么该请求才允许进入处理方法

inCheck方法返回为false那么该请求直接被忽略,客户端将得不到任何响应

inCheck方法参数包含原始Servlet对象信息和将要传入处理方法的参数值

如果该网络数据检查器被配置为全局的,那么inCheck方法的ExecuteResult参数永远传入为null

 

outCheck方法在请求被处理之后被调用

outCheck方法返回为true那么该请求的返回结果才会被响应到客户端

outCheck方法返回为false那么该请求无论处理方法执行结果如何客户端都将得不到任何响应

outCheck方法参数包含原始Servlet对象信息和传入处理方法的参数值与返回的结果值还有可能抛出的异常信息

 

 

实现好网络数据检查器之后可以如下使用

 

在配置类中重写getDataCheckerClass方法可以返回一个网络数据检查器的Class

使用这种方式配置的网络数据检查器为全局生效,每一个请求与响应都会进行拦截

 

    @Override

    public Class<? extends ControllerDataChecker> getDataCheckerClass() {

       return MyControllerDataChecker.class;

    }

 

在前端数据处理程序类或者方法上的@Path注解中指定网络数据检查器的Class

使用这种方式配置的网络数据检查器会在请求进入该类范围内或者该方法时进行拦截

 

@Path(value = "user",checker = MyControllerDataChecker.class)

public class Passport{

 

    @Path(value = "login",checker = MyControllerDataChecker.class)

    public User login(User user) throws SQLException {

       if (userService.login(user.getName())) {

           return user;

       } else {

           return null;

       }

    }

1.3.4.        定义类型处理器

考虑到网络请求数据的复杂性与业务数据的多样性,为了更好的处理一些数据结构,网络数据处理API也支持自定义类型处理器

要使用类型处理器可以实现org.jtry.framework.net.http.entity.TypeProcessor接口

 

public class JsonTypeProcessor implements TypeProcessor<JsonObject>{

 

    @Override

    public JsonObject handle(String mark, HttpServletRequest httpRequest) {

       return JsonUtil.toJsonObject( httpRequest.getParameter(mark));

    }

 

}

 

已经实现的类型处理器可以在配置类中的getTypeProcessorClasss方法返回相应的Class

 

    @SuppressWarnings("unchecked")

    @Override

    public Class<? extends TypeProcessor<?>>[] getTypeProcessorClasss() {

       Class<?>[] typeProcessorClasss = new Class<?>[]{

           XmlTypeProcessor.class,

           JsonTypeProcessor.class,

           UserTypeProcessor.class

       };

       return (Class<? extends TypeProcessor<?>>[]) typeProcessorClasss;

    }

 

凡是前端数据处理程序类中的方法参数使用到的类型

如果有配置相应的类型处理器那么请求过来的数据针对该类型的类型转换将只会使用类型处理器

所以在编写类型处理器的时候请尽量考虑全面,如非必要最好不要针对JAVA基本类型编写类型处理器

 

    @Ajax

    @Path("login")

    public JsonObject login(JsonObject userJson) throws SQLException {

       if (userService.login(userJson.get("name").getAsString())) {

           return userJson;

       } else {

           return null;

       }

    }

 

以上例子中login方法的参数JsonObject就会使用类型处理器对请求的数据进行类型转换