| @@ -19,7 +19,7 @@ weixin-java-tools | |||||
| <dependency> | <dependency> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-mp</artifactId> | <artifactId>weixin-java-mp</artifactId> | ||||
| <version>1.0.4</version> | |||||
| <version>1.0.5</version> | |||||
| </dependency> | </dependency> | ||||
| ``` | ``` | ||||
| @@ -29,7 +29,7 @@ weixin-java-tools | |||||
| <dependency> | <dependency> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
| <version>1.0.4</version> | |||||
| <version>1.0.5</version> | |||||
| </dependency> | </dependency> | ||||
| ``` | ``` | ||||
| @@ -5,7 +5,7 @@ | |||||
| <modelVersion>4.0.0</modelVersion> | <modelVersion>4.0.0</modelVersion> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.0.4</version> | |||||
| <version>1.0.5</version> | |||||
| <packaging>pom</packaging> | <packaging>pom</packaging> | ||||
| <name>WeiXin Java Tools - Parent</name> | <name>WeiXin Java Tools - Parent</name> | ||||
| <description>微信公众号、企业号上级POM</description> | <description>微信公众号、企业号上级POM</description> | ||||
| @@ -70,11 +70,6 @@ | |||||
| <artifactId>gson</artifactId> | <artifactId>gson</artifactId> | ||||
| <version>2.2.2</version> | <version>2.2.2</version> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>org.apache.commons</groupId> | |||||
| <artifactId>commons-lang3</artifactId> | |||||
| <version>3.1</version> | |||||
| </dependency> | |||||
| <dependency> | <dependency> | ||||
| <groupId>commons-codec</groupId> | <groupId>commons-codec</groupId> | ||||
| <artifactId>commons-codec</artifactId> | <artifactId>commons-codec</artifactId> | ||||
| @@ -0,0 +1,229 @@ | |||||
| <?xml version="1.0"?> | |||||
| <project | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||||
| xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <groupId>me.chanjar</groupId> | |||||
| <artifactId>weixin-java-parent</artifactId> | |||||
| <version>1.0.5-SNAPSHOT</version> | |||||
| <packaging>pom</packaging> | |||||
| <name>WeiXin Java Tools - Parent</name> | |||||
| <description>微信公众号、企业号上级POM</description> | |||||
| <url>https://github.com/chanjarster/weixin-java-tools</url> | |||||
| <licenses> | |||||
| <license> | |||||
| <name>The Apache License, Version 2.0</name> | |||||
| <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> | |||||
| </license> | |||||
| </licenses> | |||||
| <developers> | |||||
| <developer> | |||||
| <name>Daniel Qian</name> | |||||
| <email>chanjarster@gmail.com</email> | |||||
| </developer> | |||||
| </developers> | |||||
| <scm> | |||||
| <connection>scm:git:https://github.com/chanjarster/weixin-java-tools.git</connection> | |||||
| <developerConnection>scm:git:git@github.com:chanjarster/weixin-java-tools.git</developerConnection> | |||||
| <url>https://github.com/chanjarster/weixin-java-tools</url> | |||||
| </scm> | |||||
| <modules> | |||||
| <module>weixin-java-common</module> | |||||
| <module>weixin-java-cp</module> | |||||
| <module>weixin-java-mp</module> | |||||
| </modules> | |||||
| <properties> | |||||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
| <downloadJavadocs>true</downloadJavadocs> | |||||
| <downloadSources>true</downloadSources> | |||||
| <httpclient.version>4.3.5</httpclient.version> | |||||
| </properties> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>org.apache.httpcomponents</groupId> | |||||
| <artifactId>fluent-hc</artifactId> | |||||
| <version>${httpclient.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.apache.httpcomponents</groupId> | |||||
| <artifactId>httpmime</artifactId> | |||||
| <version>${httpclient.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>javax.xml.bind</groupId> | |||||
| <artifactId>jaxb-api</artifactId> | |||||
| <version>2.2.7</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.sun.xml.bind</groupId> | |||||
| <artifactId>jaxb-impl</artifactId> | |||||
| <version>2.2.7</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.google.code.gson</groupId> | |||||
| <artifactId>gson</artifactId> | |||||
| <version>2.2.2</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>commons-codec</groupId> | |||||
| <artifactId>commons-codec</artifactId> | |||||
| <version>1.9</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>commons-io</groupId> | |||||
| <artifactId>commons-io</artifactId> | |||||
| <version>2.4</version> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <dependencyManagement> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>junit</groupId> | |||||
| <artifactId>junit</artifactId> | |||||
| <version>4.11</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.google.inject</groupId> | |||||
| <artifactId>guice</artifactId> | |||||
| <version>3.0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.testng</groupId> | |||||
| <artifactId>testng</artifactId> | |||||
| <version>6.8.7</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.mockito</groupId> | |||||
| <artifactId>mockito-all</artifactId> | |||||
| <version>1.9.5</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.eclipse.jetty</groupId> | |||||
| <artifactId>jetty-server</artifactId> | |||||
| <version>9.3.0.M0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.eclipse.jetty</groupId> | |||||
| <artifactId>jetty-servlet</artifactId> | |||||
| <version>9.3.0.M0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| </dependencyManagement> | |||||
| <distributionManagement> | |||||
| <snapshotRepository> | |||||
| <id>ossrh</id> | |||||
| <url>https://oss.sonatype.org/content/repositories/snapshots</url> | |||||
| </snapshotRepository> | |||||
| <repository> | |||||
| <id>ossrh</id> | |||||
| <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url> | |||||
| </repository> | |||||
| </distributionManagement> | |||||
| <profiles> | |||||
| <profile> | |||||
| <id>release</id> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-source-plugin</artifactId> | |||||
| <version>2.2.1</version> | |||||
| <executions> | |||||
| <execution> | |||||
| <id>attach-sources</id> | |||||
| <goals> | |||||
| <goal>jar-no-fork</goal> | |||||
| </goals> | |||||
| </execution> | |||||
| </executions> | |||||
| </plugin> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-javadoc-plugin</artifactId> | |||||
| <version>2.9.1</version> | |||||
| <executions> | |||||
| <execution> | |||||
| <id>attach-javadocs</id> | |||||
| <goals> | |||||
| <goal>jar</goal> | |||||
| </goals> | |||||
| </execution> | |||||
| </executions> | |||||
| <configuration> | |||||
| <charset>UTF-8</charset> | |||||
| <locale>zh_CN</locale> | |||||
| </configuration> | |||||
| </plugin> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-gpg-plugin</artifactId> | |||||
| <version>1.5</version> | |||||
| <executions> | |||||
| <execution> | |||||
| <id>sign-artifacts</id> | |||||
| <phase>verify</phase> | |||||
| <goals> | |||||
| <goal>sign</goal> | |||||
| </goals> | |||||
| </execution> | |||||
| </executions> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </profile> | |||||
| </profiles> | |||||
| <build> | |||||
| <pluginManagement> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-surefire-plugin</artifactId> | |||||
| <version>2.17</version> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </pluginManagement> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.sonatype.plugins</groupId> | |||||
| <artifactId>nexus-staging-maven-plugin</artifactId> | |||||
| <version>1.6.3</version> | |||||
| <extensions>true</extensions> | |||||
| <configuration> | |||||
| <serverId>ossrh</serverId> | |||||
| <nexusUrl>https://oss.sonatype.org/</nexusUrl> | |||||
| <autoReleaseAfterClose>false</autoReleaseAfterClose> | |||||
| </configuration> | |||||
| </plugin> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-release-plugin</artifactId> | |||||
| <version>2.5</version> | |||||
| <configuration> | |||||
| <autoVersionSubmodules>true</autoVersionSubmodules> | |||||
| <useReleaseProfile>false</useReleaseProfile> | |||||
| <releaseProfiles>release</releaseProfiles> | |||||
| <goals>deploy</goals> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </project> | |||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.0.4</version> | |||||
| <version>1.0.5</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-common</artifactId> | <artifactId>weixin-java-common</artifactId> | ||||
| @@ -0,0 +1,68 @@ | |||||
| <?xml version="1.0"?> | |||||
| <project | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||||
| xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>me.chanjar</groupId> | |||||
| <artifactId>weixin-java-parent</artifactId> | |||||
| <version>1.0.5-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>weixin-java-common</artifactId> | |||||
| <name>WeiXin Java Tools - Common</name> | |||||
| <description>微信公众号、企业号Java SDK Common</description> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>junit</groupId> | |||||
| <artifactId>junit</artifactId> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.testng</groupId> | |||||
| <artifactId>testng</artifactId> | |||||
| <version>6.8.7</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.mockito</groupId> | |||||
| <artifactId>mockito-all</artifactId> | |||||
| <version>1.9.5</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.google.inject</groupId> | |||||
| <artifactId>guice</artifactId> | |||||
| <version>3.0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.eclipse.jetty</groupId> | |||||
| <artifactId>jetty-server</artifactId> | |||||
| <version>9.3.0.M0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.eclipse.jetty</groupId> | |||||
| <artifactId>jetty-servlet</artifactId> | |||||
| <version>9.3.0.M0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-surefire-plugin</artifactId> | |||||
| <configuration> | |||||
| <suiteXmlFiles> | |||||
| <suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile> | |||||
| </suiteXmlFiles> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </project> | |||||
| @@ -128,4 +128,12 @@ public class WxConsts { | |||||
| /** 弹出地理位置选择器 */ | /** 弹出地理位置选择器 */ | ||||
| public static final String LOCATION_SELECT = "location_select"; | public static final String LOCATION_SELECT = "location_select"; | ||||
| /////////////////////// | |||||
| // oauth2网页授权的scope | |||||
| /////////////////////// | |||||
| /** 不弹出授权页面,直接跳转,只能获取用户openid */ | |||||
| public static final String OAUTH2_SCOPE_BASE = "snsapi_base"; | |||||
| /** 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 */ | |||||
| public static final String OAUTH2_SCOPE_USER_INFO = "snsapi_userinfo"; | |||||
| } | } | ||||
| @@ -0,0 +1,100 @@ | |||||
| package me.chanjar.weixin.common.util; | |||||
| /** | |||||
| * copy from apache-commons-lang3 | |||||
| */ | |||||
| public class StringUtils { | |||||
| /** | |||||
| * <p>Checks if a CharSequence is whitespace, empty ("") or null.</p> | |||||
| * | |||||
| * <pre> | |||||
| * StringUtils.isBlank(null) = true | |||||
| * StringUtils.isBlank("") = true | |||||
| * StringUtils.isBlank(" ") = true | |||||
| * StringUtils.isBlank("bob") = false | |||||
| * StringUtils.isBlank(" bob ") = false | |||||
| * </pre> | |||||
| * | |||||
| * @param cs the CharSequence to check, may be null | |||||
| * @return {@code true} if the CharSequence is null, empty or whitespace | |||||
| * @since 2.0 | |||||
| * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence) | |||||
| */ | |||||
| public static boolean isBlank(CharSequence cs) { | |||||
| int strLen; | |||||
| if (cs == null || (strLen = cs.length()) == 0) { | |||||
| return true; | |||||
| } | |||||
| for (int i = 0; i < strLen; i++) { | |||||
| if (Character.isWhitespace(cs.charAt(i)) == false) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p> | |||||
| * | |||||
| * <pre> | |||||
| * StringUtils.isNotBlank(null) = false | |||||
| * StringUtils.isNotBlank("") = false | |||||
| * StringUtils.isNotBlank(" ") = false | |||||
| * StringUtils.isNotBlank("bob") = true | |||||
| * StringUtils.isNotBlank(" bob ") = true | |||||
| * </pre> | |||||
| * | |||||
| * @param cs the CharSequence to check, may be null | |||||
| * @return {@code true} if the CharSequence is | |||||
| * not empty and not null and not whitespace | |||||
| * @since 2.0 | |||||
| * @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence) | |||||
| */ | |||||
| public static boolean isNotBlank(CharSequence cs) { | |||||
| return !StringUtils.isBlank(cs); | |||||
| } | |||||
| /** | |||||
| * <p>Checks if a CharSequence is empty ("") or null.</p> | |||||
| * | |||||
| * <pre> | |||||
| * StringUtils.isEmpty(null) = true | |||||
| * StringUtils.isEmpty("") = true | |||||
| * StringUtils.isEmpty(" ") = false | |||||
| * StringUtils.isEmpty("bob") = false | |||||
| * StringUtils.isEmpty(" bob ") = false | |||||
| * </pre> | |||||
| * | |||||
| * <p>NOTE: This method changed in Lang version 2.0. | |||||
| * It no longer trims the CharSequence. | |||||
| * That functionality is available in isBlank().</p> | |||||
| * | |||||
| * @param cs the CharSequence to check, may be null | |||||
| * @return {@code true} if the CharSequence is empty or null | |||||
| * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence) | |||||
| */ | |||||
| public static boolean isEmpty(CharSequence cs) { | |||||
| return cs == null || cs.length() == 0; | |||||
| } | |||||
| /** | |||||
| * <p>Checks if a CharSequence is not empty ("") and not null.</p> | |||||
| * | |||||
| * <pre> | |||||
| * StringUtils.isNotEmpty(null) = false | |||||
| * StringUtils.isNotEmpty("") = false | |||||
| * StringUtils.isNotEmpty(" ") = true | |||||
| * StringUtils.isNotEmpty("bob") = true | |||||
| * StringUtils.isNotEmpty(" bob ") = true | |||||
| * </pre> | |||||
| * | |||||
| * @param cs the CharSequence to check, may be null | |||||
| * @return {@code true} if the CharSequence is not empty and not null | |||||
| * @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence) | |||||
| */ | |||||
| public static boolean isNotEmpty(CharSequence cs) { | |||||
| return !StringUtils.isEmpty(cs); | |||||
| } | |||||
| } | |||||
| @@ -1,12 +1,9 @@ | |||||
| package me.chanjar.weixin.common.util.http; | package me.chanjar.weixin.common.util.http; | ||||
| import me.chanjar.weixin.common.bean.result.WxError; | import me.chanjar.weixin.common.bean.result.WxError; | ||||
| import me.chanjar.weixin.common.util.fs.FileUtils; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import me.chanjar.weixin.common.util.http.InputStreamResponseHandler; | |||||
| import me.chanjar.weixin.common.util.http.RequestExecutor; | |||||
| import me.chanjar.weixin.common.util.http.Utf8ResponseHandler; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import me.chanjar.weixin.common.util.StringUtils; | |||||
| import me.chanjar.weixin.common.util.fs.FileUtils; | |||||
| import org.apache.http.Header; | import org.apache.http.Header; | ||||
| import org.apache.http.HttpHost; | import org.apache.http.HttpHost; | ||||
| import org.apache.http.client.ClientProtocolException; | import org.apache.http.client.ClientProtocolException; | ||||
| @@ -0,0 +1,47 @@ | |||||
| package me.chanjar.weixin.common.util.http; | |||||
| import me.chanjar.weixin.common.util.StringUtils; | |||||
| import java.io.UnsupportedEncodingException; | |||||
| public class URIUtil { | |||||
| private static final String ALLOWED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"; | |||||
| public static String encodeURIComponent(String input) { | |||||
| if (StringUtils.isEmpty(input)) { | |||||
| return input; | |||||
| } | |||||
| int l = input.length(); | |||||
| StringBuilder o = new StringBuilder(l * 3); | |||||
| try { | |||||
| for (int i = 0; i < l; i++) { | |||||
| String e = input.substring(i, i + 1); | |||||
| if (ALLOWED_CHARS.indexOf(e) == -1) { | |||||
| byte[] b = e.getBytes("utf-8"); | |||||
| o.append(getHex(b)); | |||||
| continue; | |||||
| } | |||||
| o.append(e); | |||||
| } | |||||
| return o.toString(); | |||||
| } catch (UnsupportedEncodingException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return input; | |||||
| } | |||||
| private static String getHex(byte buf[]) { | |||||
| StringBuilder o = new StringBuilder(buf.length * 3); | |||||
| for (int i = 0; i < buf.length; i++) { | |||||
| int n = (int) buf[i] & 0xff; | |||||
| o.append("%"); | |||||
| if (n < 0x10) { | |||||
| o.append("0"); | |||||
| } | |||||
| o.append(Long.toString(n, 16).toUpperCase()); | |||||
| } | |||||
| return o.toString(); | |||||
| } | |||||
| } | |||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.0.4</version> | |||||
| <version>1.0.5</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
| @@ -0,0 +1,73 @@ | |||||
| <?xml version="1.0"?> | |||||
| <project | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||||
| xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>me.chanjar</groupId> | |||||
| <artifactId>weixin-java-parent</artifactId> | |||||
| <version>1.0.5-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>weixin-java-cp</artifactId> | |||||
| <name>WeiXin Java Tools - CP</name> | |||||
| <description>微信企业号Java SDK</description> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>me.chanjar</groupId> | |||||
| <artifactId>weixin-java-common</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>junit</groupId> | |||||
| <artifactId>junit</artifactId> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.testng</groupId> | |||||
| <artifactId>testng</artifactId> | |||||
| <version>6.8.7</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.mockito</groupId> | |||||
| <artifactId>mockito-all</artifactId> | |||||
| <version>1.9.5</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.google.inject</groupId> | |||||
| <artifactId>guice</artifactId> | |||||
| <version>3.0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.eclipse.jetty</groupId> | |||||
| <artifactId>jetty-server</artifactId> | |||||
| <version>9.3.0.M0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.eclipse.jetty</groupId> | |||||
| <artifactId>jetty-servlet</artifactId> | |||||
| <version>9.3.0.M0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-surefire-plugin</artifactId> | |||||
| <configuration> | |||||
| <suiteXmlFiles> | |||||
| <suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile> | |||||
| </suiteXmlFiles> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </project> | |||||
| @@ -27,6 +27,8 @@ public interface WxCpConfigStorage { | |||||
| public int getExpiresIn(); | public int getExpiresIn(); | ||||
| public String getOauth2redirectUri(); | |||||
| public String getHttp_proxy_host(); | public String getHttp_proxy_host(); | ||||
| public int getHttp_proxy_port(); | public int getHttp_proxy_port(); | ||||
| @@ -18,6 +18,8 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| protected String agentId; | protected String agentId; | ||||
| protected int expiresIn; | protected int expiresIn; | ||||
| protected String oauth2redirectUri; | |||||
| protected String http_proxy_host; | protected String http_proxy_host; | ||||
| protected int http_proxy_port; | protected int http_proxy_port; | ||||
| protected String http_proxy_username; | protected String http_proxy_username; | ||||
| @@ -88,6 +90,15 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| this.agentId = agentId; | this.agentId = agentId; | ||||
| } | } | ||||
| @Override | |||||
| public String getOauth2redirectUri() { | |||||
| return this.oauth2redirectUri; | |||||
| } | |||||
| public void setOauth2redirectUri(String oauth2redirectUri) { | |||||
| this.oauth2redirectUri = oauth2redirectUri; | |||||
| } | |||||
| public String getHttp_proxy_host() { | public String getHttp_proxy_host() { | ||||
| return http_proxy_host; | return http_proxy_host; | ||||
| } | } | ||||
| @@ -16,8 +16,9 @@ public interface WxCpMessageHandler { | |||||
| * | * | ||||
| * @param wxMessage | * @param wxMessage | ||||
| * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | ||||
| * @param wxCpService | |||||
| * @return xml格式的消息,如果在异步规则里处理的话,可以返回null | * @return xml格式的消息,如果在异步规则里处理的话,可以返回null | ||||
| */ | */ | ||||
| public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context); | |||||
| public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService); | |||||
| } | } | ||||
| @@ -15,8 +15,9 @@ public interface WxCpMessageInterceptor { | |||||
| * 拦截微信消息 | * 拦截微信消息 | ||||
| * @param wxMessage | * @param wxMessage | ||||
| * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | ||||
| * @param wxCpService | |||||
| * @return true代表OK,false代表不OK | * @return true代表OK,false代表不OK | ||||
| */ | */ | ||||
| public boolean intercept(WxCpXmlMessage wxMessage, Map<String, Object> context); | |||||
| public boolean intercept(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService); | |||||
| } | } | ||||
| @@ -41,16 +41,30 @@ import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | |||||
| */ | */ | ||||
| public class WxCpMessageRouter { | public class WxCpMessageRouter { | ||||
| private static final int DEFAULT_THREAD_POOL_SIZE = 20; | |||||
| private final List<Rule> rules = new ArrayList<Rule>(); | private final List<Rule> rules = new ArrayList<Rule>(); | ||||
| private final ExecutorService es = Executors.newCachedThreadPool(); | |||||
| private final ExecutorService executorService; | |||||
| private final WxCpService wxCpService; | |||||
| public WxCpMessageRouter(WxCpService wxCpService) { | |||||
| this.wxCpService = wxCpService; | |||||
| this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); | |||||
| } | |||||
| public WxCpMessageRouter(WxCpService wxMpService, int threadPoolSize) { | |||||
| this.wxCpService = wxMpService; | |||||
| this.executorService = Executors.newFixedThreadPool(threadPoolSize); | |||||
| } | |||||
| /** | /** | ||||
| * 开始一个新的Route规则 | * 开始一个新的Route规则 | ||||
| * @return | * @return | ||||
| */ | */ | ||||
| public Rule rule() { | public Rule rule() { | ||||
| return new Rule(this); | |||||
| return new Rule(this, wxCpService); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -73,7 +87,7 @@ public class WxCpMessageRouter { | |||||
| if (matchRules.get(0).async) { | if (matchRules.get(0).async) { | ||||
| // 只要第一个是异步的,那就异步执行 | // 只要第一个是异步的,那就异步执行 | ||||
| // 在另一个线程里执行 | // 在另一个线程里执行 | ||||
| es.submit(new Runnable() { | |||||
| executorService.submit(new Runnable() { | |||||
| public void run() { | public void run() { | ||||
| for (final Rule rule : matchRules) { | for (final Rule rule : matchRules) { | ||||
| rule.service(wxMessage); | rule.service(wxMessage); | ||||
| @@ -101,8 +115,12 @@ public class WxCpMessageRouter { | |||||
| private final WxCpMessageRouter routerBuilder; | private final WxCpMessageRouter routerBuilder; | ||||
| private final WxCpService wxCpService; | |||||
| private boolean async = true; | private boolean async = true; | ||||
| private String fromUser; | |||||
| private String msgType; | private String msgType; | ||||
| private String event; | private String event; | ||||
| @@ -121,8 +139,9 @@ public class WxCpMessageRouter { | |||||
| private List<WxCpMessageInterceptor> interceptors = new ArrayList<WxCpMessageInterceptor>(); | private List<WxCpMessageInterceptor> interceptors = new ArrayList<WxCpMessageInterceptor>(); | ||||
| protected Rule(WxCpMessageRouter routerBuilder) { | |||||
| protected Rule(WxCpMessageRouter routerBuilder, WxCpService wxCpService) { | |||||
| this.routerBuilder = routerBuilder; | this.routerBuilder = routerBuilder; | ||||
| this.wxCpService = wxCpService; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -195,6 +214,16 @@ public class WxCpMessageRouter { | |||||
| return this; | return this; | ||||
| } | } | ||||
| /** | |||||
| * 如果fromUser等于某值 | |||||
| * @param fromUser | |||||
| * @return | |||||
| */ | |||||
| public Rule fromUser(String fromUser) { | |||||
| this.fromUser = fromUser; | |||||
| return this; | |||||
| } | |||||
| /** | /** | ||||
| * 设置微信消息拦截器 | * 设置微信消息拦截器 | ||||
| * @param interceptor | * @param interceptor | ||||
| @@ -265,6 +294,8 @@ public class WxCpMessageRouter { | |||||
| protected boolean test(WxCpXmlMessage wxMessage) { | protected boolean test(WxCpXmlMessage wxMessage) { | ||||
| return | return | ||||
| (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName())) | |||||
| && | |||||
| (this.agentId == null || this.agentId.equals(wxMessage.getAgentId())) | (this.agentId == null || this.agentId.equals(wxMessage.getAgentId())) | ||||
| && | && | ||||
| (this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) | (this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) | ||||
| @@ -288,7 +319,7 @@ public class WxCpMessageRouter { | |||||
| Map<String, Object> context = new HashMap<String, Object>(); | Map<String, Object> context = new HashMap<String, Object>(); | ||||
| // 如果拦截器不通过 | // 如果拦截器不通过 | ||||
| for (WxCpMessageInterceptor interceptor : this.interceptors) { | for (WxCpMessageInterceptor interceptor : this.interceptors) { | ||||
| if (!interceptor.intercept(wxMessage, context)) { | |||||
| if (!interceptor.intercept(wxMessage, context, wxCpService)) { | |||||
| return null; | return null; | ||||
| } | } | ||||
| } | } | ||||
| @@ -297,7 +328,7 @@ public class WxCpMessageRouter { | |||||
| WxCpXmlOutMessage res = null; | WxCpXmlOutMessage res = null; | ||||
| for (WxCpMessageHandler handler : this.handlers) { | for (WxCpMessageHandler handler : this.handlers) { | ||||
| // 返回最后handler的结果 | // 返回最后handler的结果 | ||||
| res = handler.handle(wxMessage, context); | |||||
| res = handler.handle(wxMessage, context, wxCpService); | |||||
| } | } | ||||
| return res; | return res; | ||||
| } | } | ||||
| @@ -274,6 +274,27 @@ public interface WxCpService { | |||||
| */ | */ | ||||
| public void tagAddUsers(String tagId, List<String> userIds) throws WxErrorException; | public void tagAddUsers(String tagId, List<String> userIds) throws WxErrorException; | ||||
| /** | |||||
| * <pre> | |||||
| * 构造oauth2授权的url连接 | |||||
| * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | |||||
| * </pre> | |||||
| * @param state | |||||
| * @return code | |||||
| */ | |||||
| public String oauth2buildAuthorizationUrl(String state); | |||||
| /** | |||||
| * <pre> | |||||
| * 用oauth2获取用户信息 | |||||
| * http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息 | |||||
| * 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。 | |||||
| * </pre> | |||||
| * @param code | |||||
| * @return [userid, deviceid] | |||||
| */ | |||||
| public String[] oauth2getUserInfo(String code) throws WxErrorException; | |||||
| /** | /** | ||||
| * 移除标签成员 | * 移除标签成员 | ||||
| * | * | ||||
| @@ -1,25 +1,27 @@ | |||||
| package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
| import java.io.File; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.io.StringReader; | |||||
| import java.util.List; | |||||
| import java.util.UUID; | |||||
| import java.util.concurrent.atomic.AtomicBoolean; | |||||
| import com.google.gson.JsonArray; | import com.google.gson.JsonArray; | ||||
| import com.google.gson.JsonElement; | |||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
| import com.google.gson.JsonPrimitive; | import com.google.gson.JsonPrimitive; | ||||
| import com.google.gson.internal.Streams; | |||||
| import com.google.gson.reflect.TypeToken; | |||||
| import com.google.gson.stream.JsonReader; | |||||
| import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
| import me.chanjar.weixin.common.bean.WxMenu; | import me.chanjar.weixin.common.bean.WxMenu; | ||||
| import me.chanjar.weixin.common.bean.result.WxError; | |||||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
| import me.chanjar.weixin.common.util.json.GsonHelper; | |||||
| import me.chanjar.weixin.cp.bean.*; | |||||
| import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||||
| import me.chanjar.weixin.common.util.StringUtils; | |||||
| import me.chanjar.weixin.common.util.crypto.SHA1; | import me.chanjar.weixin.common.util.crypto.SHA1; | ||||
| import me.chanjar.weixin.common.util.fs.FileUtils; | |||||
| import me.chanjar.weixin.common.util.http.*; | |||||
| import me.chanjar.weixin.common.util.json.GsonHelper; | |||||
| import me.chanjar.weixin.cp.bean.WxCpDepart; | |||||
| import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
| import me.chanjar.weixin.cp.bean.WxCpTag; | |||||
| import me.chanjar.weixin.cp.bean.WxCpUser; | |||||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.apache.http.HttpHost; | import org.apache.http.HttpHost; | ||||
| import org.apache.http.auth.AuthScope; | import org.apache.http.auth.AuthScope; | ||||
| import org.apache.http.auth.UsernamePasswordCredentials; | import org.apache.http.auth.UsernamePasswordCredentials; | ||||
| @@ -33,20 +35,13 @@ import org.apache.http.impl.client.BasicResponseHandler; | |||||
| import org.apache.http.impl.client.CloseableHttpClient; | import org.apache.http.impl.client.CloseableHttpClient; | ||||
| import org.apache.http.impl.client.HttpClients; | import org.apache.http.impl.client.HttpClients; | ||||
| import me.chanjar.weixin.cp.bean.WxCpDepart; | |||||
| import me.chanjar.weixin.common.bean.result.WxError; | |||||
| import me.chanjar.weixin.cp.bean.WxCpUser; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||||
| import me.chanjar.weixin.common.util.fs.FileUtils; | |||||
| import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; | |||||
| import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||||
| import me.chanjar.weixin.common.util.http.RequestExecutor; | |||||
| import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||||
| import com.google.gson.JsonElement; | |||||
| import com.google.gson.internal.Streams; | |||||
| import com.google.gson.reflect.TypeToken; | |||||
| import com.google.gson.stream.JsonReader; | |||||
| import java.io.File; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.io.StringReader; | |||||
| import java.util.List; | |||||
| import java.util.UUID; | |||||
| import java.util.concurrent.atomic.AtomicBoolean; | |||||
| public class WxCpServiceImpl implements WxCpService { | public class WxCpServiceImpl implements WxCpService { | ||||
| @@ -321,6 +316,41 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| execute(new SimplePostRequestExecutor(), url, jsonObject.toString()); | execute(new SimplePostRequestExecutor(), url, jsonObject.toString()); | ||||
| } | } | ||||
| @Override | |||||
| public String oauth2buildAuthorizationUrl(String state) { | |||||
| String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" ; | |||||
| url += "appid=" + wxCpConfigStorage.getCorpId(); | |||||
| url += "&redirect_uri=" + URIUtil.encodeURIComponent(wxCpConfigStorage.getOauth2redirectUri()); | |||||
| url += "&response_type=code"; | |||||
| url += "&scope=snsapi_base"; | |||||
| if (state != null) { | |||||
| url += "&state=" + state; | |||||
| } | |||||
| url += "#wechat_redirect"; | |||||
| return url; | |||||
| } | |||||
| @Override | |||||
| public String[] oauth2getUserInfo(String code) throws WxErrorException { | |||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?"; | |||||
| url += "access_token=" + wxCpConfigStorage.getAccessToken(); | |||||
| url += "&code=" + code; | |||||
| url += "agendid=" + wxCpConfigStorage.getAgentId(); | |||||
| try { | |||||
| RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||||
| String responseText = executor.execute(getHttpclient(), httpProxy, url, null); | |||||
| JsonElement je = Streams.parse(new JsonReader(new StringReader(responseText))); | |||||
| JsonObject jo = je.getAsJsonObject(); | |||||
| return new String[] {GsonHelper.getString(jo, "UserId"), GsonHelper.getString(jo, "DeviceId")}; | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| public String get(String url, String queryParam) throws WxErrorException { | public String get(String url, String queryParam) throws WxErrorException { | ||||
| return execute(new SimpleGetRequestExecutor(), url, queryParam); | return execute(new SimpleGetRequestExecutor(), url, queryParam); | ||||
| } | } | ||||
| @@ -10,7 +10,7 @@ import java.util.List; | |||||
| @XmlRootElement(name = "xml") | @XmlRootElement(name = "xml") | ||||
| @XmlAccessorType(XmlAccessType.FIELD) | @XmlAccessorType(XmlAccessType.FIELD) | ||||
| public class WxCpXmlOutMewsMessage extends WxCpXmlOutMessage { | |||||
| public class WxCpXmlOutNewsMessage extends WxCpXmlOutMessage { | |||||
| @XmlElement(name = "ArticleCount") | @XmlElement(name = "ArticleCount") | ||||
| protected int articleCount; | protected int articleCount; | ||||
| @@ -19,7 +19,7 @@ public class WxCpXmlOutMewsMessage extends WxCpXmlOutMessage { | |||||
| @XmlElement(name = "item") | @XmlElement(name = "item") | ||||
| protected final List<Item> articles = new ArrayList<Item>(); | protected final List<Item> articles = new ArrayList<Item>(); | ||||
| public WxCpXmlOutMewsMessage() { | |||||
| public WxCpXmlOutNewsMessage() { | |||||
| this.msgType = WxConsts.XML_MSG_NEWS; | this.msgType = WxConsts.XML_MSG_NEWS; | ||||
| } | } | ||||
| @@ -3,14 +3,14 @@ package me.chanjar.weixin.cp.bean.outxmlbuilder; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutMewsMessage; | |||||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutMewsMessage.Item; | |||||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage; | |||||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage.Item; | |||||
| /** | /** | ||||
| * 图文消息builder | * 图文消息builder | ||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| */ | */ | ||||
| public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxCpXmlOutMewsMessage> { | |||||
| public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxCpXmlOutNewsMessage> { | |||||
| protected final List<Item> articles = new ArrayList<Item>(); | protected final List<Item> articles = new ArrayList<Item>(); | ||||
| @@ -19,8 +19,8 @@ public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxCpXmlOutMewsMe | |||||
| return this; | return this; | ||||
| } | } | ||||
| public WxCpXmlOutMewsMessage build() { | |||||
| WxCpXmlOutMewsMessage m = new WxCpXmlOutMewsMessage(); | |||||
| public WxCpXmlOutNewsMessage build() { | |||||
| WxCpXmlOutNewsMessage m = new WxCpXmlOutNewsMessage(); | |||||
| for(Item item : articles) { | for(Item item : articles) { | ||||
| m.addArticle(item); | m.addArticle(item); | ||||
| } | } | ||||
| @@ -8,17 +8,12 @@ | |||||
| */ | */ | ||||
| package me.chanjar.weixin.cp.util.json; | package me.chanjar.weixin.cp.util.json; | ||||
| import java.lang.reflect.Type; | |||||
| import com.google.gson.*; | |||||
| import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
| import me.chanjar.weixin.common.util.StringUtils; | |||||
| import me.chanjar.weixin.cp.bean.WxCpMessage; | import me.chanjar.weixin.cp.bean.WxCpMessage; | ||||
| import com.google.gson.JsonArray; | |||||
| import com.google.gson.JsonElement; | |||||
| import com.google.gson.JsonObject; | |||||
| import com.google.gson.JsonSerializationContext; | |||||
| import com.google.gson.JsonSerializer; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import java.lang.reflect.Type; | |||||
| /** | /** | ||||
| * | * | ||||
| @@ -36,7 +31,7 @@ public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | |||||
| messageJson.addProperty("msgtype", message.getMsgType()); | messageJson.addProperty("msgtype", message.getMsgType()); | ||||
| if (StringUtils.isNotBlank(message.getToParty())) { | if (StringUtils.isNotBlank(message.getToParty())) { | ||||
| messageJson.addProperty("toparty", message.getToUser()); | |||||
| messageJson.addProperty("toparty", message.getToParty()); | |||||
| } | } | ||||
| if (StringUtils.isNotBlank(message.getToTag())) { | if (StringUtils.isNotBlank(message.getToTag())) { | ||||
| messageJson.addProperty("totag", message.getToUser()); | messageJson.addProperty("totag", message.getToUser()); | ||||
| @@ -75,6 +70,7 @@ public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | |||||
| } | } | ||||
| if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { | if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { | ||||
| JsonObject newsJsonObject = new JsonObject(); | |||||
| JsonArray articleJsonArray = new JsonArray(); | JsonArray articleJsonArray = new JsonArray(); | ||||
| for (WxCpMessage.WxArticle article : message.getArticles()) { | for (WxCpMessage.WxArticle article : message.getArticles()) { | ||||
| JsonObject articleJson = new JsonObject(); | JsonObject articleJson = new JsonObject(); | ||||
| @@ -84,7 +80,8 @@ public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | |||||
| articleJson.addProperty("picurl", article.getPicUrl()); | articleJson.addProperty("picurl", article.getPicUrl()); | ||||
| articleJsonArray.add(articleJson); | articleJsonArray.add(articleJson); | ||||
| } | } | ||||
| messageJson.add("articles", articleJsonArray); | |||||
| newsJsonObject.add("articles", articleJsonArray); | |||||
| messageJson.add("news", newsJsonObject); | |||||
| } | } | ||||
| return messageJson; | return messageJson; | ||||
| @@ -59,6 +59,7 @@ public class XmlTransformer { | |||||
| Marshaller m = JAXB_CONTEXT.createMarshaller(); | Marshaller m = JAXB_CONTEXT.createMarshaller(); | ||||
| m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); | m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); | ||||
| m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER); | m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER); | ||||
| m.setProperty(Marshaller.JAXB_FRAGMENT, true); | |||||
| m.marshal(object, writer); | m.marshal(object, writer); | ||||
| } | } | ||||
| @@ -79,7 +80,7 @@ public class XmlTransformer { | |||||
| return JAXBContext.newInstance( | return JAXBContext.newInstance( | ||||
| WxCpXmlOutMessage.class, | WxCpXmlOutMessage.class, | ||||
| WxCpXmlOutImageMessage.class, | WxCpXmlOutImageMessage.class, | ||||
| WxCpXmlOutMewsMessage.class, | |||||
| WxCpXmlOutNewsMessage.class, | |||||
| WxCpXmlOutTextMessage.class, | WxCpXmlOutTextMessage.class, | ||||
| WxCpXmlOutVideoMessage.class, | WxCpXmlOutVideoMessage.class, | ||||
| WxCpXmlOutVoiceMessage.class, | WxCpXmlOutVoiceMessage.class, | ||||
| @@ -1,6 +1,6 @@ | |||||
| package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import me.chanjar.weixin.common.util.StringUtils; | |||||
| import org.testng.Assert; | import org.testng.Assert; | ||||
| import org.testng.annotations.Guice; | import org.testng.annotations.Guice; | ||||
| import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
| @@ -47,7 +47,7 @@ public class WxCpMessageRouterTest { | |||||
| @Test(dataProvider="messages-1") | @Test(dataProvider="messages-1") | ||||
| public void testSync(WxCpXmlMessage message, String expected) { | public void testSync(WxCpXmlMessage message, String expected) { | ||||
| StringBuffer sb = new StringBuffer(); | StringBuffer sb = new StringBuffer(); | ||||
| WxCpMessageRouter router = new WxCpMessageRouter(); | |||||
| WxCpMessageRouter router = new WxCpMessageRouter(null); | |||||
| prepare(false, sb, router); | prepare(false, sb, router); | ||||
| router.route(message); | router.route(message); | ||||
| Assert.assertEquals(sb.toString(), expected); | Assert.assertEquals(sb.toString(), expected); | ||||
| @@ -56,7 +56,7 @@ public class WxCpMessageRouterTest { | |||||
| @Test(dataProvider="messages-1") | @Test(dataProvider="messages-1") | ||||
| public void testAsync(WxCpXmlMessage message, String expected) throws InterruptedException { | public void testAsync(WxCpXmlMessage message, String expected) throws InterruptedException { | ||||
| StringBuffer sb = new StringBuffer(); | StringBuffer sb = new StringBuffer(); | ||||
| WxCpMessageRouter router = new WxCpMessageRouter(); | |||||
| WxCpMessageRouter router = new WxCpMessageRouter(null); | |||||
| prepare(true, sb, router); | prepare(true, sb, router); | ||||
| router.route(message); | router.route(message); | ||||
| Thread.sleep(500l); | Thread.sleep(500l); | ||||
| @@ -64,10 +64,10 @@ public class WxCpMessageRouterTest { | |||||
| } | } | ||||
| public void testConcurrency() throws InterruptedException { | public void testConcurrency() throws InterruptedException { | ||||
| final WxCpMessageRouter router = new WxCpMessageRouter(); | |||||
| final WxCpMessageRouter router = new WxCpMessageRouter(null); | |||||
| router.rule().handler(new WxCpMessageHandler() { | router.rule().handler(new WxCpMessageHandler() { | ||||
| @Override | @Override | ||||
| public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context) { | |||||
| public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService) { | |||||
| return null; | return null; | ||||
| } | } | ||||
| }).end(); | }).end(); | ||||
| @@ -149,7 +149,7 @@ public class WxCpMessageRouterTest { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context) { | |||||
| public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService) { | |||||
| sb.append(this.echoStr).append(','); | sb.append(this.echoStr).append(','); | ||||
| return null; | return null; | ||||
| } | } | ||||
| @@ -83,8 +83,8 @@ public class WxCpMessageTest { | |||||
| article2.setTitle("Happy Day"); | article2.setTitle("Happy Day"); | ||||
| reply.getArticles().add(article2); | reply.getArticles().add(article2); | ||||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}"); | |||||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||||
| } | } | ||||
| public void testNewsBuild() { | public void testNewsBuild() { | ||||
| @@ -101,8 +101,8 @@ public class WxCpMessageTest { | |||||
| article2.setTitle("Happy Day"); | article2.setTitle("Happy Day"); | ||||
| WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); | WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); | ||||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}"); | |||||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||||
| } | } | ||||
| } | } | ||||
| @@ -13,8 +13,7 @@ public class WxCpXmlOutImageMessageTest { | |||||
| m.setFromUserName("from"); | m.setFromUserName("from"); | ||||
| m.setToUserName("to"); | m.setToUserName("to"); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -27,8 +26,7 @@ public class WxCpXmlOutImageMessageTest { | |||||
| public void testBuild() { | public void testBuild() { | ||||
| WxCpXmlOutImageMessage m = WxCpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | WxCpXmlOutImageMessage m = WxCpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -7,20 +7,19 @@ import org.testng.annotations.Test; | |||||
| public class WxCpXmlOutNewsMessageTest { | public class WxCpXmlOutNewsMessageTest { | ||||
| public void test() { | public void test() { | ||||
| WxCpXmlOutMewsMessage m = new WxCpXmlOutMewsMessage(); | |||||
| WxCpXmlOutNewsMessage m = new WxCpXmlOutNewsMessage(); | |||||
| m.setCreateTime(1122l); | m.setCreateTime(1122l); | ||||
| m.setFromUserName("fromUser"); | m.setFromUserName("fromUser"); | ||||
| m.setToUserName("toUser"); | m.setToUserName("toUser"); | ||||
| WxCpXmlOutMewsMessage.Item item = new WxCpXmlOutMewsMessage.Item(); | |||||
| WxCpXmlOutNewsMessage.Item item = new WxCpXmlOutNewsMessage.Item(); | |||||
| item.setDescription("description"); | item.setDescription("description"); | ||||
| item.setPicUrl("picUrl"); | item.setPicUrl("picUrl"); | ||||
| item.setTitle("title"); | item.setTitle("title"); | ||||
| item.setUrl("url"); | item.setUrl("url"); | ||||
| m.addArticle(item); | m.addArticle(item); | ||||
| m.addArticle(item); | m.addArticle(item); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -46,20 +45,19 @@ public class WxCpXmlOutNewsMessageTest { | |||||
| } | } | ||||
| public void testBuild() { | public void testBuild() { | ||||
| WxCpXmlOutMewsMessage.Item item = new WxCpXmlOutMewsMessage.Item(); | |||||
| WxCpXmlOutNewsMessage.Item item = new WxCpXmlOutNewsMessage.Item(); | |||||
| item.setDescription("description"); | item.setDescription("description"); | ||||
| item.setPicUrl("picUrl"); | item.setPicUrl("picUrl"); | ||||
| item.setTitle("title"); | item.setTitle("title"); | ||||
| item.setUrl("url"); | item.setUrl("url"); | ||||
| WxCpXmlOutMewsMessage m = WxCpXmlOutMessage.NEWS() | |||||
| WxCpXmlOutNewsMessage m = WxCpXmlOutMessage.NEWS() | |||||
| .fromUser("fromUser") | .fromUser("fromUser") | ||||
| .toUser("toUser") | .toUser("toUser") | ||||
| .addArticle(item) | .addArticle(item) | ||||
| .addArticle(item) | .addArticle(item) | ||||
| .build(); | .build(); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -13,8 +13,7 @@ public class WxCpXmlOutTextMessageTest { | |||||
| m.setFromUserName("from"); | m.setFromUserName("from"); | ||||
| m.setToUserName("to"); | m.setToUserName("to"); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -27,8 +26,7 @@ public class WxCpXmlOutTextMessageTest { | |||||
| public void testBuild() { | public void testBuild() { | ||||
| WxCpXmlOutTextMessage m = WxCpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); | WxCpXmlOutTextMessage m = WxCpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -15,8 +15,7 @@ public class WxCpXmlOutVideoMessageTest { | |||||
| m.setFromUserName("fromUser"); | m.setFromUserName("fromUser"); | ||||
| m.setToUserName("toUser"); | m.setToUserName("toUser"); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -39,8 +38,7 @@ public class WxCpXmlOutVideoMessageTest { | |||||
| .title("title") | .title("title") | ||||
| .description("ddfff") | .description("ddfff") | ||||
| .build(); | .build(); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -13,8 +13,7 @@ public class WxCpXmlOutVoiceMessageTest { | |||||
| m.setFromUserName("from"); | m.setFromUserName("from"); | ||||
| m.setToUserName("to"); | m.setToUserName("to"); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -27,8 +26,7 @@ public class WxCpXmlOutVoiceMessageTest { | |||||
| public void testBuild() { | public void testBuild() { | ||||
| WxCpXmlOutVoiceMessage m = WxCpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | WxCpXmlOutVoiceMessage m = WxCpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -1,20 +1,94 @@ | |||||
| package me.chanjar.weixin.cp.demo; | package me.chanjar.weixin.cp.demo; | ||||
| import me.chanjar.weixin.cp.api.*; | |||||
| import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | |||||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | |||||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | |||||
| import org.eclipse.jetty.server.Server; | import org.eclipse.jetty.server.Server; | ||||
| import org.eclipse.jetty.servlet.ServletHandler; | import org.eclipse.jetty.servlet.ServletHandler; | ||||
| import org.eclipse.jetty.servlet.ServletHolder; | |||||
| import javax.xml.bind.JAXBException; | |||||
| import java.io.InputStream; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * @author Daniel Qian | |||||
| */ | |||||
| public class WxCpDemoServer { | public class WxCpDemoServer { | ||||
| private static WxCpConfigStorage wxCpConfigStorage; | |||||
| private static WxCpService wxCpService; | |||||
| private static WxCpMessageRouter wxCpMessageRouter; | |||||
| public static void main(String[] args) throws Exception { | public static void main(String[] args) throws Exception { | ||||
| initWeixin(); | |||||
| Server server = new Server(8080); | Server server = new Server(8080); | ||||
| ServletHandler handler = new ServletHandler(); | |||||
| server.setHandler(handler); | |||||
| ServletHandler servletHandler = new ServletHandler(); | |||||
| server.setHandler(servletHandler); | |||||
| ServletHolder endpointServletHolder = new ServletHolder(new WxCpEndpointServlet(wxCpConfigStorage, wxCpService, wxCpMessageRouter)); | |||||
| servletHandler.addServletWithMapping(endpointServletHolder, "/*"); | |||||
| ServletHolder oauthServletHolder = new ServletHolder(new WxCpOAuth2Servlet(wxCpService)); | |||||
| servletHandler.addServletWithMapping(oauthServletHolder, "/oauth2/*"); | |||||
| handler.addServletWithMapping(WxCpDemoServlet.class, "/*"); | |||||
| server.start(); | server.start(); | ||||
| server.join(); | server.join(); | ||||
| } | } | ||||
| private static void initWeixin() { | |||||
| try { | |||||
| InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | |||||
| WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(is1); | |||||
| wxCpConfigStorage = config; | |||||
| wxCpService = new WxCpServiceImpl(); | |||||
| wxCpService.setWxCpConfigStorage(config); | |||||
| WxCpMessageHandler handler = new WxCpMessageHandler() { | |||||
| @Override | |||||
| public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, | |||||
| WxCpService wxCpService) { | |||||
| WxCpXmlOutTextMessage m = WxCpXmlOutMessage | |||||
| .TEXT() | |||||
| .content("测试加密消息") | |||||
| .fromUser(wxMessage.getToUserName()) | |||||
| .toUser(wxMessage.getFromUserName()) | |||||
| .build(); | |||||
| return m; | |||||
| } | |||||
| }; | |||||
| WxCpMessageHandler oauth2handler = new WxCpMessageHandler() { | |||||
| @Override | |||||
| public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, | |||||
| WxCpService wxCpService) { | |||||
| String href = "<a href=\"" + wxCpService.oauth2buildAuthorizationUrl(null) | |||||
| + "\">测试oauth2</a>"; | |||||
| return WxCpXmlOutMessage | |||||
| .TEXT() | |||||
| .content(href) | |||||
| .fromUser(wxMessage.getToUserName()) | |||||
| .toUser(wxMessage.getFromUserName()).build(); | |||||
| } | |||||
| }; | |||||
| wxCpMessageRouter = new WxCpMessageRouter(wxCpService); | |||||
| wxCpMessageRouter | |||||
| .rule() | |||||
| .async(false) | |||||
| .content("哈哈") // 拦截内容为“哈哈”的消息 | |||||
| .handler(handler) | |||||
| .end() | |||||
| .rule() | |||||
| .async(false) | |||||
| .content("oauth") | |||||
| .handler(oauth2handler) | |||||
| .end() | |||||
| ; | |||||
| } catch (JAXBException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -1,11 +1,11 @@ | |||||
| package me.chanjar.weixin.cp.demo; | package me.chanjar.weixin.cp.demo; | ||||
| import me.chanjar.weixin.common.util.StringUtils; | |||||
| import me.chanjar.weixin.cp.api.*; | import me.chanjar.weixin.cp.api.*; | ||||
| import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | ||||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | ||||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | ||||
| import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | ||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import javax.servlet.ServletException; | import javax.servlet.ServletException; | ||||
| import javax.servlet.http.HttpServlet; | import javax.servlet.http.HttpServlet; | ||||
| @@ -19,58 +19,31 @@ import java.util.Map; | |||||
| /** | /** | ||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| */ | */ | ||||
| public class WxCpDemoServlet extends HttpServlet { | |||||
| public class WxCpEndpointServlet extends HttpServlet { | |||||
| protected WxCpService wxCpService; | |||||
| protected WxCpConfigStorage wxCpConfigStorage; | protected WxCpConfigStorage wxCpConfigStorage; | ||||
| protected WxCpService wxCpService; | |||||
| protected WxCpMessageRouter wxCpMessageRouter; | protected WxCpMessageRouter wxCpMessageRouter; | ||||
| @Override public void init() throws ServletException { | |||||
| // | |||||
| super.init(); | |||||
| try { | |||||
| InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | |||||
| WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(is1); | |||||
| wxCpConfigStorage = config; | |||||
| wxCpService = new WxCpServiceImpl(); | |||||
| wxCpService.setWxCpConfigStorage(config); | |||||
| WxCpMessageHandler handler = new WxCpMessageHandler() { | |||||
| @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context) { | |||||
| WxCpXmlOutTextMessage m = WxCpXmlOutMessage | |||||
| .TEXT() | |||||
| .content("测试加密消息") | |||||
| .fromUser(wxMessage.getToUserName()) | |||||
| .toUser(wxMessage.getFromUserName()) | |||||
| .build(); | |||||
| return m; | |||||
| } | |||||
| }; | |||||
| wxCpMessageRouter = new WxCpMessageRouter(); | |||||
| wxCpMessageRouter | |||||
| .rule() | |||||
| .async(false) | |||||
| .content("哈哈") // 拦截内容为“哈哈”的消息 | |||||
| .handler(handler) | |||||
| .end(); | |||||
| } catch (JAXBException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| public WxCpEndpointServlet(WxCpConfigStorage wxCpConfigStorage, WxCpService wxCpService, | |||||
| WxCpMessageRouter wxCpMessageRouter) { | |||||
| this.wxCpConfigStorage = wxCpConfigStorage; | |||||
| this.wxCpService = wxCpService; | |||||
| this.wxCpMessageRouter = wxCpMessageRouter; | |||||
| } | } | ||||
| @Override protected void service(HttpServletRequest request, HttpServletResponse response) | |||||
| @Override | |||||
| protected void service(HttpServletRequest request, HttpServletResponse response) | |||||
| throws ServletException, IOException { | throws ServletException, IOException { | ||||
| response.setContentType("text/html;charset=utf-8"); | |||||
| response.setStatus(HttpServletResponse.SC_OK); | |||||
| String msgSignature = request.getParameter("msg_signature"); | String msgSignature = request.getParameter("msg_signature"); | ||||
| String nonce = request.getParameter("nonce"); | String nonce = request.getParameter("nonce"); | ||||
| String timestamp = request.getParameter("timestamp"); | String timestamp = request.getParameter("timestamp"); | ||||
| String echostr = request.getParameter("echostr"); | String echostr = request.getParameter("echostr"); | ||||
| response.setContentType("text/html;charset=utf-8"); | |||||
| response.setStatus(HttpServletResponse.SC_OK); | |||||
| if (StringUtils.isNotBlank(echostr)) { | if (StringUtils.isNotBlank(echostr)) { | ||||
| if (!wxCpService.checkSignature(msgSignature, timestamp, nonce, echostr)) { | if (!wxCpService.checkSignature(msgSignature, timestamp, nonce, echostr)) { | ||||
| // 消息签名不正确,说明不是公众平台发过来的消息 | // 消息签名不正确,说明不是公众平台发过来的消息 | ||||
| @@ -84,11 +57,9 @@ public class WxCpDemoServlet extends HttpServlet { | |||||
| return; | return; | ||||
| } | } | ||||
| WxCpXmlMessage inMessage = WxCpXmlMessage.fromEncryptedXml(request.getInputStream(), wxCpConfigStorage, timestamp, nonce, msgSignature); | |||||
| WxCpXmlMessage inMessage = WxCpXmlMessage | |||||
| .fromEncryptedXml(request.getInputStream(), wxCpConfigStorage, timestamp, nonce, msgSignature); | |||||
| WxCpXmlOutMessage outMessage = wxCpMessageRouter.route(inMessage); | WxCpXmlOutMessage outMessage = wxCpMessageRouter.route(inMessage); | ||||
| if (outMessage != null) { | if (outMessage != null) { | ||||
| response.getWriter().write(outMessage.toEncryptedXml(wxCpConfigStorage)); | response.getWriter().write(outMessage.toEncryptedXml(wxCpConfigStorage)); | ||||
| } | } | ||||
| @@ -0,0 +1,48 @@ | |||||
| package me.chanjar.weixin.cp.demo; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||||
| import me.chanjar.weixin.cp.api.WxCpService; | |||||
| import javax.servlet.ServletException; | |||||
| import javax.servlet.http.HttpServlet; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletResponse; | |||||
| import java.io.IOException; | |||||
| import java.util.Arrays; | |||||
| /** | |||||
| * Created by qianjia on 14/11/28. | |||||
| */ | |||||
| public class WxCpOAuth2Servlet extends HttpServlet { | |||||
| protected WxCpService wxCpService; | |||||
| public WxCpOAuth2Servlet(WxCpService wxCpService) { | |||||
| this.wxCpService = wxCpService; | |||||
| } | |||||
| @Override protected void service(HttpServletRequest request, HttpServletResponse response) | |||||
| throws ServletException, IOException { | |||||
| response.setContentType("text/html;charset=utf-8"); | |||||
| response.setStatus(HttpServletResponse.SC_OK); | |||||
| String code = request.getParameter("code"); | |||||
| try { | |||||
| response.getWriter().println("<h1>code</h1>"); | |||||
| response.getWriter().println(code); | |||||
| String[] res = wxCpService.oauth2getUserInfo(code); | |||||
| response.getWriter().println("<h1>result</h1>"); | |||||
| response.getWriter().println(Arrays.toString(res)); | |||||
| } catch (WxErrorException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| response.getWriter().flush(); | |||||
| response.getWriter().close(); | |||||
| } | |||||
| } | |||||
| @@ -9,4 +9,5 @@ | |||||
| <userId>企业号通讯录里的某个userid</userId> | <userId>企业号通讯录里的某个userid</userId> | ||||
| <departmentId>企业号通讯录的某个部门id</departmentId> | <departmentId>企业号通讯录的某个部门id</departmentId> | ||||
| <tagId>企业号通讯录里的某个tagid</tagId> | <tagId>企业号通讯录里的某个tagid</tagId> | ||||
| <oauth2redirectUri>网页授权获取用户信息回调地址</oauth2redirectUri> | |||||
| </xml> | </xml> | ||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.0.4</version> | |||||
| <version>1.0.5</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-mp</artifactId> | <artifactId>weixin-java-mp</artifactId> | ||||
| <name>WeiXin Java Tools - MP</name> | <name>WeiXin Java Tools - MP</name> | ||||
| @@ -0,0 +1,72 @@ | |||||
| <?xml version="1.0"?> | |||||
| <project | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||||
| xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>me.chanjar</groupId> | |||||
| <artifactId>weixin-java-parent</artifactId> | |||||
| <version>1.0.5-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>weixin-java-mp</artifactId> | |||||
| <name>WeiXin Java Tools - MP</name> | |||||
| <description>微信公众号Java SDK</description> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>me.chanjar</groupId> | |||||
| <artifactId>weixin-java-common</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>junit</groupId> | |||||
| <artifactId>junit</artifactId> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.testng</groupId> | |||||
| <artifactId>testng</artifactId> | |||||
| <version>6.8.7</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.mockito</groupId> | |||||
| <artifactId>mockito-all</artifactId> | |||||
| <version>1.9.5</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.google.inject</groupId> | |||||
| <artifactId>guice</artifactId> | |||||
| <version>3.0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.eclipse.jetty</groupId> | |||||
| <artifactId>jetty-server</artifactId> | |||||
| <version>9.3.0.M0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.eclipse.jetty</groupId> | |||||
| <artifactId>jetty-servlet</artifactId> | |||||
| <version>9.3.0.M0</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-surefire-plugin</artifactId> | |||||
| <configuration> | |||||
| <suiteXmlFiles> | |||||
| <suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile> | |||||
| </suiteXmlFiles> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </project> | |||||
| @@ -25,6 +25,8 @@ public interface WxMpConfigStorage { | |||||
| public int getExpiresIn(); | public int getExpiresIn(); | ||||
| public String getOauth2redirectUri(); | |||||
| public String getHttp_proxy_host(); | public String getHttp_proxy_host(); | ||||
| public int getHttp_proxy_port(); | public int getHttp_proxy_port(); | ||||
| @@ -16,6 +16,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| protected String aesKey; | protected String aesKey; | ||||
| protected int expiresIn; | protected int expiresIn; | ||||
| protected String oauth2redirectUri; | |||||
| protected String http_proxy_host; | protected String http_proxy_host; | ||||
| protected int http_proxy_port; | protected int http_proxy_port; | ||||
| protected String http_proxy_username; | protected String http_proxy_username; | ||||
| @@ -78,6 +80,15 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| this.expiresIn = expiresIn; | this.expiresIn = expiresIn; | ||||
| } | } | ||||
| @Override | |||||
| public String getOauth2redirectUri() { | |||||
| return this.oauth2redirectUri; | |||||
| } | |||||
| public void setOauth2redirectUri(String oauth2redirectUri) { | |||||
| this.oauth2redirectUri = oauth2redirectUri; | |||||
| } | |||||
| public String getHttp_proxy_host() { | public String getHttp_proxy_host() { | ||||
| return http_proxy_host; | return http_proxy_host; | ||||
| } | } | ||||
| @@ -16,8 +16,9 @@ public interface WxMpMessageHandler { | |||||
| * | * | ||||
| * @param wxMessage | * @param wxMessage | ||||
| * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | ||||
| * @param wxMpService | |||||
| * @return xml格式的消息,如果在异步规则里处理的话,可以返回null | * @return xml格式的消息,如果在异步规则里处理的话,可以返回null | ||||
| */ | */ | ||||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context); | |||||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService); | |||||
| } | } | ||||
| @@ -15,8 +15,9 @@ public interface WxMpMessageInterceptor { | |||||
| * 拦截微信消息 | * 拦截微信消息 | ||||
| * @param wxMessage | * @param wxMessage | ||||
| * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | ||||
| * @param wxMpService | |||||
| * @return true代表OK,false代表不OK | * @return true代表OK,false代表不OK | ||||
| */ | */ | ||||
| public boolean intercept(WxMpXmlMessage wxMessage, Map<String, Object> context); | |||||
| public boolean intercept(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService); | |||||
| } | } | ||||
| @@ -40,17 +40,31 @@ import java.util.regex.Pattern; | |||||
| * | * | ||||
| */ | */ | ||||
| public class WxMpMessageRouter { | public class WxMpMessageRouter { | ||||
| private static final int DEFAULT_THREAD_POOL_SIZE = 20; | |||||
| private final List<Rule> rules = new ArrayList<Rule>(); | private final List<Rule> rules = new ArrayList<Rule>(); | ||||
| private final ExecutorService es = Executors.newCachedThreadPool(); | |||||
| private final ExecutorService executorService; | |||||
| private final WxMpService wxMpService; | |||||
| public WxMpMessageRouter(WxMpService wxMpService) { | |||||
| this.wxMpService = wxMpService; | |||||
| this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); | |||||
| } | |||||
| public WxMpMessageRouter(WxMpService wxMpService, int threadPoolSize) { | |||||
| this.wxMpService = wxMpService; | |||||
| this.executorService = Executors.newFixedThreadPool(threadPoolSize); | |||||
| } | |||||
| /** | /** | ||||
| * 开始一个新的Route规则 | * 开始一个新的Route规则 | ||||
| * @return | * @return | ||||
| */ | */ | ||||
| public Rule rule() { | public Rule rule() { | ||||
| return new Rule(this); | |||||
| return new Rule(this, wxMpService); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -73,7 +87,7 @@ public class WxMpMessageRouter { | |||||
| if (matchRules.get(0).async) { | if (matchRules.get(0).async) { | ||||
| // 只要第一个是异步的,那就异步执行 | // 只要第一个是异步的,那就异步执行 | ||||
| // 在另一个线程里执行 | // 在另一个线程里执行 | ||||
| es.submit(new Runnable() { | |||||
| executorService.execute(new Runnable() { | |||||
| public void run() { | public void run() { | ||||
| for (final Rule rule : matchRules) { | for (final Rule rule : matchRules) { | ||||
| rule.service(wxMessage); | rule.service(wxMessage); | ||||
| @@ -101,7 +115,11 @@ public class WxMpMessageRouter { | |||||
| private final WxMpMessageRouter routerBuilder; | private final WxMpMessageRouter routerBuilder; | ||||
| private final WxMpService wxMpService; | |||||
| private boolean async = true; | private boolean async = true; | ||||
| private String fromUser; | |||||
| private String msgType; | private String msgType; | ||||
| @@ -119,8 +137,9 @@ public class WxMpMessageRouter { | |||||
| private List<WxMpMessageInterceptor> interceptors = new ArrayList<WxMpMessageInterceptor>(); | private List<WxMpMessageInterceptor> interceptors = new ArrayList<WxMpMessageInterceptor>(); | ||||
| protected Rule(WxMpMessageRouter routerBuilder) { | |||||
| protected Rule(WxMpMessageRouter routerBuilder, WxMpService wxMpService) { | |||||
| this.routerBuilder = routerBuilder; | this.routerBuilder = routerBuilder; | ||||
| this.wxMpService = wxMpService; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -182,7 +201,17 @@ public class WxMpMessageRouter { | |||||
| this.rContent = regex; | this.rContent = regex; | ||||
| return this; | return this; | ||||
| } | } | ||||
| /** | |||||
| * 如果fromUser等于某值 | |||||
| * @param fromUser | |||||
| * @return | |||||
| */ | |||||
| public Rule fromUser(String fromUser) { | |||||
| this.fromUser = fromUser; | |||||
| return this; | |||||
| } | |||||
| /** | /** | ||||
| * 设置微信消息拦截器 | * 设置微信消息拦截器 | ||||
| * @param interceptor | * @param interceptor | ||||
| @@ -252,7 +281,9 @@ public class WxMpMessageRouter { | |||||
| } | } | ||||
| protected boolean test(WxMpXmlMessage wxMessage) { | protected boolean test(WxMpXmlMessage wxMessage) { | ||||
| return | |||||
| return | |||||
| (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName())) | |||||
| && | |||||
| (this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) | (this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) | ||||
| && | && | ||||
| (this.event == null || this.event.equals(wxMessage.getEvent())) | (this.event == null || this.event.equals(wxMessage.getEvent())) | ||||
| @@ -274,7 +305,7 @@ public class WxMpMessageRouter { | |||||
| Map<String, Object> context = new HashMap<String, Object>(); | Map<String, Object> context = new HashMap<String, Object>(); | ||||
| // 如果拦截器不通过 | // 如果拦截器不通过 | ||||
| for (WxMpMessageInterceptor interceptor : this.interceptors) { | for (WxMpMessageInterceptor interceptor : this.interceptors) { | ||||
| if (!interceptor.intercept(wxMessage, context)) { | |||||
| if (!interceptor.intercept(wxMessage, context, wxMpService)) { | |||||
| return null; | return null; | ||||
| } | } | ||||
| } | } | ||||
| @@ -283,7 +314,7 @@ public class WxMpMessageRouter { | |||||
| WxMpXmlOutMessage res = null; | WxMpXmlOutMessage res = null; | ||||
| for (WxMpMessageHandler handler : this.handlers) { | for (WxMpMessageHandler handler : this.handlers) { | ||||
| // 返回最后handler的结果 | // 返回最后handler的结果 | ||||
| res = handler.handle(wxMessage, context); | |||||
| res = handler.handle(wxMessage, context, wxMpService); | |||||
| } | } | ||||
| return res; | return res; | ||||
| } | } | ||||
| @@ -329,6 +329,54 @@ public interface WxMpService { | |||||
| */ | */ | ||||
| WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException; | WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException; | ||||
| /** | |||||
| * <pre> | |||||
| * 构造oauth2授权的url连接 | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息 | |||||
| * </pre> | |||||
| * @param scope | |||||
| * @param state | |||||
| * @return code | |||||
| */ | |||||
| public String oauth2buildAuthorizationUrl(String scope, String state); | |||||
| /** | |||||
| * <pre> | |||||
| * 用code换取oauth2的access token | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息 | |||||
| * </pre> | |||||
| * @param code | |||||
| * @return | |||||
| */ | |||||
| public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 刷新oauth2的access token | |||||
| * </pre> | |||||
| * @param refreshToken | |||||
| * @return | |||||
| */ | |||||
| public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 用oauth2获取用户信息, 当前面引导授权时的scope是snsapi_userinfo的时候才可以 | |||||
| * </pre> | |||||
| * @param oAuth2AccessToken | |||||
| * @param lang zh_CN, zh_TW, en | |||||
| */ | |||||
| public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 验证oauth2的access token是否有效 | |||||
| * </pre> | |||||
| * @param oAuth2AccessToken | |||||
| * @return | |||||
| */ | |||||
| public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken); | |||||
| /** | /** | ||||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | ||||
| * @param url | * @param url | ||||
| @@ -10,6 +10,7 @@ import me.chanjar.weixin.common.bean.WxMenu; | |||||
| import me.chanjar.weixin.common.bean.result.WxError; | import me.chanjar.weixin.common.bean.result.WxError; | ||||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import me.chanjar.weixin.common.util.StringUtils; | |||||
| import me.chanjar.weixin.common.util.crypto.SHA1; | import me.chanjar.weixin.common.util.crypto.SHA1; | ||||
| import me.chanjar.weixin.common.util.fs.FileUtils; | import me.chanjar.weixin.common.util.fs.FileUtils; | ||||
| import me.chanjar.weixin.common.util.http.*; | import me.chanjar.weixin.common.util.http.*; | ||||
| @@ -18,7 +19,6 @@ import me.chanjar.weixin.mp.bean.*; | |||||
| import me.chanjar.weixin.mp.bean.result.*; | import me.chanjar.weixin.mp.bean.result.*; | ||||
| import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor; | import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor; | ||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.apache.http.HttpHost; | import org.apache.http.HttpHost; | ||||
| import org.apache.http.auth.AuthScope; | import org.apache.http.auth.AuthScope; | ||||
| import org.apache.http.auth.UsernamePasswordCredentials; | import org.apache.http.auth.UsernamePasswordCredentials; | ||||
| @@ -27,7 +27,6 @@ import org.apache.http.client.CredentialsProvider; | |||||
| import org.apache.http.client.config.RequestConfig; | import org.apache.http.client.config.RequestConfig; | ||||
| import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||
| import org.apache.http.client.methods.HttpGet; | import org.apache.http.client.methods.HttpGet; | ||||
| import org.apache.http.client.methods.HttpPost; | |||||
| import org.apache.http.impl.client.BasicCredentialsProvider; | import org.apache.http.impl.client.BasicCredentialsProvider; | ||||
| import org.apache.http.impl.client.BasicResponseHandler; | import org.apache.http.impl.client.BasicResponseHandler; | ||||
| import org.apache.http.impl.client.CloseableHttpClient; | import org.apache.http.impl.client.CloseableHttpClient; | ||||
| @@ -38,7 +37,6 @@ import java.io.IOException; | |||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| import java.io.StringReader; | import java.io.StringReader; | ||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.Map; | |||||
| import java.util.UUID; | import java.util.UUID; | ||||
| import java.util.concurrent.atomic.AtomicBoolean; | import java.util.concurrent.atomic.AtomicBoolean; | ||||
| @@ -299,6 +297,98 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| return WxMpSemanticQueryResult.fromJson(responseContent); | return WxMpSemanticQueryResult.fromJson(responseContent); | ||||
| } | } | ||||
| @Override | |||||
| public String oauth2buildAuthorizationUrl(String scope, String state) { | |||||
| String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" ; | |||||
| url += "appid=" + wxMpConfigStorage.getAppId(); | |||||
| url += "&redirect_uri=" + URIUtil.encodeURIComponent(wxMpConfigStorage.getOauth2redirectUri()); | |||||
| url += "&response_type=code"; | |||||
| url += "&scope=" + scope; | |||||
| if (state != null) { | |||||
| url += "&state=" + state; | |||||
| } | |||||
| url += "#wechat_redirect"; | |||||
| return url; | |||||
| } | |||||
| @Override | |||||
| public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException { | |||||
| String url = "https://api.weixin.qq.com/sns/oauth2/access_token?"; | |||||
| url += "appid=" + wxMpConfigStorage.getAppId(); | |||||
| url += "&secret=" + wxMpConfigStorage.getSecret(); | |||||
| url += "&code=" + code; | |||||
| url += "&grant_type=authorization_code"; | |||||
| try { | |||||
| RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||||
| String responseText = executor.execute(getHttpclient(), httpProxy, url, null); | |||||
| return WxMpOAuth2AccessToken.fromJson(responseText); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException { | |||||
| String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?"; | |||||
| url += "appid=" + wxMpConfigStorage.getAppId(); | |||||
| url += "&grant_type=refresh_token"; | |||||
| url += "&refresh_token=" + refreshToken; | |||||
| try { | |||||
| RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||||
| String responseText = executor.execute(getHttpclient(), httpProxy, url, null); | |||||
| return WxMpOAuth2AccessToken.fromJson(responseText); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException { | |||||
| String url = "https://api.weixin.qq.com/sns/userinfo?"; | |||||
| url += "access_token=" + oAuth2AccessToken.getAccessToken(); | |||||
| url += "&openid=" + oAuth2AccessToken.getOpenId(); | |||||
| if (lang == null) { | |||||
| url += "&lang=zh_CN"; | |||||
| } else { | |||||
| url += "&lang=" + lang; | |||||
| } | |||||
| try { | |||||
| RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||||
| String responseText = executor.execute(getHttpclient(), httpProxy, url, null); | |||||
| return WxMpUser.fromJson(responseText); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken) { | |||||
| String url = "https://api.weixin.qq.com/sns/auth?"; | |||||
| url += "access_token=" + oAuth2AccessToken; | |||||
| url += "&openid=" + oAuth2AccessToken.getOpenId(); | |||||
| try { | |||||
| RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||||
| executor.execute(getHttpclient(), httpProxy, url, null); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (WxErrorException e) { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| public String get(String url, String queryParam) throws WxErrorException { | public String get(String url, String queryParam) throws WxErrorException { | ||||
| return execute(new SimpleGetRequestExecutor(), url, queryParam); | return execute(new SimpleGetRequestExecutor(), url, queryParam); | ||||
| } | } | ||||
| @@ -10,7 +10,7 @@ import java.util.List; | |||||
| @XmlRootElement(name = "xml") | @XmlRootElement(name = "xml") | ||||
| @XmlAccessorType(XmlAccessType.FIELD) | @XmlAccessorType(XmlAccessType.FIELD) | ||||
| public class WxMpXmlOutMewsMessage extends WxMpXmlOutMessage { | |||||
| public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage { | |||||
| @XmlElement(name = "ArticleCount") | @XmlElement(name = "ArticleCount") | ||||
| protected int articleCount; | protected int articleCount; | ||||
| @@ -19,7 +19,7 @@ public class WxMpXmlOutMewsMessage extends WxMpXmlOutMessage { | |||||
| @XmlElement(name = "item") | @XmlElement(name = "item") | ||||
| protected final List<Item> articles = new ArrayList<Item>(); | protected final List<Item> articles = new ArrayList<Item>(); | ||||
| public WxMpXmlOutMewsMessage() { | |||||
| public WxMpXmlOutNewsMessage() { | |||||
| this.msgType = WxConsts.XML_MSG_NEWS; | this.msgType = WxConsts.XML_MSG_NEWS; | ||||
| } | } | ||||
| @@ -1,6 +1,6 @@ | |||||
| package me.chanjar.weixin.mp.bean.outxmlbuilder; | package me.chanjar.weixin.mp.bean.outxmlbuilder; | ||||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMewsMessage; | |||||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutNewsMessage; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| @@ -9,18 +9,18 @@ import java.util.List; | |||||
| * 图文消息builder | * 图文消息builder | ||||
| * @author chanjarster | * @author chanjarster | ||||
| */ | */ | ||||
| public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxMpXmlOutMewsMessage> { | |||||
| public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxMpXmlOutNewsMessage> { | |||||
| protected final List<WxMpXmlOutMewsMessage.Item> articles = new ArrayList<WxMpXmlOutMewsMessage.Item>(); | |||||
| protected final List<WxMpXmlOutNewsMessage.Item> articles = new ArrayList<WxMpXmlOutNewsMessage.Item>(); | |||||
| public NewsBuilder addArticle(WxMpXmlOutMewsMessage.Item item) { | |||||
| public NewsBuilder addArticle(WxMpXmlOutNewsMessage.Item item) { | |||||
| this.articles.add(item); | this.articles.add(item); | ||||
| return this; | return this; | ||||
| } | } | ||||
| public WxMpXmlOutMewsMessage build() { | |||||
| WxMpXmlOutMewsMessage m = new WxMpXmlOutMewsMessage(); | |||||
| for(WxMpXmlOutMewsMessage.Item item : articles) { | |||||
| public WxMpXmlOutNewsMessage build() { | |||||
| WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage(); | |||||
| for(WxMpXmlOutNewsMessage.Item item : articles) { | |||||
| m.addArticle(item); | m.addArticle(item); | ||||
| } | } | ||||
| setCommon(m); | setCommon(m); | ||||
| @@ -0,0 +1,75 @@ | |||||
| package me.chanjar.weixin.mp.bean.result; | |||||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
| /** | |||||
| * Created by qianjia on 14/11/26. | |||||
| */ | |||||
| public class WxMpOAuth2AccessToken { | |||||
| private String accessToken; | |||||
| private int expiresIn = -1; | |||||
| private String refreshToken; | |||||
| private String openId; | |||||
| private String scope; | |||||
| public String getRefreshToken() { | |||||
| return refreshToken; | |||||
| } | |||||
| public void setRefreshToken(String refreshToken) { | |||||
| this.refreshToken = refreshToken; | |||||
| } | |||||
| public String getOpenId() { | |||||
| return openId; | |||||
| } | |||||
| public void setOpenId(String openId) { | |||||
| this.openId = openId; | |||||
| } | |||||
| public String getScope() { | |||||
| return scope; | |||||
| } | |||||
| public void setScope(String scope) { | |||||
| this.scope = scope; | |||||
| } | |||||
| public String getAccessToken() { | |||||
| return accessToken; | |||||
| } | |||||
| public void setAccessToken(String accessToken) { | |||||
| this.accessToken = accessToken; | |||||
| } | |||||
| public int getExpiresIn() { | |||||
| return expiresIn; | |||||
| } | |||||
| public void setExpiresIn(int expiresIn) { | |||||
| this.expiresIn = expiresIn; | |||||
| } | |||||
| public static WxMpOAuth2AccessToken fromJson(String json) { | |||||
| return WxMpGsonBuilder.create().fromJson(json, WxMpOAuth2AccessToken.class); | |||||
| } | |||||
| @Override | |||||
| public String toString() { | |||||
| return "WxMpOAuth2AccessToken{" + | |||||
| "accessToken='" + accessToken + '\'' + | |||||
| ", expiresIn=" + expiresIn + | |||||
| ", refreshToken='" + refreshToken + '\'' + | |||||
| ", openId='" + openId + '\'' + | |||||
| ", scope='" + scope + '\'' + | |||||
| '}'; | |||||
| } | |||||
| } | |||||
| @@ -9,7 +9,7 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
| */ | */ | ||||
| public class WxMpUser { | public class WxMpUser { | ||||
| protected boolean subscribe; | |||||
| protected Boolean subscribe; | |||||
| protected String openId; | protected String openId; | ||||
| protected String nickname; | protected String nickname; | ||||
| protected String sex; | protected String sex; | ||||
| @@ -18,13 +18,13 @@ public class WxMpUser { | |||||
| protected String province; | protected String province; | ||||
| protected String country; | protected String country; | ||||
| protected String headImgUrl; | protected String headImgUrl; | ||||
| protected long subscribeTime; | |||||
| protected Long subscribeTime; | |||||
| protected String unionId; | protected String unionId; | ||||
| public boolean isSubscribe() { | |||||
| public Boolean isSubscribe() { | |||||
| return subscribe; | return subscribe; | ||||
| } | } | ||||
| public void setSubscribe(boolean subscribe) { | |||||
| public void setSubscribe(Boolean subscribe) { | |||||
| this.subscribe = subscribe; | this.subscribe = subscribe; | ||||
| } | } | ||||
| public String getOpenId() { | public String getOpenId() { | ||||
| @@ -75,10 +75,10 @@ public class WxMpUser { | |||||
| public void setHeadImgUrl(String headImgUrl) { | public void setHeadImgUrl(String headImgUrl) { | ||||
| this.headImgUrl = headImgUrl; | this.headImgUrl = headImgUrl; | ||||
| } | } | ||||
| public long getSubscribeTime() { | |||||
| public Long getSubscribeTime() { | |||||
| return subscribeTime; | return subscribeTime; | ||||
| } | } | ||||
| public void setSubscribeTime(long subscribeTime) { | |||||
| public void setSubscribeTime(Long subscribeTime) { | |||||
| this.subscribeTime = subscribeTime; | this.subscribeTime = subscribeTime; | ||||
| } | } | ||||
| public String getUnionId() { | public String getUnionId() { | ||||
| @@ -91,5 +91,21 @@ public class WxMpUser { | |||||
| public static WxMpUser fromJson(String json) { | public static WxMpUser fromJson(String json) { | ||||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUser.class); | return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUser.class); | ||||
| } | } | ||||
| @Override | |||||
| public String toString() { | |||||
| return "WxMpUser{" + | |||||
| "subscribe=" + subscribe + | |||||
| ", openId='" + openId + '\'' + | |||||
| ", nickname='" + nickname + '\'' + | |||||
| ", sex='" + sex + '\'' + | |||||
| ", language='" + language + '\'' + | |||||
| ", city='" + city + '\'' + | |||||
| ", province='" + province + '\'' + | |||||
| ", country='" + country + '\'' + | |||||
| ", headImgUrl='" + headImgUrl + '\'' + | |||||
| ", subscribeTime=" + subscribeTime + | |||||
| ", unionId='" + unionId + '\'' + | |||||
| '}'; | |||||
| } | |||||
| } | } | ||||
| @@ -64,6 +64,7 @@ public class WxMpCustomMessageGsonAdapter implements JsonSerializer<WxMpCustomMe | |||||
| } | } | ||||
| if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { | if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { | ||||
| JsonObject newsJsonObject = new JsonObject(); | |||||
| JsonArray articleJsonArray = new JsonArray(); | JsonArray articleJsonArray = new JsonArray(); | ||||
| for (WxMpCustomMessage.WxArticle article : message.getArticles()) { | for (WxMpCustomMessage.WxArticle article : message.getArticles()) { | ||||
| JsonObject articleJson = new JsonObject(); | JsonObject articleJson = new JsonObject(); | ||||
| @@ -73,7 +74,8 @@ public class WxMpCustomMessageGsonAdapter implements JsonSerializer<WxMpCustomMe | |||||
| articleJson.addProperty("picurl", article.getPicUrl()); | articleJson.addProperty("picurl", article.getPicUrl()); | ||||
| articleJsonArray.add(articleJson); | articleJsonArray.add(articleJson); | ||||
| } | } | ||||
| messageJson.add("articles", articleJsonArray); | |||||
| newsJsonObject.add("articles", articleJsonArray); | |||||
| messageJson.add("news", newsJsonObject); | |||||
| } | } | ||||
| return messageJson; | return messageJson; | ||||
| @@ -4,6 +4,7 @@ import com.google.gson.Gson; | |||||
| import com.google.gson.GsonBuilder; | import com.google.gson.GsonBuilder; | ||||
| import me.chanjar.weixin.mp.bean.*; | import me.chanjar.weixin.mp.bean.*; | ||||
| import me.chanjar.weixin.mp.bean.result.*; | import me.chanjar.weixin.mp.bean.result.*; | ||||
| import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | |||||
| public class WxMpGsonBuilder { | public class WxMpGsonBuilder { | ||||
| @@ -24,6 +25,7 @@ public class WxMpGsonBuilder { | |||||
| INSTANCE.registerTypeAdapter(WxMpQrCodeTicket.class, new WxQrCodeTicketAdapter()); | INSTANCE.registerTypeAdapter(WxMpQrCodeTicket.class, new WxQrCodeTicketAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpTemplateMessage.class, new WxMpTemplateMessageGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMpTemplateMessage.class, new WxMpTemplateMessageGsonAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpSemanticQueryResult.class, new WxMpSemanticQueryResultAdapter()); | INSTANCE.registerTypeAdapter(WxMpSemanticQueryResult.class, new WxMpSemanticQueryResultAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpOAuth2AccessToken.class, new WxMpOAuth2AccessTokenAdapter()); | |||||
| } | } | ||||
| public static Gson create() { | public static Gson create() { | ||||
| @@ -0,0 +1,38 @@ | |||||
| package me.chanjar.weixin.mp.util.json; | |||||
| import com.google.gson.*; | |||||
| import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
| import me.chanjar.weixin.common.util.json.GsonHelper; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | |||||
| import java.lang.reflect.Type; | |||||
| /** | |||||
| * Created by qianjia on 14/11/28. | |||||
| */ | |||||
| public class WxMpOAuth2AccessTokenAdapter implements JsonDeserializer<WxMpOAuth2AccessToken> { | |||||
| public WxMpOAuth2AccessToken deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws | |||||
| JsonParseException { | |||||
| WxMpOAuth2AccessToken accessToken = new WxMpOAuth2AccessToken(); | |||||
| JsonObject accessTokenJsonObject = json.getAsJsonObject(); | |||||
| if (accessTokenJsonObject.get("access_token") != null && !accessTokenJsonObject.get("access_token").isJsonNull()) { | |||||
| accessToken.setAccessToken(GsonHelper.getAsString(accessTokenJsonObject.get("access_token"))); | |||||
| } | |||||
| if (accessTokenJsonObject.get("expires_in") != null && !accessTokenJsonObject.get("expires_in").isJsonNull()) { | |||||
| accessToken.setExpiresIn(GsonHelper.getAsPrimitiveInt(accessTokenJsonObject.get("expires_in"))); | |||||
| } | |||||
| if (accessTokenJsonObject.get("refresh_token") != null && !accessTokenJsonObject.get("refresh_token").isJsonNull()) { | |||||
| accessToken.setRefreshToken(GsonHelper.getAsString(accessTokenJsonObject.get("refresh_token"))); | |||||
| } | |||||
| if (accessTokenJsonObject.get("openid") != null && !accessTokenJsonObject.get("openid").isJsonNull()) { | |||||
| accessToken.setOpenId(GsonHelper.getAsString(accessTokenJsonObject.get("openid"))); | |||||
| } | |||||
| if (accessTokenJsonObject.get("scope") != null && !accessTokenJsonObject.get("scope").isJsonNull()) { | |||||
| accessToken.setScope(GsonHelper.getAsString(accessTokenJsonObject.get("scope"))); | |||||
| } | |||||
| return accessToken; | |||||
| } | |||||
| } | |||||
| @@ -56,6 +56,7 @@ public class XmlTransformer { | |||||
| Marshaller m = JAXB_CONTEXT.createMarshaller(); | Marshaller m = JAXB_CONTEXT.createMarshaller(); | ||||
| m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); | m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); | ||||
| m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER); | m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER); | ||||
| m.setProperty(Marshaller.JAXB_FRAGMENT, true); | |||||
| m.marshal(object, writer); | m.marshal(object, writer); | ||||
| } | } | ||||
| @@ -76,7 +77,7 @@ public class XmlTransformer { | |||||
| return JAXBContext.newInstance( | return JAXBContext.newInstance( | ||||
| WxMpXmlOutMessage.class, | WxMpXmlOutMessage.class, | ||||
| WxMpMpXmlOutImageMessage.class, | WxMpMpXmlOutImageMessage.class, | ||||
| WxMpXmlOutMewsMessage.class, | |||||
| WxMpXmlOutNewsMessage.class, | |||||
| WxMpXmlOutMusicMessage.class, | WxMpXmlOutMusicMessage.class, | ||||
| WxMpXmlOutTextMessage.class, | WxMpXmlOutTextMessage.class, | ||||
| WxMpXmlOutVideoMessage.class, | WxMpXmlOutVideoMessage.class, | ||||
| @@ -1,6 +1,6 @@ | |||||
| package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import me.chanjar.weixin.common.util.StringUtils; | |||||
| import org.testng.Assert; | import org.testng.Assert; | ||||
| import org.testng.annotations.Guice; | import org.testng.annotations.Guice; | ||||
| import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
| @@ -47,7 +47,7 @@ public class WxMpMessageRouterTest { | |||||
| @Test(dataProvider="messages-1") | @Test(dataProvider="messages-1") | ||||
| public void testSync(WxMpXmlMessage message, String expected) { | public void testSync(WxMpXmlMessage message, String expected) { | ||||
| StringBuffer sb = new StringBuffer(); | StringBuffer sb = new StringBuffer(); | ||||
| WxMpMessageRouter router = new WxMpMessageRouter(); | |||||
| WxMpMessageRouter router = new WxMpMessageRouter(null); | |||||
| prepare(false, sb, router); | prepare(false, sb, router); | ||||
| router.route(message); | router.route(message); | ||||
| Assert.assertEquals(sb.toString(), expected); | Assert.assertEquals(sb.toString(), expected); | ||||
| @@ -56,7 +56,7 @@ public class WxMpMessageRouterTest { | |||||
| @Test(dataProvider="messages-1") | @Test(dataProvider="messages-1") | ||||
| public void testAsync(WxMpXmlMessage message, String expected) throws InterruptedException { | public void testAsync(WxMpXmlMessage message, String expected) throws InterruptedException { | ||||
| StringBuffer sb = new StringBuffer(); | StringBuffer sb = new StringBuffer(); | ||||
| WxMpMessageRouter router = new WxMpMessageRouter(); | |||||
| WxMpMessageRouter router = new WxMpMessageRouter(null); | |||||
| prepare(true, sb, router); | prepare(true, sb, router); | ||||
| router.route(message); | router.route(message); | ||||
| Thread.sleep(500l); | Thread.sleep(500l); | ||||
| @@ -64,10 +64,10 @@ public class WxMpMessageRouterTest { | |||||
| } | } | ||||
| public void testConcurrency() throws InterruptedException { | public void testConcurrency() throws InterruptedException { | ||||
| final WxMpMessageRouter router = new WxMpMessageRouter(); | |||||
| final WxMpMessageRouter router = new WxMpMessageRouter(null); | |||||
| router.rule().handler(new WxMpMessageHandler() { | router.rule().handler(new WxMpMessageHandler() { | ||||
| @Override | @Override | ||||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context) { | |||||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService) { | |||||
| return null; | return null; | ||||
| } | } | ||||
| }).end(); | }).end(); | ||||
| @@ -149,7 +149,7 @@ public class WxMpMessageRouterTest { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context) { | |||||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService) { | |||||
| sb.append(this.echoStr).append(','); | sb.append(this.echoStr).append(','); | ||||
| return null; | return null; | ||||
| } | } | ||||
| @@ -108,7 +108,7 @@ public class WxMpCustomMessageTest { | |||||
| reply.getArticles().add(article2); | reply.getArticles().add(article2); | ||||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}"); | |||||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||||
| } | } | ||||
| public void testNewsBuild() { | public void testNewsBuild() { | ||||
| @@ -125,8 +125,8 @@ public class WxMpCustomMessageTest { | |||||
| article2.setTitle("Happy Day"); | article2.setTitle("Happy Day"); | ||||
| WxMpCustomMessage reply = WxMpCustomMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); | WxMpCustomMessage reply = WxMpCustomMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); | ||||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}"); | |||||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||||
| } | } | ||||
| } | } | ||||
| @@ -13,8 +13,7 @@ public class WxMpXmlOutImageMessageTest { | |||||
| m.setFromUserName("from"); | m.setFromUserName("from"); | ||||
| m.setToUserName("to"); | m.setToUserName("to"); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -27,8 +26,7 @@ public class WxMpXmlOutImageMessageTest { | |||||
| public void testBuild() { | public void testBuild() { | ||||
| WxMpMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | WxMpMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -17,8 +17,7 @@ public class WxXmlOutMusicMessageTest { | |||||
| m.setFromUserName("fromUser"); | m.setFromUserName("fromUser"); | ||||
| m.setToUserName("toUser"); | m.setToUserName("toUser"); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -45,8 +44,7 @@ public class WxXmlOutMusicMessageTest { | |||||
| .musicUrl("musicUrl") | .musicUrl("musicUrl") | ||||
| .thumbMediaId("thumbMediaId") | .thumbMediaId("thumbMediaId") | ||||
| .build(); | .build(); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -7,20 +7,19 @@ import org.testng.annotations.Test; | |||||
| public class WxXmlOutNewsMessageTest { | public class WxXmlOutNewsMessageTest { | ||||
| public void test() { | public void test() { | ||||
| WxMpXmlOutMewsMessage m = new WxMpXmlOutMewsMessage(); | |||||
| WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage(); | |||||
| m.setCreateTime(1122l); | m.setCreateTime(1122l); | ||||
| m.setFromUserName("fromUser"); | m.setFromUserName("fromUser"); | ||||
| m.setToUserName("toUser"); | m.setToUserName("toUser"); | ||||
| WxMpXmlOutMewsMessage.Item item = new WxMpXmlOutMewsMessage.Item(); | |||||
| WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item(); | |||||
| item.setDescription("description"); | item.setDescription("description"); | ||||
| item.setPicUrl("picUrl"); | item.setPicUrl("picUrl"); | ||||
| item.setTitle("title"); | item.setTitle("title"); | ||||
| item.setUrl("url"); | item.setUrl("url"); | ||||
| m.addArticle(item); | m.addArticle(item); | ||||
| m.addArticle(item); | m.addArticle(item); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -46,20 +45,19 @@ public class WxXmlOutNewsMessageTest { | |||||
| } | } | ||||
| public void testBuild() { | public void testBuild() { | ||||
| WxMpXmlOutMewsMessage.Item item = new WxMpXmlOutMewsMessage.Item(); | |||||
| WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item(); | |||||
| item.setDescription("description"); | item.setDescription("description"); | ||||
| item.setPicUrl("picUrl"); | item.setPicUrl("picUrl"); | ||||
| item.setTitle("title"); | item.setTitle("title"); | ||||
| item.setUrl("url"); | item.setUrl("url"); | ||||
| WxMpXmlOutMewsMessage m = WxMpXmlOutMessage.NEWS() | |||||
| WxMpXmlOutNewsMessage m = WxMpXmlOutMessage.NEWS() | |||||
| .fromUser("fromUser") | .fromUser("fromUser") | ||||
| .toUser("toUser") | .toUser("toUser") | ||||
| .addArticle(item) | .addArticle(item) | ||||
| .addArticle(item) | .addArticle(item) | ||||
| .build(); | .build(); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -13,8 +13,7 @@ public class WxXmlOutTextMessageTest { | |||||
| m.setFromUserName("from"); | m.setFromUserName("from"); | ||||
| m.setToUserName("to"); | m.setToUserName("to"); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -27,8 +26,7 @@ public class WxXmlOutTextMessageTest { | |||||
| public void testBuild() { | public void testBuild() { | ||||
| WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); | WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -15,8 +15,7 @@ public class WxXmlOutVideoMessageTest { | |||||
| m.setFromUserName("fromUser"); | m.setFromUserName("fromUser"); | ||||
| m.setToUserName("toUser"); | m.setToUserName("toUser"); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -39,8 +38,7 @@ public class WxXmlOutVideoMessageTest { | |||||
| .title("title") | .title("title") | ||||
| .description("ddfff") | .description("ddfff") | ||||
| .build(); | .build(); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -13,8 +13,7 @@ public class WxXmlOutVoiceMessageTest { | |||||
| m.setFromUserName("from"); | m.setFromUserName("from"); | ||||
| m.setToUserName("to"); | m.setToUserName("to"); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -27,8 +26,7 @@ public class WxXmlOutVoiceMessageTest { | |||||
| public void testBuild() { | public void testBuild() { | ||||
| WxMpXmlOutVoiceMessage m = WxMpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | WxMpXmlOutVoiceMessage m = WxMpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | ||||
| String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||||
| + "<xml>" | |||||
| String expected = "<xml>" | |||||
| + "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
| + "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
| + "<CreateTime>1122</CreateTime>" | + "<CreateTime>1122</CreateTime>" | ||||
| @@ -1,20 +1,124 @@ | |||||
| package me.chanjar.weixin.mp.demo; | package me.chanjar.weixin.mp.demo; | ||||
| import me.chanjar.weixin.common.api.WxConsts; | |||||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||||
| import me.chanjar.weixin.mp.api.*; | |||||
| import me.chanjar.weixin.mp.bean.WxMpMpXmlOutImageMessage; | |||||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage; | |||||
| import org.eclipse.jetty.server.Server; | import org.eclipse.jetty.server.Server; | ||||
| import org.eclipse.jetty.servlet.ServletHandler; | |||||
| import org.eclipse.jetty.servlet.*; | |||||
| import javax.xml.bind.JAXBException; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * @author Daniel Qian | |||||
| */ | |||||
| public class WxMpDemoServer { | public class WxMpDemoServer { | ||||
| private static WxMpConfigStorage wxMpConfigStorage; | |||||
| private static WxMpService wxMpService; | |||||
| private static WxMpMessageRouter wxMpMessageRouter; | |||||
| public static void main(String[] args) throws Exception { | public static void main(String[] args) throws Exception { | ||||
| initWeixin(); | |||||
| Server server = new Server(8080); | Server server = new Server(8080); | ||||
| ServletHandler handler = new ServletHandler(); | |||||
| server.setHandler(handler); | |||||
| ServletHandler servletHandler = new ServletHandler(); | |||||
| server.setHandler(servletHandler); | |||||
| ServletHolder endpointServletHolder = new ServletHolder(new WxMpEndpointServlt(wxMpConfigStorage, wxMpService, wxMpMessageRouter)); | |||||
| servletHandler.addServletWithMapping(endpointServletHolder, "/*"); | |||||
| ServletHolder oauthServletHolder = new ServletHolder(new WxMpOAuth2Servlet(wxMpService)); | |||||
| servletHandler.addServletWithMapping(oauthServletHolder, "/oauth2/*"); | |||||
| handler.addServletWithMapping(WxMpDemoServlet.class, "/*"); | |||||
| server.start(); | server.start(); | ||||
| server.join(); | server.join(); | ||||
| } | } | ||||
| private static void initWeixin() { | |||||
| try { | |||||
| InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | |||||
| WxMpDemoInMemoryConfigStorage config = WxMpDemoInMemoryConfigStorage.fromXml(is1); | |||||
| wxMpConfigStorage = config; | |||||
| wxMpService = new WxMpServiceImpl(); | |||||
| wxMpService.setWxMpConfigStorage(config); | |||||
| WxMpMessageHandler textHandler = new WxMpMessageHandler() { | |||||
| @Override | |||||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, | |||||
| WxMpService wxMpService) { | |||||
| WxMpXmlOutTextMessage m | |||||
| = WxMpXmlOutMessage.TEXT().content("测试加密消息").fromUser(wxMessage.getToUserName()) | |||||
| .toUser(wxMessage.getFromUserName()).build(); | |||||
| return m; | |||||
| } | |||||
| }; | |||||
| WxMpMessageHandler imageHandler = new WxMpMessageHandler() { | |||||
| @Override | |||||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, | |||||
| WxMpService wxMpService) { | |||||
| try { | |||||
| WxMediaUploadResult wxMediaUploadResult = wxMpService | |||||
| .mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, ClassLoader.getSystemResourceAsStream("mm.jpeg")); | |||||
| WxMpMpXmlOutImageMessage m | |||||
| = WxMpXmlOutMessage | |||||
| .IMAGE() | |||||
| .mediaId(wxMediaUploadResult.getMediaId()) | |||||
| .fromUser(wxMessage.getToUserName()) | |||||
| .toUser(wxMessage.getFromUserName()) | |||||
| .build(); | |||||
| return m; | |||||
| } catch (WxErrorException e) { | |||||
| e.printStackTrace(); | |||||
| } catch (IOException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| }; | |||||
| WxMpMessageHandler oauth2handler = new WxMpMessageHandler() { | |||||
| @Override | |||||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, | |||||
| WxMpService wxMpService) { | |||||
| String href = "<a href=\"" + wxMpService.oauth2buildAuthorizationUrl(WxConsts.OAUTH2_SCOPE_USER_INFO, null) | |||||
| + "\">测试oauth2</a>"; | |||||
| return WxMpXmlOutMessage | |||||
| .TEXT() | |||||
| .content(href) | |||||
| .fromUser(wxMessage.getToUserName()) | |||||
| .toUser(wxMessage.getFromUserName()).build(); | |||||
| } | |||||
| }; | |||||
| wxMpMessageRouter = new WxMpMessageRouter(wxMpService); | |||||
| wxMpMessageRouter | |||||
| .rule() | |||||
| .async(false) | |||||
| .content("哈哈") // 拦截内容为“哈哈”的消息 | |||||
| .handler(textHandler) | |||||
| .end() | |||||
| .rule() | |||||
| .async(false) | |||||
| .content("图片") | |||||
| .handler(imageHandler) | |||||
| .end() | |||||
| .rule() | |||||
| .async(false) | |||||
| .content("oauth") | |||||
| .handler(oauth2handler) | |||||
| .end() | |||||
| ; | |||||
| } catch (JAXBException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -1,10 +1,14 @@ | |||||
| package me.chanjar.weixin.mp.demo; | package me.chanjar.weixin.mp.demo; | ||||
| import me.chanjar.weixin.common.api.WxConsts; | |||||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||||
| import me.chanjar.weixin.common.util.StringUtils; | |||||
| import me.chanjar.weixin.mp.api.*; | import me.chanjar.weixin.mp.api.*; | ||||
| import me.chanjar.weixin.mp.bean.WxMpMpXmlOutImageMessage; | |||||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | ||||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | ||||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage; | import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage; | ||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import javax.servlet.ServletException; | import javax.servlet.ServletException; | ||||
| import javax.servlet.http.HttpServlet; | import javax.servlet.http.HttpServlet; | ||||
| @@ -18,55 +22,29 @@ import java.util.Map; | |||||
| /** | /** | ||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| */ | */ | ||||
| public class WxMpDemoServlet extends HttpServlet { | |||||
| public class WxMpEndpointServlt extends HttpServlet { | |||||
| protected WxMpService wxMpService; | |||||
| protected WxMpConfigStorage wxMpConfigStorage; | protected WxMpConfigStorage wxMpConfigStorage; | ||||
| protected WxMpService wxMpService; | |||||
| protected WxMpMessageRouter wxMpMessageRouter; | protected WxMpMessageRouter wxMpMessageRouter; | ||||
| @Override public void init() throws ServletException { | |||||
| // | |||||
| super.init(); | |||||
| try { | |||||
| InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | |||||
| WxMpDemoInMemoryConfigStorage config = WxMpDemoInMemoryConfigStorage.fromXml(is1); | |||||
| wxMpConfigStorage = config; | |||||
| wxMpService = new WxMpServiceImpl(); | |||||
| wxMpService.setWxMpConfigStorage(config); | |||||
| WxMpMessageHandler handler = new WxMpMessageHandler() { | |||||
| @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context) { | |||||
| WxMpXmlOutTextMessage m | |||||
| = WxMpXmlOutMessage.TEXT().content("测试加密消息").fromUser(wxMessage.getToUserName()) | |||||
| .toUser(wxMessage.getFromUserName()).build(); | |||||
| return m; | |||||
| } | |||||
| }; | |||||
| wxMpMessageRouter = new WxMpMessageRouter(); | |||||
| wxMpMessageRouter | |||||
| .rule() | |||||
| .async(false) | |||||
| .content("哈哈") // 拦截内容为“哈哈”的消息 | |||||
| .handler(handler) | |||||
| .end(); | |||||
| } catch (JAXBException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| public WxMpEndpointServlt(WxMpConfigStorage wxMpConfigStorage, WxMpService wxMpService, | |||||
| WxMpMessageRouter wxMpMessageRouter) { | |||||
| this.wxMpConfigStorage = wxMpConfigStorage; | |||||
| this.wxMpService = wxMpService; | |||||
| this.wxMpMessageRouter = wxMpMessageRouter; | |||||
| } | } | ||||
| @Override protected void service(HttpServletRequest request, HttpServletResponse response) | @Override protected void service(HttpServletRequest request, HttpServletResponse response) | ||||
| throws ServletException, IOException { | throws ServletException, IOException { | ||||
| response.setContentType("text/html;charset=utf-8"); | |||||
| response.setStatus(HttpServletResponse.SC_OK); | |||||
| String signature = request.getParameter("signature"); | String signature = request.getParameter("signature"); | ||||
| String nonce = request.getParameter("nonce"); | String nonce = request.getParameter("nonce"); | ||||
| String timestamp = request.getParameter("timestamp"); | String timestamp = request.getParameter("timestamp"); | ||||
| response.setContentType("text/html;charset=utf-8"); | |||||
| response.setStatus(HttpServletResponse.SC_OK); | |||||
| if (!wxMpService.checkSignature(timestamp, nonce, signature)) { | if (!wxMpService.checkSignature(timestamp, nonce, signature)) { | ||||
| // 消息签名不正确,说明不是公众平台发过来的消息 | // 消息签名不正确,说明不是公众平台发过来的消息 | ||||
| response.getWriter().println("非法请求"); | response.getWriter().println("非法请求"); | ||||
| @@ -84,31 +62,25 @@ public class WxMpDemoServlet extends HttpServlet { | |||||
| "raw" : | "raw" : | ||||
| request.getParameter("encrypt_type"); | request.getParameter("encrypt_type"); | ||||
| WxMpXmlMessage inMessage = null; | |||||
| if ("raw".equals(encryptType)) { | if ("raw".equals(encryptType)) { | ||||
| // 明文传输的消息 | // 明文传输的消息 | ||||
| inMessage = WxMpXmlMessage.fromXml(request.getInputStream()); | |||||
| } else if ("aes".equals(encryptType)) { | |||||
| // 是aes加密的消息 | |||||
| String msgSignature = request.getParameter("msg_signature"); | |||||
| inMessage = WxMpXmlMessage.fromEncryptedXml(request.getInputStream(), wxMpConfigStorage, timestamp, nonce, msgSignature); | |||||
| } else { | |||||
| response.getWriter().println("不可识别的加密类型"); | |||||
| WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(request.getInputStream()); | |||||
| WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage); | |||||
| response.getWriter().write(outMessage.toXml()); | |||||
| return; | return; | ||||
| } | } | ||||
| WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage); | |||||
| if (outMessage != null) { | |||||
| if ("raw".equals(encryptType)) { | |||||
| response.getWriter().write(outMessage.toXml()); | |||||
| } else if ("aes".equals(encryptType)) { | |||||
| response.getWriter().write(outMessage.toEncryptedXml(wxMpConfigStorage)); | |||||
| } | |||||
| if ("aes".equals(encryptType)) { | |||||
| // 是aes加密的消息 | |||||
| String msgSignature = request.getParameter("msg_signature"); | |||||
| WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(request.getInputStream(), wxMpConfigStorage, timestamp, nonce, msgSignature); | |||||
| WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage); | |||||
| response.getWriter().write(outMessage.toEncryptedXml(wxMpConfigStorage)); | |||||
| return; | return; | ||||
| } | } | ||||
| response.getWriter().println("不可识别的加密类型"); | |||||
| return; | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,65 @@ | |||||
| package me.chanjar.weixin.mp.demo; | |||||
| import me.chanjar.weixin.common.api.WxConsts; | |||||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||||
| import me.chanjar.weixin.common.util.StringUtils; | |||||
| import me.chanjar.weixin.mp.api.*; | |||||
| import me.chanjar.weixin.mp.bean.WxMpMpXmlOutImageMessage; | |||||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpUser; | |||||
| import javax.servlet.ServletContext; | |||||
| import javax.servlet.ServletException; | |||||
| import javax.servlet.http.HttpServlet; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletResponse; | |||||
| import javax.xml.bind.JAXBException; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.util.Map; | |||||
| public class WxMpOAuth2Servlet extends HttpServlet { | |||||
| protected WxMpService wxMpService; | |||||
| public WxMpOAuth2Servlet(WxMpService wxMpService) { | |||||
| this.wxMpService = wxMpService; | |||||
| } | |||||
| @Override protected void service(HttpServletRequest request, HttpServletResponse response) | |||||
| throws ServletException, IOException { | |||||
| response.setContentType("text/html;charset=utf-8"); | |||||
| response.setStatus(HttpServletResponse.SC_OK); | |||||
| String code = request.getParameter("code"); | |||||
| try { | |||||
| response.getWriter().println("<h1>code</h1>"); | |||||
| response.getWriter().println(code); | |||||
| WxMpOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code); | |||||
| response.getWriter().println("<h1>access token</h1>"); | |||||
| response.getWriter().println(wxMpOAuth2AccessToken.toString()); | |||||
| WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null); | |||||
| response.getWriter().println("<h1>user info</h1>"); | |||||
| response.getWriter().println(wxMpUser.toString()); | |||||
| wxMpOAuth2AccessToken = wxMpService.oauth2refreshAccessToken(wxMpOAuth2AccessToken.getRefreshToken()); | |||||
| response.getWriter().println("<h1>after refresh</h1>"); | |||||
| response.getWriter().println(wxMpOAuth2AccessToken.toString()); | |||||
| } catch (WxErrorException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| response.getWriter().flush(); | |||||
| response.getWriter().close(); | |||||
| } | |||||
| } | |||||
| @@ -6,4 +6,5 @@ | |||||
| <accessToken>可以不填写</accessToken> | <accessToken>可以不填写</accessToken> | ||||
| <expiresIn>可以不填写</expiresIn> | <expiresIn>可以不填写</expiresIn> | ||||
| <openId>某个加你公众号的用户的openId</openId> | <openId>某个加你公众号的用户的openId</openId> | ||||
| <oauth2redirectUri>网页授权获取用户信息回调地址</oauth2redirectUri> | |||||
| </xml> | </xml> | ||||