从源代码角度看Struts2返回JSON数据的原理

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

前面一篇文章其实只是介绍了如何在Struts2中返回JSON数据到客户端的具体范例而无关其原理,内容与标题不符惹来标题党嫌疑确实是笔者发文不够严谨,目前已修改标题,与内容匹配。本文将从struts2-json插件的源码角度出发,结合之前的应用范例来说明struts2-json插件返回JSON数据的原理。

用winrar打开struts2-json-plugin-xx.jar(笔者使用版本为2.1.8.1),根目录下有一个struts-plugin.xml,这个文件想必大家都很了解,不做过多介绍了。打开该文件,内容非常简答,如下:

Xml代码   收藏代码
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2.   
  3. <!DOCTYPE struts PUBLIC  
  4.         "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"  
  5.         "http://struts.apache.org/dtds/struts-2.0.dtd">  
  6.   
  7. <struts>  
  8.     <package name="json-default" extends="struts-default">  
  9.         <result-types>  
  10.             <result-type name="json" class="org.apache.struts2.json.JSONResult"/>  
  11.         </result-types>  
  12.         <interceptors>  
  13.             <interceptor name="json" class="org.apache.struts2.json.JSONInterceptor"/>  
  14.         </interceptors>  
  15.     </package>  
  16. </struts>  

 

前文提到,如果要使用Struts2返回JSON数据到客户端,那么action所在的package必须继承自json-default包,原因就在上边的配置文件中:这里的配置文件指定了该插件的包名为json-default,所以要使用该插件的功能,就必须继承自该包——json-default。

上面的配置文件中,配置了两个类:org.apache.struts2.json.JSONResult和org.apache.struts2.json.JSONInterceptor,前者是结果类型,后者是一个拦截器。简单说一下,org.apache.struts2.json.JSONResult负责将action中的“某些”(通过相关参数可以指定,前文已有详述)或action中所有"可获取"(有getter方法的属性或一个有返回值的getter方法的返回值)数据序列化成JSON字符串,然后发送给客户端;org.apache.struts2.json.JSONInterceptor负责拦截客户端到json-default包下的所有请求,并检查客户端提交的数据是否是JSON类型,如果是则根据指定配置来反序列化JSON数据到action中的bean中(说的有点简单,其实该拦截器内部对数据做了很多判断),拦截器不是本文的重点,介绍到此为止。看一张图,或许能够更加清晰明了的说明JSON插件执行的流程:

 

JSON插件执行时序图

 

下面重点说说org.apache.struts2.json.JSONResult。

首先看一下org.apache.struts2.json.JSONResult源码的核心部分:

部分属性

Java代码   收藏代码
  1. private String defaultEncoding = "ISO-8859-1";//默认的编码  
  2. private List<Pattern> includeProperties;//被包含的属性的正则表达式,这些属性的值将被序列化为JSON字符串,传送到客户端  
  3. private List<Pattern> excludeProperties;//被排除的属性的正则表达式,这些属性的值在对象序列化时将被忽略  
  4. private String root;//根对象,即要被序列化的对象,如不指定,将序列化action中所有可被序列化的数据  
  5. private boolean wrapWithComments;//是否包装成注释  
  6. private boolean prefix;//前缀  
  7. private boolean enableGZIP = false;//是否压缩  
  8. private boolean ignoreHierarchy = true;//是否忽略层次关系,即是否序列化对象父类中的属性  
  9. private boolean ignoreInterfaces = true;//是否忽略接口  
  10. private boolean enumAsBean = false;//是否将枚举类型作为一个bean处理  
  11. private boolean excludeNullProperties = false;//是否排除空的属性,即是否不序列化空值属性  
  12. private int statusCode;//HTTP状态码  
  13. private int errorCode;//HTTP错误码  
  14. private String contentType;//内容类型,通常为application/json,在IE浏览器中会提示下载,可以通过参数配置<param name="contentType">text/html</param>,则不提示下载  
  15. private String wrapPrefix;//包装前缀  
  16. private String wrapSuffix;//包装后缀  

 

看一下上一篇文章中的相关参数配置:

Xml代码   收藏代码
  1. <package name="json" extends="json-default" namespace="/test">  
  2.     <action name="testByAction"  
  3.             class="cn.ysh.studio.struts2.json.demo.action.UserAction" method="testByAction">  
  4.         <result type="json">  
  5.                 <!-- 这里指定将被Struts2序列化的属性,该属性在action中必须有对应的getter方法 -->  
  6.                 <!-- 默认将会序列所有有返回值的getter方法的值,而无论该方法是否有对应属性 -->  
  7.                 <param name="root">dataMap</param>  
  8.                 <!-- 指定是否序列化空的属性 -->  
  9.                 <param name="excludeNullProperties">true</param>  
  10.                 <!-- 这里指定将序列化dataMap中的那些属性 -->  
  11.                 <param name="includeProperties">  
  12.                     user.*  
  13.                 </param>  
  14.                 <!-- 指定内容类型,默认为application/json,IE浏览器会提示下载 -->  
  15.                 <param name="contentType">text/html</param>  
  16.                 <!-- 这里指定将要从dataMap中排除那些属性,这些排除的属性将不被序列化,一半不与上边的参数配置同时出现 -->  
  17.                 <param name="excludeProperties">  
  18.                     SUCCESS  
  19.                 </param>  
  20.         </result>  
  21.     </action>  
  22. </package>  

 

配置中出现了JSONResult的部分属性名,是的,JSONResult中的属性都可以根据需要在struts.xml中配置对应参数以改变默认值来满足我们的需要。

接下来看看它的两个核心方法:

Java代码   收藏代码
  1. public void execute(ActionInvocation invocation) throws Exception {  
  2.         ActionContext actionContext = invocation.getInvocationContext();  
  3.         HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);  
  4.         HttpServletResponse response = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE);  
  5.   
  6.         try {  
  7.             String json;  
  8.             Object rootObject;  
  9.             //查找指定的需要序列化的对象,否则序列化整个action(上文包括前一篇文章中一提到过多次)  
  10.             if (this.enableSMD) {  
  11.                 // generate SMD  
  12.                 rootObject = this.writeSMD(invocation);  
  13.             } else {  
  14.                 // generate JSON  
  15.                 if (this.root != null) {  
  16.                     ValueStack stack = invocation.getStack();  
  17.                     rootObject = stack.findValue(this.root);  
  18.                 } else {  
  19.                     rootObject = invocation.getAction();  
  20.                 }  
  21.             }  
  22.             //这是最核心的一行代码,包括了如何从rootObject抽取"可以"被序列化的属性的值,然后包装称JSON字符串并返回  
  23.             json = JSONUtil.serialize(rootObject, excludeProperties, includeProperties, ignoreHierarchy,  
  24.                     enumAsBean, excludeNullProperties);  
  25.             //针对JSONP的一个成员方法  
  26.             json = addCallbackIfApplicable(request, json);  
  27.   
  28.             boolean writeGzip = enableGZIP && JSONUtil.isGzipInRequest(request);  
  29.   
  30.             //该方法是org.apache.struts2.json.JSONResult的一个成员方法,用于将JSON字符串根据指定参数包装后发送到客户端  
  31.             writeToResponse(response, json, writeGzip);  
  32.   
  33.         } catch (IOException exception) {  
  34.             LOG.error(exception.getMessage(), exception);  
  35.             throw exception;  
  36.         }  
  37.     }  
  38.   
  39.     /** 
  40.      * 负责根据相关参数配置,将制定JSON字符串发送到客户端 
  41.      * @param response 
  42.      * @param json 
  43.      * @param gzip 
  44.      * @throws IOException 
  45.      */  
  46.     protected void writeToResponse(HttpServletResponse response, String json, boolean gzip)  
  47.             throws IOException {  
  48.         JSONUtil.writeJSONToResponse(new SerializationParams(response, getEncoding(), isWrapWithComments(),  
  49.                 json, false, gzip, noCache, statusCode, errorCode, prefix, contentType, wrapPrefix,  
  50.                 wrapSuffix));  
  51.     }  

 

恕笔者愚钝,找了好多资料,始终不明白这里的"SMD"是个什么意思,所在这里包括下文,都将忽略"SMD"。

可以看到,Struts2序列化对象为JSON字符串的整个过程都被JSONUtil的serialize方法包办了,所以有必要跟入这个方法一探究竟:

Java代码   收藏代码
  1. /** 
  2.  * Serializes an object into JSON, excluding any properties matching any of 
  3.  * the regular expressions in the given collection. 
  4.  *  
  5.  * @param object 
  6.  *            to be serialized 
  7.  * @param excludeProperties 
  8.  *            Patterns matching properties to exclude 
  9.  * @param ignoreHierarchy 
  10.  *            whether to ignore properties defined on base classes of the 
  11.  *            root object 
  12.  * @param enumAsBean 
  13.  *            whether to serialized enums a Bean or name=value pair 
  14.  * @return JSON string 
  15.  * @throws JSONException 
  16.  */  
  17. public static String serialize(Object object, Collection<Pattern> excludeProperties,  
  18.         Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean enumAsBean,  
  19.         boolean excludeNullProperties) throws JSONException {  
  20.     JSONWriter writer = new JSONWriter();  
  21.     writer.setIgnoreHierarchy(ignoreHierarchy);  
  22.     writer.setEnumAsBean(enumAsBean);  
  23.     return writer.write(object, excludeProperties, includeProperties, excludeNullProperties);  
  24. }  

 

该方法还有一个重载的兄弟方法,只是少了boolean enumAsBean这个参数,我们并不关心它,这里不讨论它。可以看到,这个方法更简单:构建一个JSONWriter实例,注入两个参数,然后调用该实例的write方法。我们进入JSONWriter,查看write方法的源码:

Java代码   收藏代码
  1. /** 
  2.  * @param object 
  3.  *            Object to be serialized into JSON 
  4.  * @return JSON string for object 
  5.  * @throws JSONException 
  6.  */  
  7. public String write(Object object, Collection<Pattern> excludeProperties,  
  8.         Collection<Pattern> includeProperties, boolean excludeNullProperties) throws JSONException {  
  9.     this.excludeNullProperties = excludeNullProperties;  
  10.     this.buf.setLength(0);  
  11.     this.root = object;  
  12.     this.exprStack = "";  
  13.     this.buildExpr = ((excludeProperties != null) && !excludeProperties.isEmpty())  
  14.             || ((includeProperties != null) && !includeProperties.isEmpty());  
  15.     this.excludeProperties = excludeProperties;  
  16.     this.includeProperties = includeProperties;  
  17.     this.value(object, null);  
  18.   
  19.     return this.buf.toString();  
  20. }  

 

它同样有一个重载的方法,我们同样不关心,浏览整个方法,不难发现,它只是所做了一些赋值操作,然后将对象的序列化工作交给了value成员方法,那么我们进入value方法看一看: 

Java代码   收藏代码
  1. /** 
  2.  * Detect cyclic references 
  3.  */  
  4. private void value(Object object, Method method) throws JSONException {  
  5.     if (object == null) {  
  6.         this.add("null");  
  7.   
  8.         return;  
  9.     }  
  10.   
  11.     if (this.stack.contains(object)) {  
  12.         Class clazz = object.getClass();  
  13.   
  14.         // cyclic reference  
  15.         if (clazz.isPrimitive() || clazz.equals(String.class)) {  
  16.             this.process(object, method);  
  17.         } else {  
  18.             if (LOG.isDebugEnabled()) {  
  19.                 LOG.debug("Cyclic reference detected on " + object);  
  20.             }  
  21.   
  22.             this.add("null");  
  23.         }  
  24.   
  25.         return;  
  26.     }  
  27.   
  28.     this.process(object, method);  
  29. }  


很简洁,进入process方法

Java代码   收藏代码
  1. /** 
  2.  * Serialize object into json 
  3.  */  
  4. private void process(Object object, Method method) throws JSONException {  
  5.     this.stack.push(object);  
  6.   
  7.     if (object instanceof Class) {  
  8.         this.string(object);  
  9.     } else if (object instanceof Boolean) {  
  10.         this.bool(((Boolean) object).booleanValue());  
  11.     } else if (object instanceof Number) {  
  12.         this.add(object);  
  13.     } else if (object instanceof String) {  
  14.         this.string(object);  
  15.     } else if (object instanceof Character) {  
  16.         this.string(object);  
  17.     } else if (object instanceof Map) {  
  18.         this.map((Map) object, method);  
  19.     } else if (object.getClass().isArray()) {  
  20.         this.array(object, method);  
  21.     } else if (object instanceof Iterable) {  
  22.         this.array(((Iterable) object).iterator(), method);  
  23.     } else if (object instanceof Date) {  
  24.         this.date((Date) object, method);  
  25.     } else if (object instanceof Calendar) {  
  26.         this.date(((Calendar) object).getTime(), method);  
  27.     } else if (object instanceof Locale) {  
  28.         this.string(object);  
  29.     } else if (object instanceof Enum) {  
  30.         this.enumeration((Enum) object);  
  31.     } else {  
  32.         this.bean(object);  
  33.     }  
  34.   
  35.     this.stack.pop();  
  36. }  

 

发现它做了很多判断,并结合不同的方法来支持不同的数据类型,那么从这里我们可以知道Struts-json-plugin支持哪些数据类型了。对于每一种支持的数据类型,Struts-json-plugin都有相应的方法来从从对象中抽取数据并封装成JSON字符串,以Map为例,我们看一下map方法的源码:

Java代码   收藏代码
  1.  /** 
  2.   * Add map to buffer 
  3.   */  
  4.  private void map(Map map, Method method) throws JSONException {  
  5.      //这是一个对象,按照JSON语法,应该以"{}"括起来  
  6. his.add("{");  
  7.   
  8.      Iterator it = map.entrySet().iterator();  
  9.   
  10.      boolean warnedNonString = false// one report per map  
  11.      boolean hasData = false;  
  12.      while (it.hasNext()) {  
  13.          Map.Entry entry = (Map.Entry) it.next();  
  14. //如果key不是String类型,将发出警告  
  15.          Object key = entry.getKey();  
  16. //当前属性的OGNL表达式  
  17.          String expr = null;  
  18.          if (this.buildExpr) {  
  19.              if (key == null) {  
  20.                  LOG.error("Cannot build expression for null key in " + this.exprStack);  
  21.                  continue;  
  22.              } else {  
  23.         //获取完整的OGNL表达式  
  24.                  expr = this.expandExpr(key.toString());  
  25.         //是否是被排除的属性  
  26.         //如果你对上边生成的OGNL表达式的格式有所了解,那么includeProperties和excludeProperties的正则配置绝对不是问题  
  27.                  if (this.shouldExcludeProperty(expr)) {  
  28.                      continue;  
  29.                  }  
  30.         //如果不被排除,则将当前属性名压入表达式栈(其实就是一个String而非传统意义上的栈,此处是模拟,非常精巧的算法)  
  31.         //该方法返回原来的表达式,稍后还将恢复该表达式到"栈"中  
  32.                  expr = this.setExprStack(expr);  
  33.              }  
  34.          }  
  35. //如果还有数据,则以","风格,这是JSON的语法格式  
  36.          if (hasData) {  
  37.              this.add(',');  
  38.          }  
  39.          hasData = true;  
  40. //如果key不是String类型,将发出警告,且只警告一次  
  41.          if (!warnedNonString && !(key instanceof String)) {  
  42.              LOG.warn("JavaScript doesn't support non-String keys, using toString() on "  
  43.                      + key.getClass().getName());  
  44.              warnedNonString = true;  
  45.          }  
  46.          this.value(key.toString(), method);  
  47.          this.add(":");  
  48. //递归抽取数据  
  49.          this.value(entry.getValue(), method);  
  50. //下一层的数据递归完成后,恢复表达式栈值为当前层的属性名  
  51.          if (this.buildExpr) {  
  52.              this.setExprStack(expr);  
  53.          }  
  54.      }  
  55.   
  56.      this.add("}");  
  57.  }  

 

这个方法中比较重要的几行代码都做了注释,不再赘述。过滤某些属性,以使其不被序列化时struts2-JSON应用中非常常见的,比如在序列化一个用户对象的时候,密码信息时不应该被传送到客户端的,所以要排除掉。了解shouldExcludeProperty方法的过滤规则,可以帮助我们更好的使用此功能。源码如下:

Java代码   收藏代码
  1. private boolean shouldExcludeProperty(String expr) {  
  2.         if (this.excludeProperties != null) {  
  3.             for (Pattern pattern : this.excludeProperties) {  
  4.                 if (pattern.matcher(expr).matches()) {  
  5.                     if (LOG.isDebugEnabled())  
  6.                         LOG.debug("Ignoring property because of exclude rule: " + expr);  
  7.                     return true;  
  8.                 }  
  9.             }  
  10.         }  
  11.   
  12.         if (this.includeProperties != null) {  
  13.             for (Pattern pattern : this.includeProperties) {  
  14.                 if (pattern.matcher(expr).matches()) {  
  15.                     return false;  
  16.                 }  
  17.             }  
  18.   
  19.             if (LOG.isDebugEnabled())  
  20.                 LOG.debug("Ignoring property because of include rule:  " + expr);  
  21.             return true;  
  22.         }  
  23.   
  24.         return false;  
  25.     }  

 

非常简单,就是简单的正则匹配,如果有排除配置,则先判断当前属性是否被排除,如果没有被排除,且有包含配置则检查是否被包含,如果没有被包含,则不序列化该属性,如果没有被排除且没有包含配置,则将序列化该属性。

源码跟踪到这里,已经没有继续下去的必要了,因为我们已经很清楚Struts2是如何将一个对象转换成JSON字符串并返回客户端的:


1、收集用户配置;
2、JSONWriter通过判断对象的类型来有针对性的抽取其中的属性值,对于嵌套的对象则采用递归的方式来抽取,抽取的同时,包装成符合JSON语法规范的字符串;
3、JSONUtil.writeJSONToResponse将序列化的JSON字符串按照相关配置发送到客户端;
 
  
不难看出,代码逻辑清晰,简单,朴素,没有半点花巧和卖弄,但确实是非常的精巧,表现出作者扎实的编程功底和过人的逻辑思维能力。尤其是递归抽取嵌套对象的属性值和获取当前属性的OGNL表达式的算法,堪称经典!

通过以上的源码跟踪,我们很清楚的了解Struts2序列化对象的原理和过程,并对相关参数的配置有了深刻的体会。只是令人感到奇怪的是,他并没有使用json-lib.xx.jar中的API接口,而是以字符串拼接的方式手动构建JSON字符串,我想原因可能是因为它要用正则表达式包含或排除某些属性的原因吧,仅作猜测,还望高人指点。

有很多人说不知道includeProperties和excludeProperties的正则表达式该怎么配置,我想说其实很简单,除了正则知识外,就是"对象名.属性名",数组稍微不同,以为它有下标,所以是"数组对象名\[\d+\]\.属性名"。如果这里觉得说的不清楚,可以阅读以下JSONWriter中关于OGNL表达式是如何获取的部分代码,就会明白正则该如何写了。

纯属个人理解,如有错误,烦请指正,不胜荣幸!

转载自 : http://www.yshjava.cn/post/329.html

转载于:https://my.oschina.net/u/129971/blog/131946

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402713.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一张图看懂encodeURI、encodeURIComponent、decodeURI、decodeURIComponent的区别

From:https://www.cnblogs.com/shuiyi/p/5277233.html 一、这四个方法的用处 1、用来编码和解码URI的 统一资源标识符&#xff0c;或叫做 URI&#xff0c;是用来标识互联网上的资源&#xff08;例如&#xff0c;网页或文件&#xff09;和怎样访问这些资源的传输协议&#xf…

关于axios请求报400如何获取报错信息

不废话&#xff0c;直接放代码 addGoods(product).then(res > {if (res.code 200) {this.$message.success("添加成功");this.handleFilter();} else {this.$message.error("添加失败");}}).catch(res > {console.log(res)console.log(res.respons…

炫酷弹窗效果制作

昨天在家看电视时&#xff0c;退出的时候发现了一个弹窗效果&#xff0c;整个背景模糊&#xff0c;觉得这样的效果好炫&#xff0c;要比纯色加透明度高大上好多&#xff0c;连续试了几个界面&#xff0c;最终确定效果由css实现的&#xff0c;于是今天一大早来到公司便赶紧搜索了…

选择版本Win7系统VS2010下搭建qt开发环境

这几周一直在研究选择版本之类的问题,下午正好有机会和大家共享一下. win7下vs2010搭建qt环境总算成功了&#xff0c;在此分享一下。 最初选择了VS2012 qt-windows-opensource-5.0.2-msvc2012_64-x64-offline.exe &#xff0b; qt-vs-addin-1.2.1-opensource.exe 一路安装成功…

学习 Spring Boot:(二十九)Spring Boot Junit 单元测试

From: https://blog.wuwii.com/springboot-test.html 前言 JUnit 是一个回归测试框架&#xff0c;被开发者用于实施对应用程序的单元测试&#xff0c;加快程序编制速度&#xff0c;同时提高编码的质量。 JUnit 测试框架具有以下重要特性&#xff1a; 测试工具测试套件测试运…

如何通过Maven的Tomcat插件运行Web工程

From: https://blog.wuwii.com/maven-tomcat.html Tomcat 服务器是一个免费的开放源代码的Web 应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发和调试JSP 程序的首选。对于一个初学者来说&…

DUMPE2FS(8)

为什么80%的码农都做不了架构师&#xff1f;>>> DUMPE2FS(8) DUMPE2FS(8) NAME dumpe2fs - dump ext2/ext3/ext4 filesystem information SYNOPSIS dumpe2fs [ -bfhixV ] [ -o superblocksuperblock ] [ -o blocksizeblock- size ] …

Maven多模块,Dubbo分布式服务框架,SpringMVC,前后端分离项目,基础搭建,搭建过程出现的问题...

现互联网公司后端架构常用到SpringSpringMVCMyBatis&#xff0c;通过Maven来构建。通过学习&#xff0c;我已经掌握了基本的搭建过程&#xff0c;写下基础文章为而后的深入学习奠定基础。 首先说一下这篇文章的主要内容分为&#xff1a; 1、Maven多模块项目的创建&#xff1b; …

基于struts2,hibernate的小javaweb项目

19:47:49 这是截图 闲话不说 就开始了 web-xml&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <web-app version"2.5" xmlns"http://java.sun.com/xml/ns/javaee"xmlns:xsi"http://www.w3.org/2001/XMLSchema-in…

MySql的用户权限

用户管理 MySQL数据库中的表与其他任何关系表没有区别&#xff0c;都可以通过典型的SQL命令修改其结构和数据。可以使用GRANT和REVOKE命令。通过这些命令&#xff0c;可以创建和禁用用户&#xff0c;可以在线授予和撤回用户访问权限。在5.0版本中增加了两个新命令&#xff1a;C…

.yaml 文件格式简介

From: https://www.cnblogs.com/wxmdevelop/p/7341292.html YAML 的意思其实是&#xff1a;"Yet Another Markup Language"&#xff08;仍是一种置标语言&#xff09;的缩写。 功能 YAML的语法和其他高阶语言类似&#xff0c;并且可以简单表达清单、散列表&#x…

SnakeYaml快速入门

From: https://www.jianshu.com/p/d8136c913e52 在YAML快速入门[https://www.jianshu.com/p/97222440cd08]中&#xff0c;我们已经简单介绍了YAML的语法&#xff0c;本节中主要介绍YAML的配置读取。 目前有很多可以生成和解析YAML的第三方工具&#xff0c;常见的&#xff0c;…

有源代码的iphone项目

2019独角兽企业重金招聘Python工程师标准>>> http://blog.joomla.org.tw/iphone-ipad/104-iphone.html 學習和利用現成的資源是很重要的&#xff0c;以下列出有原始碼可下載的iPhone/iPod程式&#xff0c;這邊收集的是以已經放到App Store上的程式為主&#xff0c;…

Content Security Policy 入门教程

From: http://www.ruanyifeng.com/blog/2016/09/csp.html 跨域脚本攻击 XSS 是最常见、危害最大的网页安全漏洞。 为了防止它们&#xff0c;要采取很多编程措施&#xff0c;非常麻烦。很多人提出&#xff0c;能不能根本上解决问题&#xff0c;浏览器自动禁止外部注入恶意脚本&…

springboot跨域配置

From: https://www.cnblogs.com/nananana/p/8492185.html 前言&#xff1a; 当它请求的一个资源是从一个与它本身提供的第一个资源的不同的域名时&#xff0c;一个资源会发起一个跨域HTTP请求(Cross-site HTTP request)。 比如说&#xff0c;域名A ( http://domaina.example …

l2正则化

在机器学习中&#xff0c;无论是分类还是回归&#xff0c;都可能存在由于特征过多而导致的过拟合问题。当然解决的办法有 &#xff08;1&#xff09;减少特征&#xff0c;留取最重要的特征。 &#xff08;2&#xff09;惩罚不重要的特征的权重。 但是通常情况下&#xff0c;我们…

机房收费系统的合作版

概述 机房收费系统的合作版自己负责的是B层和Facade层&#xff0c;在做这块的时候有很多的感触&#xff1a;动态SQL语句&#xff1b;设计模式&#xff1b;合作开发应该注意的点。其中动态SQL语句的理解已经在上一篇博客中写了&#xff0c;如果你有意向的话&#xff0c;可以看一…

ajax跨域,这应该是最全的解决方案了

From: https://segmentfault.com/a/1190000012469713 前言 从刚接触前端开发起&#xff0c;跨域这个词就一直以很高的频率在身边重复出现&#xff0c;一直到现在&#xff0c;已经调试过N个跨域相关的问题了&#xff0c;16年时也整理过一篇相关文章&#xff0c;但是感觉还是差…

如何导入ShareSDK的sample

由于项目需要&#xff0c;最近需要做10几个平台的分享&#xff0c;如果自己去集成&#xff0c;浪费很多时间&#xff0c;而且还很难成功。最后发现Sharesdk,可以满足项目需求。 首先&#xff0c;需要到他们的官网http://sharesdk.cn/下载android版本的SDK。 然后玩了一下他们的…

EF5.x Code First 一对多关联条件查询,Contains,Any,All

背景 通过多个部门id获取所有用户&#xff0c;部门和用户是多对多。 已知部门id&#xff0c;获取该部门包括该部门下的所有子部门的所有用户。 关系如下&#xff1a; public class Entity:IEntity{public Guid Id { get; set; }public string CreateUser { get; set; }public D…