Content-Type引发的服务端收不到HTTP请求参数的问题

问题现象:

前端POST请求参数已经传过来了,Java后端Debug也能进到请求里,可就是取不到请求参数。


用Chrome 开发者工具可以看到请求的不同:



可以看到请求参数一个在Form Data中,一个在Request Payload中,而且格式也不同。

不同的原因就在于Content-Type设置不同。


HTTP Content-Type 

用于标识传输数据的类型。在请求中,Content-Type告诉服务端实际请求内容的类型;在响应中,Content-Type告诉客户端实际返回内容的类型。

HTTP定义的Content-Type类型有近200种(https://www.w3cschool.cn/http/ahkmgfmz.html),其中最常用的是以下三种:

1、application/x-www-form-urlencoded
请求参数在Form Data中,只能上传键值对,并且键值对都是间隔分开的。
参数形式:  name1=value1&name2=value2

2、multipart/form-data
请求参数在Request Payload中,既可以上传文件等二进制数据,也可以上传表单键值对,只是最后会转化为一条信息。
浏览器将表单内的数据和文件放在一起发送。这种方式会定义一个不可能在数据中出现的字符串作为分隔符,然后用它将各个数据段分开。

3、application/json
请求参数在Request Payload中
参数形式: {key1:value1,key2:value2}


multipart/form-data; boundary=${bound}   // 其中${bound}是一个占位符,代表我们规定的分割符,可以自己任意规定,但为了避免和正常文本重复了,尽量要使用复杂一点的内容。

以下是一个multipart/form-data类型的请求:



重点:对应以上三种类型Java服务端获取请求参数的方法也不同(伪代码)

1、application/x-www-form-urlencoded

1)注解@RequestParam(value="name1") String name1  
2)注解@ModelAttribute 绑定请求参数到指定对象
3)HttpServletRequest.getParameter("name1")

2、multipart/form-data

流HttpServletRequest.getInputStream()或者HttpServletRequest.getReader()

3、application/json
1)注解@RequestBody 
2)流HttpServletRequest.getInputStream()或者HttpServletRequest.getReader()

Tips: request.getParameter()、 request.getInputStream()、request.getReader()这三种方法是有冲突的,因为流只能被读一次,所以只有第一次能取到参数。


原理

那么是什么导致Content-Type类型不同取参方式也不同呢?
由于Tomcat对于Content-Type multipart/form-data(文件上传)和application/x-www-form-urlencoded(POST请求)做了“特殊处理”:
Tomcat的HttpServletRequest类的实现类为org.apache.catalina.connector.Request,而它处理请求参数的方法为protected void parseParameters(),这个方法中对Content-Type multipart/form-data(文件上传)和application/x-www-form-urlencoded(POST请求)做了处理,会解析表单数据放到request parameter map中。其他请求不会解析表单数据,所以通过request.getParameter()是获取不到的。

服务器为什么会对Content-Type application/x-www-form-urlencoded做特殊处理?

因为表单提交数据是名值对的方式,而其他的post请求(Content-Type不是application/x-www-form-urlencoded)数据格式不固定,不一定是名值对的方式,服务器无法知道具体的处理方式,所以只能通过获取原始数据流的方式来进行解析。

tomcat部分源码:

protectedvoid parseParameters() {  
           //省略部分代码......  
           parameters.handleQueryParameters();// 这里是处理url中的参数  
           //省略部分代码......  
           if ("multipart/form-data".equals(contentType)) { // 这里是处理文件上传请求  
                parseParts();  
                success = true;  
                return;  
           }  
   
           if(!("application/x-www-form-urlencoded".equals(contentType))) {// 这里如果是非POST请求直接返回,不再进行处理  
                success = true;  
                return;  
           }  
           //下面的代码才是处理POST请求参数  
           //省略部分代码......  
           try {  
                if (readPostBody(formData, len)!= len) { // 读取请求体数据  
                    return;  
                }  
           } catch (IOException e) {  
                // Client disconnect  
                if(context.getLogger().isDebugEnabled()) {  
                    context.getLogger().debug(  
                            sm.getString("coyoteRequest.parseParameters"),e);  
                }  
                return;  
           }  
           parameters.processParameters(formData, 0, len); // 处理POST请求参数,把它放到requestparameter map中(即request.getParameterMap获取到的Map,request.getParameter(name)也是从这个Map中获取的)  
           // 省略部分代码......  
}

protected int readPostBody(byte body[], int len)  
   throws IOException {  

   int offset = 0;  
   do {  
	   int inputLen = getStream().read(body, offset, len - offset);  
	   if (inputLen <= 0) {  
			return offset;  
	   }  
	   offset += inputLen;  
   } while ((len - offset) > 0);  
   return len;  
}

实际开发中具体选用哪种Content-Type呢?
Form解析可以直接从Request对象中获取请求参数,这样对象转换与处理相对容易,但在大片JSON数据需要提交时,可能会出现大量的数据拆分与处理工作,另外针对集合类型的处理,也是其比较孱弱的地方。
而Payload的优势是一次可以提交大量JSON字符串,但无法从Request从获取参数,也会受限于JSON解析的深度(尤其是有多层对象级联的情况,最底层的对象几乎无法转换为具体类型)。


其他
jQuery在执行post请求时,会默认设置Content-Type为application/x-www-form-urlencoded,所以服务器能够正确解析,
而使用原生ajax请求时,如果不显示的设置Content-Type,那么默认是text/plain,这时服务器就不知道怎么解析数据了,所以才只能通过获取原始数据流的方式来进行解析请求数据。

jQuery中的dataType指的是预期服务器返回的数据类型,而不是发送的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,比如 XML MIME 类型就被识别为 XML。这样服务端返回json数据,前端就会获取不到返回值。


  • 11
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
选择器速度提升<br>选择器的速度大幅度提高了,下表为jQuery1.1.2和1.1.3的选择器速度对比,提高了8倍多<br>Browser jQuery 1.1.2 jQuery 1.1.3 % Improvement <br>IE 6 4890ms 661ms 740% <br>Firefox 2 5629ms 567ms 993% <br>Safari 2 3575ms 475ms 753% <br>Opera 9.1 3196ms 326ms 980% <br>Average improvement: 867% <br><br>下表为jQuery1.1.3与常用的一些JS库选择器的对比:<br>Browser Prototype jQuery Mootools Ext Dojo <br>IE 6 1476ms 661ms 1238ms 672ms 738ms <br>Firefox 2 219ms 567ms 220ms 951ms 440ms <br>Safari 2 1568ms 475ms 909ms 417ms 527ms <br>Opera 9.1 220ms 326ms 217ms 296ms 220ms <br><br><br>更新项目<br><br><br>New Selectors<br>Unicode Selectors: This is a huge addition for those of you who want to use Unicode attribute values, IDs, class names, or tag names. You can now use them directly in jQuery selectors:<br><br>$("div.台北")<br>$("div#台北")<br>$("foo_bar台北")<br>$("div[@id=台北]")Escape Selectors: A frequently requested feature you can now select elements by ID (or other selector) that uses a special character, for example this will find the div that has the ID of “foo.bar”:<br><br>$("div#foo\\.bar")Inequality Selector: While this selector isn’t part of the CSS specification, it’s frequently used and included in other selector libraries, so we decided to add it in:<br><br>$("div[@id!=test]"):nth-child() improvements: This selector allows you to locate specific child elements. We’ve supported selectors like :nth-child(1) and :nth-child(odd) since the beginning of jQuery, now we’ve added advanced :nth-child selectors, such as:<br><br>$("div:nth-child(2n)")<br>$("div:nth-child(2n+1)")<br>$("div:nth-child(n)")Space-separated attributes: After being removed in jQuery 1.0, this selector has now been brought back by popular demand. It allows you to locate individual items in a space-separated attribute (such as a class or rel attribute).<br><br>$("a[@rel~=test]")Animation Improvements<br><br>参数: <br>options <br>返回值: <br>XMLHttpRequest <br>使用HTTP请求一个页面。<br>这是jQuery的低级AJAX实现。要查看高级抽象,见$.set、$.post等,这些方法更易于理解和使用。但是功能上有限制(例如,没有错误处理函数)。<br>警告:如果数据类型指定为"script",那么POST自动转化为GET方法。(因为script会作为一个嵌入页面的script标签进行载入) <br>$.ajax()函数返回它创建的XMLHttpRequest对象。在大部分情况下,你不需要直接操作此对象。通常,这个XMLHttpRequest对象主要用于需要手动中断XMLHttpRequest请求的时候。 <br>注意:如果你指明了下面列出的数据类型,请确保服务端发送了正确的MIME响应类型(如. xml 的类型是 "text/xml")。错误的MIME类型能够导致脚本出现意想不到问题。请查看AJAX的范例来了解数据类型的更多信息。 <br>$.ajax()函数需要一个参数,一个包含有键/值对的对象,用于初始化并操作请求对象。 <br>在jQuery 1.2版本中,如果你指明了一个JSONP回调函数,你就可以从其它的域中载入JSON类型的数据,写法类似于 "myurl?callback=?" 。jQuery会自动调用正确的方法名称来代替查询字符串,执行你指定的回调函数。或者,你也可以指定jsonp的数据类型的回调函数,此函数会自动添加到Ajax请求中。 <br>参数选项: <br>async(true) 数据类型: Boolean <br>默认情况下,所有的请求都是异步发送的(默认为true)。 如果需要发送同步请求, 设置选项为false。注意,同步请求可能会暂时的锁定浏览器, 当请求激活时不能进行任何操作。 <br>beforeSend 数据类型: Function <br>一个预处理函数用于在发送前修改XMLHttpRequest对象,设置自定义头部等。 XMLHttpRequest作为惟一的参数被传递。这是一个 Ajax 事件。 function (XMLHttpRequest) {<br> this; // the options for this ajax request<br>}cache(true) 数据类型: Boolean <br>jQuery 1.2中新添加的参数, 如果设为false,则会强制浏览器不缓存请求的页面。 <br>complete 数据类型: Function <br>当请求完成时执行的函数(在成功或失败之后执行)。这个函数有2个参数: XMLHttpRequest对象和一个描述HTTP相应的状态字符串。 这是一个 Ajax 事件。 function (XMLHttpRequest, textStatus) {<br> this; // the options for this ajax request<br>}contentType("application/x-www-form-urlencoded") 数据类型: String <br>发送到服务器的数据的内容类型。默认是 "application/x-www-form-urlencoded", 适合大多数情况。 <br>data 数据类型: Object,String <br>要发送给服务器的数据。如果不是字符串,那么它会被转化为一个查询字符串。在GET请求中它被添加到url的末尾。要防止这种自动转化,请查看processData选项。 数据对象必须是一组键/值对。如果键对应的值是数组,jQuery会将其值赋给同一个键属性。 例如 {foo:["bar1", "bar2"]} 变为 '&foo=bar1&foo=bar2'。 <br>dataType( Intelligent Guess (xml or html)) 数据类型: String <br>期待由服务器返回值类型。如果没有明确指定,jQuery会根据实际返回的MIME类型自动的将responseXML或responseText传递给success指定的回调函数。有效的类型(返回的类型的结果值会作为第一个参数传递给success指定的回调函数)有: "xml": 返回一个可以由jQuery处理的XML文档。 <br>"html": 返回文本格式的HTML代码。包括求值后的脚本标记。 <br>"script": 将响应作为Javascript语句求值,并返回纯文本。不缓存此脚本,除非设置了cache选项。设置为"script"类型会将post方法转换为get方法。 <br>"json": 将响应作为JSON求值,并返回一个Javascript对象。 <br>"jsonp": 使用JSONP载入一个JSON代码块. 会在URL的末尾添加"?callback=?"来指明回调函数。(jQuery 1.2以上的版本支持) <br>"text": 文本格式的字符串 <br>error 数据类型: Function <br>请求失败时执行的函数。函数具有3个参数: XMLHttpRequest对象,一个描述产生的错误类型和一个可选的异常对象, 如果有的化。 这是一个Ajax 事件。function (XMLHttpRequest, textStatus, errorThrown) {<br> // typically only one of textStatus or errorThrown <br> // will have info<br> this; // the options for this ajax request<br>}global(true) 数据类型: Boolean <br>是否为当前的请求触发全局AJAX事件处理函数,默认值为true。设置为false可以防止触发像ajaxStart或ajaxStop这样的全局事件处理函数。这可以用于控制多个不同的Ajax事件。 <br>ifModified(false) 数据类型: Boolean <br>只有响应自上次请求后被修改过才承认是成功的请求。是通过检查头部的Last-Modified值实现的。默认值为false,即忽略对头部的检查 <br>jsonp 数据类型: String <br>在jsonp请求中重新设置回调的函数。这个值用于代替'callback=?'中的查询字符串。'callback=?'位于get请求中url的末尾或是post请求传递的数据中。因此设置 {jsonp:'onJsonPLoad'} 会将 'onJsonPLoad=?' 传送给服务器。 <br>processData(true) 数据类型: Boolean <br>在默认的情况下,如果data选项传进的数据是一个对象而不是字符串,将会自动地被处理和转换成一个查询字符串,以适应默认的content-type--"application/x-www-form-urlencoded"。如果想发送DOMDocuments,就要把这个选项设置为false。 <br>success 数据类型: Function <br>当请求成功时调用的函数。这个函数会得到二个参数:从服务器返回的数据(根据“dataType”进行了格式化)和一个描述HTTP相应的状态字符串。这是一个 Ajax 事件。 function (data, textStatus) {<br> // data could be xmlDoc, jsonObj, html, text, etc...<br> this; // the options for this ajax request<br>}timeout 数据类型: Number <br>如果通过$.ajaxSetup设置了一个全局timeout,那么此函数使用一个局部timeout覆盖了全局timeout(单位为毫秒)。例如,你可以设置比较长的延迟给一个特殊的请求,同时其他所有请求使用1秒的延迟。有关全局延迟,见$.ajaxTimeout()。 <br>type("GET") 数据类型: String <br>请求的类型 ("POST" 或 "GET"), 默认是 "GET"。注意:其他的HTTP请求方法,如PUT和DELETE,在这里也可以使用,当时它们并不被所有的浏览器支持。 <br>url(The current page) 数据类型: String <br>请求发送的目标URL地址 <br>username 数据类型: String <br>username可用于在响应一个HTTP连接时的认证请求。 实例 <br>载入并执行一个JavaScript文件。 <br>$.ajax({<br> type: "GET",<br> url: "test.js",<br> dataType: "script"<br>});保存数据到服务器,完成后通知用户。 <br> $.ajax({<br> type: "POST",<br> url: "some.php",<br> data: "name=John&location=Boston",<br> success: function(msg){<br> alert( "Data Saved: " + msg );<br> }<br> });取得一个HTML页面的最新版本。 <br>$.ajax({<br> url: "test.html",<br> cache: false,<br> success: function(html){<br> $("#results").append(html);<br> }<br>});同步载入数据。在执行请求的时候阻塞浏览器。这是在保证数据的同步性比交互更重要的情况下的一种更好的方法。 <br> var html = $.ajax({<br> url: "some.php",<br> async: false<br> }).responseText;向服务器发送xml文档数据。通过设置processData选项为false,将数据自动转换为string的动作被禁止了。 <br>var xmlDocument = [create xml document];<br> $.ajax({<br> url: "page.php",<br> processData: false,<br> data: xmlDocument,<br> success: handleResponse<br> });load( url, [data], [callback] )参数: <br>url (String): 装入页面的URL地址。 <br>params (Map): (可选)发送到服务端的键/值对参数。 <br>callback (Function): (可选) 当数据装入完成时执行的函数. function (responseText, textStatus, XMLHttpRequest) {<br> this; // dom element<br>}返回值: <br>jQuery <br>装入一个远程HTML内容到一个DOM结点。 默认使用get方法发送请求,但如果指定了额外的参数,将会使用post方法发送请求。在 jQuery 1.2中,可以在URL参数中指定一个jQuery选择器,这会过滤返回的HTML文档,只取得文档中匹配选择器的元素。此语法类似于"url #some > selector"。 <br>实例 <br>载入文档的sidebar的导航部分到一个无序列表中。 <br>$("#links").load("/Main_Page #p-Getting-Started li");将feeds.html文件载入到id为feeds的div中。 <br>$("#feeds").load("feeds.html");同上,但是发送了附加的参数,并且在响应结束后执行一个自定义函数。 <br> $("#feeds").load("feeds.php", {limit: 25}, function(){<br> alert("The last 25 entries in the feed have been loaded");<br> });jQuery.get( url, [data], [callback] )参数: <br>url (String): 装入页面的URL地址 <br>Map(可选): (可选)发送到服务端的键/值对参数 <br>callback (Function): (可选) 当远程页面装入完成时执行的函数 function (data, textStatus) {<br> // data可以是xmlDoc, jsonObj, html, text, 等...<br> this; // the options for this ajax request<br>}返回值: <br>XMLHttpRequest <br>使用GET请求一个页面。 <br>这是向服务器发送get请求的简单方法。它可以指定一个回调函数,在请求完成后执行(只有在请求成功时)。如果还需要设置error和success回调函数,则需要使用$.ajax。 <br>实例 <br>请求test.php页,忽略返回值. <br>$.get("test.php");请求test.php页并发送附加数据(忽略返回值). <br>$.get("test.php", { name: "John", time: "2pm" } );显示从test.php请求的返回值(HTML 或 XML, 根据不同返回值). <br>$.get("test.php", function(data){<br> alert("Data Loaded: " + data);<br>});显示向test.cgi发送附加数据请求的返回值 (HTML 或 XML, 根据不同返回值). <br>$.get("test.cgi", { name: "John", time: "2pm" },<br> function(data){<br> alert("Data Loaded: " + data);<br> });jQuery.getJSON( url, [data], [callback] )参数: <br>url (String): 装入页面的URL地址 <br>Map(可选): (可选)发送到服务端的键/值对参数 <br>callback (Function): (可选) 当数据装入完成时执行的函数 function (data, textStatus) {<br> // data will be a jsonObj<br> this; // the options for this ajax request<br>}返回值: <br>XMLHttpRequest <br>使用GET请求JSON数据。 <br>在jQuery 1.2版本中,如果你指明了一个JSONP回调函数,你就可以从其它的域中载入JSON类型的数据,写法类似于 "myurl?callback=?" 。jQuery会自动调用正确的方法名称来代替查询字符串,执行你指定的回调函数。或者,你也可以指定jsonp的数据类型的回调函数,此函数会自动添加到Ajax请求中。注意: 请记住, that lines after this function will be executed before callback. <br>实例 <br>从Flickr JSONP API中载入最新的四幅猫的图片 <br>$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?",<br> function(data){<br> $.each(data.items, function(i,item){<br> $("<img/>").attr("src", item.media.m).appendTo("#images");<br> if ( i == 3 ) return false;<br> });<br> });从test.js载入JSON数据, 从返回的JSON数据读取name值。 <br>$.getJSON("test.js", function(json){<br> alert("JSON Data: " + json.users[3].name);<br>});从test.js载入JSON数据, 传递一个附加参数,从返回的JSON数据读取name值。 <br>$.getJSON("test.js", { name: "John", time: "2pm" }, function(json){<br> alert("JSON Data: " + json.users[3].name);<br>});显示向test.php发送请求的返回值 (HTML 或 XML, 根据不同返回值). <br>$.getIfModified("test.php", function(data){<br> alert("Data Loaded: " + data);<br>});显示向test.php发送请求的返回值 (HTML 或 XML, 根据不同返回值),提供了一个附加的参数. <br>$.getIfModified("test.php", { name: "John", time: "2pm" },<br> function(data){<br> alert("Data Loaded: " + data);<br> });列出从pages.php返回的查询结果,将返回的数组转化为一段HTML代码。 <br>var id=$("#id").attr("value");<br> $.getJSON("pages.php",{id:id},dates);<br>function dates(datos)<br>{ <br> $("#list").html("Name:"+datos[1].name+"<br>"+"Last Name:"+datos[1].lastname+"<br>"+"Address:"+datos[1].address);<br>}jQuery.getScript( url, [callback] )参数: <br>url (String): 装入页面的URL地址 <br>callback (Function): (可选) 当数据装入完成时执行的函数 function (data, textStatus) {<br> // data应该是javascript<br> this; // the options for this ajax request<br>}返回值: <br>XMLHttpRequest <br>使用GET请求JavaScript文件并执行。 <br>在jQuery 1.2前, getScript只能从页面所在的主机载入脚本,1.2中, 你可以从任何主机载入脚本。警告: Safari 2 及其更老的版本不能在全局上下文中正确识别脚本。如果你通过getScript载入函数,请保证设置一个延迟来执行这个脚本。 <br>实例 <br>我们动态的载入一个新的官方jQuery颜色动画插件,载入后绑定一些动画效果到元素上。 <br>$.getScript("http://dev.jquery.com/view/trunk/plugins/color/jquery.color.js", function(){<br> $("#go").click(function(){<br> $(".block").animate( { backgroundColor: 'pink' }, 1000)<br> .animate( { backgroundColor: 'blue' }, 1000);<br> });<br>});载入test.js JavaScript文件并执行。 <br>$.getScript("test.js");载入test.js JavaScript文件并执行,当执行结束后显示一条警告信息。 <br>$.getScript("test.js", function(){<br> alert("Script loaded and executed.");<br>});jQuery.post( url, [data], [callback], [type] )参数: <br>url (String): 装入页面的URL地址 <br>Map(可选): (可选)发送到服务端的键/值对参数 <br>callback (Function): (可选) 当数据装入完成时执行的函数 function (data, textStatus) {<br> // data可能是 xmlDoc, jsonObj, html, text, 等...<br> this; // the options for this ajax request<br>}String $.postJSON = function(url, data, callback) {<br> $.post(url, data, callback, "json");<br>};返回值: <br>XMLHttpRequest <br>使用POST请求一个页面。 <br>这是向服务器发送post请求的简单方法。它可以指定一个回调函数,在请求完成后执行(只有在请求成功时)。如果还需要设置error和success回调函数,则需要使用$.ajax。 <br>ajaxComplete( callback )参数: <br>callback (Function): 要执行的函数 function (event, XMLHttpRequest, ajaxOptions) {<br> this; // dom element listening<br>}返回值: <br>jQuery <br>当一个AJAX请求结束后,执行一个函数。这是一个Ajax事件 <br>实例 <br>当AJAX请求完成时显示一条信息。 <br>$("#msg").ajaxComplete(function(request, settings){<br> $(this).append("<li>Request Complete.</li>");<br> });ajaxError( callback )参数: <br>callback (Function): 要执行的函数 function (event, XMLHttpRequest, ajaxOptions, thrownError) {<br> // thrownError only passed if an error was caught<br> this; // dom element listening<br>}返回值: <br>jQuery <br>当一个AJAX请求失败后,执行一个函数。这是一个Ajax事件. <br>实例 <br>当AJAX请求错误时显示一条信息。 <br> $("#msg").ajaxError(function(request, settings){<br> $(this).append("<li>Error requesting page " + settings.url + "</li>");<br> });ajaxSend( callback )参数: <br>callback (Function): 要执行的函数 function (event, XMLHttpRequest, ajaxOptions) {<br> this; // dom element listening<br>}返回值: <br>jQuery <br>在一个AJAX请求发送时,执行一个函数。这是一个Ajax事件. <br>实例 <br>当AJAX请求发出后显示一条信息。 <br> $("#msg").ajaxSend(function(evt, request, settings){<br> $(this).append("<li<Starting request at " + settings.url + "</li<");<br> });ajaxStart( callback )参数: <br>callback (Function): 要执行的函数 function () {<br> this; // dom element listening<br>}返回值: <br>jQuery <br>在一个AJAX请求开始但还没有激活时,执行一个函数。这是一个Ajax事件. <br>实例 <br>当AJAX请求开始(并还没有激活时)显示loading信息。 <br>$("#loading").ajaxStart(function(){<br> $(this).show();<br> });ajaxStop( callback )参数: <br>callback (Function): 要执行的函数 function () {<br> this; // dom element listening<br>}返回值: <br>jQuery <br>当所有的AJAX都停止时,执行一个函数。这是一个Ajax事件. <br>实例 <br>当所有AJAX请求都停止时,隐藏loading信息。 <br>$("#loading").ajaxStop(function(){<br> $(this).hide();<br> });ajaxSuccess( callback )参数: <br>callback (Function): 要执行的函数 function (event, XMLHttpRequest, ajaxOptions) {<br> this; // dom element listening<br>}返回值: <br>jQuery <br>当一个AJAX请求成功完成后,执行一个函数。这是一个Ajax事件 <br>实例 <br>当AJAX请求成功完成时,显示信息。 <br> $("#msg").ajaxSuccess(function(evt, request, settings){<br> $(this).append("<li>Successful Request!</li>");<br> });jQuery.ajaxSetup( options )参数: <br>Options: 用于Ajax请求的键/值对 <br>为所有的AJAX请求进行全局设置。查看$.ajax函数取得所有选项信息。 <br>实例 <br>设置默认的全局AJAX请求选项。 <br>$.ajaxSetup({<br> url: "/xmlhttp/",<br> global: false,<br> type: "POST"<br>});<br>$.ajax({ data: myData });serialize( )返回值: <br>jQuery <br>以名称和值的方式连接一组input元素。返回值类似于: single=Single2&multiple=Multiple&multiple=Multiple3&radio=radio2 。在jQuery 1.2中。serialize方法实现了正确表单元素序列,而不再需要插件支持。 <br>实例 <br>连接表单元素的一组查询字符串,可用于发送Ajax请求。 <br> function showValues() {<br> var str = $("form").serialize();<br> $("#results").text(str);<br> }<br><br> $(":checkbox, :radio").click(showValues);<br> $("select").change(showValues);<br> showValues();serializeArray( )返回值: <br>jQuery <br>连接所有的表单和表单元素(类似于.serialize()方法),但是返回一个JSON数据格式。 <br>实例 <br>从form中取得一组值,显示出来 <br> function showValues() {<br> var fields = $(":input").serializeArray();<br> alert(fields);<br> $("#results").empty();<br> jQuery.each(fields, function(i, field){<br> $("#results").append(field.value + " ");<br> });<br> }<br><br> $(":checkbox, :radio").click(showValues);<br> $("select").change(showValues);<br> showValues();
一:RestApi接口增加JWT认证功能<br/> 用户填入用户名密码后,与数据库里存储的用户信息进行比对,如果通过,则认证成功。传统的方法是在认证通过后,创建sesstion,并给客户端返回cookie。 现在我们采用JWT来处理用户名密码的认证。区别在于,认证通过后,服务器生成一个token,将token返回给客户端,客户端以后的所有请求都需要在http头中指定该token。 服务器接收的请求后,会对token的合法性进行验证。验证的内容包括: 内容是一个正确的JWT格式 检查签名 检查claims 检查权限 处理登录 创建一个类JWTLoginFilter,核心功能是在验证用户名密码正确后,生成一个token,并将token返回给客户端: 该类继承自UsernamePasswordAuthenticationFilter,重写了其中的2个方法: attemptAuthentication :接收并解析用户凭证。 successfulAuthentication :用户成功登录后,这个方法会被调用,我们在这个方法里生成token。 二:授权验证 用户一旦登录成功后,会拿到token,后续的请求都会带着这个token,服务端会验证token的合法性。 创建JwtAuthenticationFilter类,我们在这个类中实现token的校验功能。 该类继承自BasicAuthenticationFilter,在doFilterInternal方法中,从http头的Authorization 项读取token数据,然后用Jwts包提供的方法校验token的合法性。 如果校验通过,就认为这是一个取得授权的合法请求。 三:SpringSecurity配置 通过SpringSecurity的配置,将上面的方法组合在一起。 这是标准的SpringSecurity配置内容,就不在详细说明。注意其中的 .addFilter(new JWTLoginFilter(authenticationManager())) .addFilter(new JwtAuthenticationFilter(authenticationManager())) 这两行,将我们定义的JWT方法加入SpringSecurity的处理流程中。 四:简单测试 下面对我们的程序进行简单的验证:<br/> 1.请求获取用户列表接口:http://localhost:8080/users/userList接口,会收到403错误<br/> { "timestamp": 1518333248079, "status": 403, "error": "Forbidden", "message": "Access Denied", "path": "http://localhost:8080/users/userList" } curl http://localhost:8080/users/userList<br/> 原因就是因为这个url没有授权,所以返回403<br/> ![输入图片说明](https://gitee.com/uploads/images/2018/0211/154022_8d9806ae_130820.png "jwt-1.png") 2.注册一个新用户<br/> curl -H "Content-Type: application/json" -X POST -d '{<br/> "username": "admin",<br/> "password": "password"<br/> }' http://localhost:8080/users/signup<br/> ![输入图片说明](https://gitee.com/uploads/images/2018/0211/154042_74fb2aa6_130820.png "jwt-2.png") 3.登录,会返回token,在http header中,Authorization: Bearer 后面的部分就是token<br/> curl -i -H "Content-Type: application/json" -X POST -d '{<br/> "username": "admin",<br/> "password": "password"<br/> }' http://localhost:8080/login<br/> 温馨提醒:这里的login方法是spring specurity框架提供的默认登录url ![输入图片说明](https://gitee.com/uploads/images/2018/0211/154308_9576ce90_130820.png "jwt-3.png") 4.用登录成功后拿到的token再次请求/users/userList接口<br/> 4.1将请求中的XXXXXX替换成拿到的token<br/> 4.2这次可以成功调用接口了<br/> curl -H "Content-Type: application/json"<br/> -H "Authorization: Bearer XXXXXX"<br/> "http://localhost:8080/users/userList" ![输入图片说明](https://gitee.com/uploads/images/2018/0211/154315_241cd6b2_130820.png "jwt-4.png")
第一个文件ajax_txt.asp的代码: (此页是AJAX异步提交txt文本文件的路径到ajax_txt_save.asp页面,并从ajax_txt_save.asp获取返回的数据) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>无标题文档</title> <style type="text/css"> <!-- body,td,th { font-size: 12px; } body { margin-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; } --> </style> <script type="text/javascript"> <!-- //将用户输入异步提交到服务器 function ajaxSubmit(){ //获取文件浏览控件中选择的文件路径 var filesname=document.form1.FileName.value; if (filesname=="") { alert("请先选择要导入的txt文件!"); document.form1.FileName.focus(); return false; } //创建XMLHttpRequest对象 var xmlhttp; try{ xmlhttp=new XMLHttpRequest(); }catch(e){ xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } //创建请求结果处理程序 xmlhttp.onreadystatechange=function(){ if (4==xmlhttp.readyState){ if (200==xmlhttp.status){ var dates=xmlhttp.responseText; //接收服务端返回的数据 var alldataarr; if (dates=="null") { alert('内容获取失败!'); } else { alert('已成功获取到txt文本文件中的内容!'); alldataarr = dates.split(","); document.form1.textarea1.value = alldataarr[0]; document.getElementById('num').innerHTML = alldataarr[1]; //清空上传域file document.getElementById('FileName').select(); document.selection.clear(); } }else{ alert("error"); } } } //打开连接,true表示异步提交 xmlhttp.open("post", "ajax_txt_save.asp", true); //当方法为post时需要如下设置http头 xmlhttp.setRequestHeader('Content-type','application/x-www-form-urlencoded'); //发送数据 xmlhttp.send("filesname="+escape(filesname)); } //--> </script> </head> <body> <div align="center"> <br><font color="#009900" size="3"><b>JS读取文本文件的内容并赋值给textarea控件</b></font><br><br> <form id="form1" name="form1" action="index.asp?Action=Write" method="post"> <input type="file" id="FileName" name="FileName" size="30"> <input type="button" name="button1" value="导入" onclick="ajaxSubmit();"><br><br> <textarea id="textarea1" name="textarea1" cols="46" rows="20"></textarea> <br><br> 已导入:<font color="#FF0000"><span id="num">0</span></font> 条记录<br><br><br> <input type="submit" name="Submit1" value="提交导入的内容到数据库"> </form> </div> </body> </html> 第二个文件ajax_txt_save.asp的代码: (此页是用ASP中的FSO来读取txt文本文件中的内容并输出,为ajax_txt.asp这个页面的AJAX异步获取提供数据) <% '//禁止缓存该页 让AJAX读取该页始终为最新而非过期缓存页 Response.Expires = 0 Response.Expiresabsolute = Now() - 1 Response.AddHeader "pragma","no-cache" Response.AddHeader "cache-control","private" Response.CacheControl = "no-cache" response.Charset="GB2312" '//数据返回的编码类型 显示中文数据必须 Dim objFSO Dim objText Dim ObjFile Dim strTextContent Dim objDrive '创建一个文件操作对象实例 Set objFSO = CreateObject("Scripting.FileSystemObject") '要打开的文件 ObjFile=trim(request("filesname")) '从文浏览控件中获取 IF objFSO.FileExists(ObjFile) then Set objText = objFSO.OpenTextFile(ObjFile,1) '循环读取数据 k=0 While not objText.AtEndOfStream '到文件的末尾 strTextContent = strTextContent & objText.ReadLine() & vbcrlf k=k+1 wend objText.Close response.write strTextContent & "," & k else strTextContent= "文件不存在" response.end() end if %> 以上代码经测试,100%能使用,且不受浏览器的安全级别限制,祝你好运!
官方文档:其实在几个月之前我就开始关注它,而且小程序发布那天是9号,那时候我正好考试,因为小程序是新事物,当时我真的有点忍不住,但是又要复习考试,所以很不爽,放假之后我就开始看微信小程序的官方文档 通俗易懂很不错。个人推荐想学习小程序的就按照这份官方文档按顺序看吧,其它比如掘金或者w3school也有,但是这毕竟是官方的,比较权威。语言是基于es6,注意这里使用的wxml,wxss,是是对应html和css的,wxml跟html语法上相似,wxss语法就是跟css一样。注意事项:首先开发小程序是需要一个APPID的这个在教程官方文档里面的官方文档里面会说到,但是这个微信小程序表面上是必须具有个体户工商营业执照或者企业营业执照才可以注册的,但是像我这种穷学生要是想学一下小程序怎么办呢?这时候你需要这个 我就是像这样申请一个小程序账号,得到一个APPID,注意不需要搞这个微信认证的,这个专空子申请的APPID只可以用来开发,是没办法拿来发布小程序的,认证也是认证不了的 在小程序里面的wx.reques函数在是用来向你的后端服务器发出请求的wx.request({   url: 'test.php', //仅为示例,并非真实的接口地址   data: {      x: '' ,      y: ''   },   header: {       'content-type': 'application/json'   },   success: function(res) {     console.log(res.data)   } })注意这个函数的url必须是https,这是除了看文档之外我花费时间最多的地方接上面讲https这个真的浪费我很多时间,我的后端是在我的腾讯云服务器使用nodejs语言搭建的server,因为小程序必须的request的url必须是https这就需要你的服务器具有ssl证书。如果你使用的腾讯云服务器的话,登录你的腾讯云服务器云主机,在上面的ssl证书管理那里你会看到这个: 你需要申请证书,按照指引走就行,申请之后就会看到截图那样的证书申请好在那里了,下载之后会发现主要有三部分:Apache, Nginx, IIS: 注意:证书的安装腾讯云的文档不是说的太明白,我当时就是搞得一脸懵逼。因为我的的服务端是用nodejs写的,所以需要安装Nginx服务器(涉及Nginx反向代理nodejs项目),Nginx的安装教程安装之后你就可以使用http://yourDomainName yourDomainName是你的域名(没有域名的话需要自己申请一个,在腾讯云上有得买,加上优惠券还是挺便宜的,但是之后几年的价格怎样不知道,想要免费的域名也可以,可以自行谷歌百度搜素一下就行) 来访问你的服务器的了(默认是80端口),然后安装Nginx证书,教程安装好ssl证书之后,你就可以适应https://yourDomainName 来访问你的服务器接着就是使用你的Nginx服务器来代理你作为微信小程序后端的nodejs项目了先说说使用Nginx反向代理nodejs项目是什么意思Nginx是安装在你服务器里面,他可以占据着一个端口,比如443,然后你运行你的nodejs项目,让它在3000端口跑,这时候你让Nginx代理,让别人访问你服务器443端口的时候,转而自动访问你nodejs所在3000端口,这就是使用Nginx反向代理nodejs项目 教程 核心部分如下(下图源自Nginx的nginx.conf配置文件):我的小程序使用: 下载本项目到本地或者你的服务器,进入server文件夹,输入npm install安装依赖,等待安装完成输入npm start启动后端脚本,app文件夹使用微信开发工具打开,把pages文件夹内index.js中的wx.request中的url改为你的服务器域名或者ip地址(真机测试必须是https访问,这就是上面我煞费口舌说https的原因),也可以本地调试的话应该可以改为:localhost:端口我做的这个小程序是一个翻译助手,非常简单,微信的api我只是使用wx.request和onShareAppMessage两个重要的官方api函数,至于逻辑那些只能自己写小程序部分在这个项目的app文件夹里面,可以下载到本地,使用微信开发工具打开这个名为app的文件夹就可以打开我的这个小程序啦这个小程序,部署在我的腾讯云服务器里面,在3000端口跑,用Nginx在443端口进行代理,在server文件夹内的server.js负责接收小程序发送过来的请求,并且使用translate.js里面的函数进行翻译,最后把结果反馈给小程序。translate.js里面使用百度翻译的api,主要使用http.request()函数,百度翻译的api很好用,很喜欢。核心代码module.exports = function(params, callback) {    if (typeof params === 'string') {      params = {        query: params      };    }    params = {      from: params.from || 'zh',      to: params.to || 'en',      query: params.query || ''    };    var data = querystring.stringify(params);      options = {        host: 'fanyi.baidu.com',        port: 80,        path: '/v2transapi',        method: 'POST',        headers: {          'Content-Type':'application/x-www-form-urlencoded',          'Content-Length': data.length        }      };    var req = http.request(options, function(res) {      var result = '';      res.setEncoding('utf8');      res.on('data', function(data) {        result  = data;      });      res.on('end', function() {      //console.log(result);       var obj = JSON.parse(result);       console.log(obj);       var str = obj.trans_result.data[0].dst;        callback(str);      });    });    req.on('error', function(err) {      console.log(err);      setTimeout(function() {        translation(query, callback);      }, 3000);    });    req.write(data);    req.end();  };params = {     from: params.from || 'zh', //原来是什么语言                    to: params.to || 'en', //要翻译为什么语言             query: params.query || ''  //要翻译的语句  };使用百度翻译的api,必须知道每种语言,百度翻译使用什么单词表示的: 解决语音问题:这是百度翻译女声语音的url,喜欢的朋友可以收藏,以后可能用得上 注意里面有两个参数,一个是lan意思是要读出出来的是什么语言,zh是中文,en是英文,很遗憾,这个url这可以播报中文和英文的语音,其他的小语种不可以这也是我的小程序只可以有中文和英文两种语言的语音的原因,第二个参数是text就是要播报的文本是什么效果图如下:foreverforever是可以让nodejs项目在后台运行的,只需要npm install forever一下就可以使用forever start server.js来运行你的expres项目,简单好用,当你想停止下来debug的时候就forever stop server.js一下就行

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值