整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Java实现图形验证码程序

Java实现图形验证码程序

力提升-生成图形验证码

<!--访问静态资源和访问控制器的结果都是一样的---一个是静态访问图片路径------另一个是动态获取图片流 -->

(一)动态获取图片两种方法:

1.静态HTML获取图片地址(通过JSP脚本动态更换图片路径地址)

2.动态控制器Servlet(Java的I/O流控制输出)

步骤一、

编写JSP静态获取图片地址页面:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%String path=request.getContextPath();String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <img alt="" src="/SecurityCode(安全码)/WebRoot/images/ea111ec9e85c9a06f7756b488bfaae60.jpg"> </body></html>

浏览器输出:

第二、通过Servlet控制器输出I/O流文件的形式

package com.wq.Servlet;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.ServletOutputStream;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet("/demo")public class DemoSrtvlet extends HttpServlet{ /** * */ private static final long serialVersionUID=1L; @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 输出文字流(获取文字) //PrintWriter outPrintWriter=resp.getWriter(); //获取响应流(图片,视频,文件等字节流) ServletOutputStream os=resp.getOutputStream(); //IO流获取文件,getRealPath(arg0)获取文件具体路径;;File(getServletContext().getRealPath(""),"ea111ec9e85c9a06f7756b488bfaae60.jpg"));//文件路径+文件名 InputStream iStream=new FileInputStream(new File(getServletContext().getRealPath("images"),"ea111ec9e85c9a06f7756b488bfaae60.jpg")); //输入流转输出流(JavaSE I/O的时候必须会写的代码) int index=-1; while((index=iStream.read())!=-1){ //index读取文件流直到等于-1 os.write(index); //同时写入到http中--os.write(index); } }}

浏览器输出:文件的形式

JSP获取src路径文件

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%String path=request.getContextPath();String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <img alt="" src="demo"><!--直接返回一个Servlet的图片流 --> <!--访问静态资源和访问控制器的结果都是一样的---一个是静态访问图片路径------另一个是动态获取图片流 --> </body></html>


浏览器输出;(效果一样)


实现步骤

一、实现原理


public class BufferedImage

extends Image

implements WritableRenderedImage, Transparency所述BufferedImage亚类描述了一种Image与图像数据的访问的缓冲器。 A BufferedImage由图像数据的ColorModel和Raster组成。 SampleModel中SampleModel中的Raster的数量和类型必须与ColorModel所要求的数字和类型相匹配,以表示其颜色和Alpha组件。 所有BufferedImage对象的左上角坐标为(0,0)。 Raster用于构建BufferedImage的任何BufferedImage必须具有minX=0和minY=0。

这个类依赖的数据获取和设置方法Raster ,并在颜色表征方法ColorModel 。

JavaWeb Jquery Ajax实现验证码局部刷新和校验


一:设置JSP页面


(1):给form表单中的 "登录" 按钮设置onsubmit验证点击后调用checkCode()返回结果为true页面跳转,为false页面不跳转。

(2):通过标签设置当点击"看不清?"时调用flushImage();进 行验局部证码刷新。

(3):点击注册按钮转发到"register.jsp"页面。


<body><form action="login.do" method="post" onsubmit="return checkCode()"> <h1>登录页面</h1> 用户名:<input type="text" name="username"> </br></br> 密码:<input type="password" name="password"> </br></br> 验证码:<input type="text" id="txt_code" name="code" size="5"> <img alt="验证码" id="code" src="image.do"> <a href="javascript:flushInamge();">看不清?</a> <span id="s_code"></span> </br></br> <input type="submit" value="登录"> <a href="register.jsp"><input type="button" value="注册"></a>


二:制作验证码


(1)制作java验证码并把产生的验证码字符串存储在

(2)request.getSession().setAttribute("code", str);存储在session中。

(3)利用ImageIO.write(image, "png", out);把图片返回给ajax的回调函数。


else if("/image.do".equals(path)) {//生成验证码 //避免浏览器缓存 response.addHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); //1.动态的创建一个图片 //第一个参数高度 第二个参数高度 第三个参数图片类型RGB BufferedImage image=new BufferedImage(100, 30, BufferedImage.TYPE_INT_RGB); //2.在图片上画字符 Graphics graphics=image.getGraphics(); graphics.setColor(Color.white);//改变图片背景色默认黑色 graphics.fillRect(0, 0, 100, 30);//画矩形 String str=""; Random rand=new Random(); Font font=new Font(null,Font.BOLD,20); for (int i=0; i < 5; i++) { int index=rand.nextInt(chars.length); Color color=new Color(rand.nextInt(255),rand.nextInt(255),rand.nextInt(255)); //生成一个随机颜色字符 graphics.setColor(color); graphics.setFont(font); graphics.drawString(chars[index]+"", 30+i*10, 20); str+=chars[index]; } for (int i=0; i < 4; i++) { Color color=new Color(rand.nextInt(255),rand.nextInt(255),rand.nextInt(255)); graphics.setColor(color); graphics.drawLine(rand.nextInt(100), rand.nextInt(30), rand.nextInt(100), rand.nextInt(30)); } //3.将图片上的文字保存到session中 request.getSession().setAttribute("code", str); //将图片利用response输出 OutputStream out=response.getOutputStream(); ImageIO.write(image, "png", out); }


三:创建jquery ajax请求通过Java进行校验


(1):flushImamge();通过选择器选择名字为"code"的对象,发送image.do请求调用Java程序对'"code"对象进行刷新。


(2):checkCode();通过选择器选择id为"txt_code"的对象获取参数,然后发送checeked.do请求调用Java程序进行校验,如果验证码正确活动的data为 '验证码正确' 返回true,为空或者是 "验证码错误" 就是false。


function flushInamge(){ $("#code").attr("src","image.do?r"+Math.random());//实现局部刷新 }function checkCode(){ var check=true; //获取用户输入的验证码信息 var code=$("#txt_code").val().trim(); if(code==''){ $("#s_code").html("验证码不能为空"); return false; } //验证码不为空就进行校验 $.ajax({ url:"checked.do", type:"post", async:false, data:{"code":code}, dataType:"text", success:function(data){//data是服务器返回的消息 $("#s_code").html(data); if(data=='验证码错误'){check=false;} } }) return check;}


四:用Java对验证码进行校验


通过request.getParameter("code");获取ajax请求传递的参数,然后通过request.getSession().getAttribute("code");获取session作用域中设置的参数,

最后通过if(code.equalsIgnoreCase(scode))无视大小写对输入的验证码和生成的验证码进行匹配相同out.print("验证码正确"),不相同out.print("验证码错误");将参数返回给ajax。


else if("/checked.do".equals(path)) { //接收请求code参数 String code=request.getParameter("code"); System.out.println("code"+code); //获取session中验证码信息 String scode=(String) request.getSession().getAttribute("code"); System.out.println("scode"+scode); //比对输出 response.setContentType("text/html;charset=UTF-8"); PrintWriter out=response.getWriter(); if(code.equalsIgnoreCase(scode)) { System.out.println(true); out.print("验证码正确"); }else { out.print("验证码错误"); System.out.println(false); } out.close();


效果



在前面

今年国庆假期终于可以憋在家里了不用出门了,不用出去看后脑了,真的是一种享受。这么好的光阴怎么浪费,睡觉、吃饭、打豆豆这怎么可能(耍多了也烦),完全不符合我们程序员的作风,赶紧起来把文章写完。

这篇文章比较基础,在国庆期间的业余时间写的,这几天又完善了下,力求把更多的前端所涉及到的关于文件上传的各种场景和应用都涵盖了,若有疏漏和问题还请留言斧正和补充。

自测读不读

以下是本文所涉及到的知识点,break or continue ?

  • 文件上传原理
  • 最原始的文件上传
  • 使用 koa2 作为服务端写一个文件上传接口
  • 单文件上传和上传进度
  • 多文件上传和上传进度
  • 拖拽上传
  • 剪贴板上传
  • 大文件上传之分片上传
  • 大文件上传之断点续传
  • node 端文件上传

原理概述

原理很简单,就是根据 http 协议的规范和定义,完成请求消息体的封装和消息体的解析,然后将二进制内容保存到文件。

我们都知道如果要上传一个文件,需要把 form 标签的enctype设置为multipart/form-data,同时method必须为post方法。

那么multipart/form-data表示什么呢?

multipart互联网上的混合资源,就是资源由多种元素组成,form-data表示可以使用HTML Forms 和 POST 方法上传文件,具体的定义可以参考RFC 7578。

multipart/form-data 结构

看下 http 请求的消息体



  • 请求头:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDCntfiXcSkPhS4PN 表示本次请求要上传文件,其中boundary表示分隔符,如果要上传多个表单项,就要使用boundary分割,每个表单项由———XXX开始,以———XXX结尾。

  • 消息体- Form Data 部分

每一个表单项又由Content-Type和Content-Disposition组成。

Content-Disposition: form-data 为固定值,表示一个表单元素,name 表示表单元素的 名称,回车换行后面就是name的值,如果是上传文件就是文件的二进制内容。

Content-Type:表示当前的内容的 MIME 类型,是图片还是文本还是二进制数据。

解析

客户端发送请求到服务器后,服务器会收到请求的消息体,然后对消息体进行解析,解析出哪是普通表单哪些是附件。

可能大家马上能想到通过正则或者字符串处理分割出内容,不过这样是行不通的,二进制buffer转化为string,对字符串进行截取后,其索引和字符串是不一致的,所以结果就不会正确,除非上传的就是字符串。

不过一般情况下不需要自行解析,目前已经有很成熟的三方库可以使用。

至于如何解析,这个也会占用很大篇幅,后面的文章在详细说。

最原始的文件上传

使用 form 表单上传文件

在 ie时代,如果实现一个无刷新的文件上传那可是费老劲了,大部分都是用 iframe 来实现局部刷新或者使用 flash 插件来搞定,在那个时代 ie 就是最好用的浏览器(别无选择)。

DEMO



这种方式上传文件,不需要 js ,而且没有兼容问题,所有浏览器都支持,就是体验很差,导致页面刷新,页面其他数据丢失。

HTML

 <form method="post" action="http://localhost:8100" enctype="multipart/form-data">

        选择文件:
            <input type="file" name="f1"/> input 必须设置 name 属性,否则数据无法发送<br/>
<br/>
            标题:<input type="text" name="title"/><br/><br/><br/>

        <button type="submit" id="btn-0">上 传</button>

</form>

复制代码

文件上传接口

服务端文件的保存基于现有的库koa-body结合 koa2实现服务端文件的保存和数据的返回。

在项目开发中,文件上传本身和业务无关,代码基本上都可通用。

在这里我们使用koa-body库来实现解析和文件的保存。

koa-body 会自动保存文件到系统临时目录下,也可以指定保存的文件路径。



然后在后续中间件内得到已保存的文件的信息,再做二次处理。

  • ctx.request.files.f1 得到文件信息,f1为input file 标签的 name
  • 获得文件的扩展名,重命名文件

NODE

/**
 * 服务入口
 */
var http=require('http');
var koaStatic=require('koa-static');
var path=require('path');
var koaBody=require('koa-body');//文件保存库
var fs=require('fs');
var Koa=require('koa2');

var app=new Koa();
var port=process.env.PORT || '8100';

var uploadHost=`http://localhost:${port}/uploads/`;

app.use(koaBody({
    formidable: {
        //设置文件的默认保存目录,不设置则保存在系统临时目录下  os
        uploadDir: path.resolve(__dirname, '../static/uploads')
    },
    multipart: true // 开启文件上传,默认是关闭
}));

//开启静态文件访问
app.use(koaStatic(
    path.resolve(__dirname, '../static') 
));

//文件二次处理,修改名称
app.use((ctx)=> {
    var file=ctx.request.files.f1;//得道文件对象
    var path=file.path;
    var fname=file.name;//原文件名称
    var nextPath=path+fname;
    if(file.size>0 && path){
        //得到扩展名
        var extArr=fname.split('.');
        var ext=extArr[extArr.length-1];
        var nextPath=path+'.'+ext;
        //重命名文件
        fs.renameSync(path, nextPath);
    }
    //以 json 形式输出上传文件地址
    ctx.body=`{
        "fileUrl":"${uploadHost}${nextPath.slice(nextPath.lastIndexOf('/')+1)}"
    }`;
});

/**
 * http server
 */
var server=http.createServer(app.callback());
server.listen(port);
console.log('demo1 server start ......   ');
复制代码

CODE

https://github.com/Bigerfe/fe-learn-code/

、@RequestMapping

RequestMapping是一个用来处理请求地址映射的注解(将请求映射到对应的控制器方法中),可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

RequestMapping请求路径映射,如果标注在某个controller的类级别上,则表明访问此类路径下的方法都要加上其配置的路径;最常用是标注在方法上,表明哪个具体的方法来接受处理某次请求。

@Controller
@RequestMapping(value="/book")
public class BookController {
 @RequestMapping(value="/title")
 public String getTitle(){
 return "title";
 } 
 
 @RequestMapping(value="/content")
 public String getContent(){
 return "content";
 } 
} 

由于BookController类加了value="/book"的@RequestMapping的注解,所以相关路径都要加上"/book",即请求的url分别为:

(1)http://localhost:8080/book/title

(2)http://localhost:8080/book/content

"@RequestMapping"的value值前后是否有“/”对请求的路径没有影响,即value="book" 、"/book"、"/book/"其效果是一样的。

RequestMapping的属性

value:指定请求的实际url

(1)普通的具体值。如前面的value="/book"。

(2)含某变量的一类值。

@RequestMapping(value="/get/{bookId}")
public String getBookById(@PathVariable String bookId,Model model){
 model.addAttribute("bookId", bookId);
 return "book";
} 

路径中的bookId可以当变量,@PathVariable注解即提取路径中的变量值。

(3)ant风格

@RequestMapping(value="/get/id?"):可匹配“/get/id1”或“/get/ida”,但不匹配“/get/id”或“/get/idaa”;

@RequestMapping(value="/get/id*"):可匹配“/get/idabc”或“/get/id”,但不匹配“/get/idabc/abc”;

@RequestMapping(value="/get/id/*"):可匹配“/get/id/abc”,但不匹配“/get/idabc”;

@RequestMapping(value="/get/id/**/{id}"):可匹配“/get/id/abc/abc/123”或“/get/id/123”,也就是Ant风格和URI模板变量风格可混用。

(4)含正则表达式的一类值

@RequestMapping(value="/get/{idPre:\d+}-{idNum:\d+}"):可以匹配“/get/123-1”,但不能匹配“/get/abc-1”,这样可以设计更加严格的规则。

可以通过@PathVariable 注解提取路径中的变量(idPre,idNum)

(5)或关系

@RequestMapping(value={"/get","/fetch"} )即 /get或/fetch都会映射到该方法上。

method:指定请求的method类型, GET、POST、PUT、DELETE等;

@RequestMapping(value="/get/{bookid}",method={RequestMethod.GET,RequestMethod.POST})

params:指定request中必须包含某些参数值是,才让该方法处理。

@RequestMapping(params="action=del"),请求参数包含“action=del”,如:http://localhost:8080/book?action=del

2、@RequestParam绑定单个请求参数值

@RequestParam用于将请求参数区数据映射到功能处理方法的参数上。

public String requestparam1(@RequestParam String username)

请求中包含username参数(如/requestparam1?username=zhang),则自动传入。

@RequestParam有以下三个参数:

value:参数名字,即入参的请求参数名字,如username表示请求的参数区中的名字为username的参数的值将传入;

required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将抛出异常;

defaultValue:默认值,表示如果请求中没有同名参数时的默认值,设置该参数时,自动将required设为false。

public String requestparam4(@RequestParam(value="username",required=false) String username)

表示请求中可以没有名字为username的参数,如果没有默认为null,此处需要注意如下几点:

原子类型:必须有值,否则抛出异常,如果允许空值请使用包装类代替。

Boolean包装类型:默认Boolean.FALSE,其他引用类型默认为null。

3、@PathVariable绑定URI模板变量值

@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。

@RequestMapping(value="/users/{userId}/topics/{topicId}")
public String test(
 @PathVariable(value="userId") int userId, 
 @PathVariable(value="topicId") int topicId) 

如请求的URL为“控制器URL/users/123/topics/456”,则自动将URL中模板变量{userId}和{topicId}绑定到通过@PathVariable注解的同名参数上,即入参后userId=123、topicId=456。

4、@ModelAttribute

ModelAttribute可以应用在方法参数上或方法上,他的作用主要是当注解在方法参数上时会将注解的参数对象添加到Model中;当注解在请求处理方法Action上时会将该方法变成一个非请求处理的方法,但其它Action被调用时会首先调用该方法。

注释一个方法

被@ModelAttribute注释的方法表示这个方法的目的是增加一个或多个模型(model)属性。这个方法和被@RequestMapping注释的方法一样也支持@RequestParam参数,但是它不能直接被请求映射。实际上,控制器中的@ModelAttribute方法是在同一控制器中的@RequestMapping方法被调用之前调用的。

被@ModelAttribute注释的方法用于填充model属性,例如,为下拉菜单填充内容,或检索一个command对象(如,Account),用它来表示一个HTML表单中的数据。

一个控制器可以有任意数量的@ModelAttribute方法。所有这些方法都在@RequestMapping方法被调用之前调用。

有两种类型的@ModelAttribute方法。一种是:只加入一个属性,用方法的返回类型隐含表示。另一种是:方法接受一个Model类型的参数,这个model可以加入任意多个model属性。

注解的使用场景

当@ModelAttribute注解用于方法时,与其处于同一个处理类的所有请求方法执行前都会执行一次此方法,这可能并不是我们想要的,因此,我们使用更多的是将其应用在请求方法的参数上,而它的一部分功能与@RequestParam注解是一致的,只不过@RequestParam用于绑定单个参数值,而@ModelAttribute注解可以绑定所有名称匹配的,此外它自动将绑定后的数据添加到模型中,无形中也给我们提供了便利,这也可能是它命名为ModelAttribute的原因。

5、SessionAttributes

在默认情况下,ModelMap中的属性作用域是request级别,也就是说,当本次请求结束后,ModelMap 中的属性将销毁。如果希望在多个请求中共享ModelMap中的属性,必须将其属性转存到session 中,这样 ModelMap 的属性才可以被跨请求访问。

Spring 允许我们有选择地指定 ModelMap 中的哪些属性需要转存到 session 中,以便下一个请求属对应的 ModelMap 的属性列表中还能访问到这些属性。这一功能是通过类定义处标注 @SessionAttributes 注解来实现的。

package demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import demo.model.User;
@Controller
@RequestMapping(value="/demo1")
//(1)将ModelMap中属性名为currUser的属性放到Session属性列表中,以便这个属性可以跨请求访问
@SessionAttributes("currUser")
public class Demo1Controller {
 
 @RequestMapping(value="/getUser")
 public String getUser(ModelMap model){
 User user=new User();
 user.setUser_name("zhangsan");
 user.setUser_age(25);
 user.setUser_email("zhangsan@sina.com");
 //(2)向ModelMap中添加一个属性
 model.addAttribute("currUser",user);
 return "/demo/user";
 }
 
 @RequestMapping(value="/getUser1")
 public String getUser1(ModelMap model){
 User user=(User)model.get("currUser");
 System.out.println(user.getUser_name());
 System.out.println(user.getUser_age());
 System.out.println(user.getUser_email());
 return "demo/user1";
 }
}

我们在(2)处添加了一个 ModelMap 属性,其属性名为 currUser,而(1)处通过 @SessionAttributes 注解将 ModelMap 中名为 currUser 的属性放置到 Session 中,所以我们不但可以在 getUser() 请求所对应的 JSP 视图页面中通过 request.getAttribute(“currUser”) 和 session.getAttribute(“currUser”) 获取 user 对象,还可以在下一个请求(getUser1())所对应的 JSP 视图页面中通过 session.getAttribute(“currUser”) 或 session.getAttribute(“currUser”)访问到这个属性。

这里我们仅将一个 ModelMap 的属性放入 Session 中,其实 @SessionAttributes 允许指定多个属性。你可以通过字符串数组的方式指定多个属性,如 @SessionAttributes({“attr1”,"attr2”})。此外,@SessionAttributes 还可以通过属性类型指定要 session 化的 ModelMap 属性,如 @SessionAttributes(types=User.class),当然也可以指定多个类,如 @SessionAttributes(types={User.class,Dept.class}),还可以联合使用属性名和属性类型指定:@SessionAttributes(types={User.class,Dept.class},value={“attr1”,"attr2”})。

user.jsp页面:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ page import="demo.model.User" %>
<%
String path=request.getContextPath();
String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
 <head>
 <base href="<%=basePath%>">
 <title>My JSP 'index.jsp' starting page</title>
 </head>
 <body><br>
 <%User user=(User)session.getAttribute("currUser");%>
 用户名:<%=user.getUser_name() %><br/>
 年龄:<%=user.getUser_age() %><br/>
 邮箱:<%=user.getUser_email() %><br/><br/>
 <a href="<%=path %>/demo1/getUser1">跳转</a>
 </body>
</html>

6、@Responsebody与@RequestBody

@Responsebody表示该方法的返回结果直接写入HTTP response body中。一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@Responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@Responsebody后,会直接返回json数据。

@RequestBody将HTTP请求正文插入方法中,使用适合的HttpMessageConverter将请求体写入某个对象。

@RequestBody 将HTTP请求正文转换为适合的HttpMessageConverter对象。

@ResponseBody 将内容或对象作为 HTTP 响应正文返回,并调用适合HttpMessageConverter的Adapter转换对象,写入输出流。

@RequestBody

作用:

i) 该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上;

ii) 再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上。

使用时机:

A) GET、POST方式提时, 根据request header Content-Type的值来判断:

application/x-www-form-urlencoded, 可选(即非必须,因为这种情况的数据@RequestParam, @ModelAttribute也可以处理,当然@RequestBody也能处理);

multipart/form-data, 不能处理(即使用@RequestBody不能处理这种格式的数据);

其他格式, 必须(其他格式包括application/json, application/xml等。这些格式的数据,必须使用@RequestBody来处理);

B) PUT方式提交时, 根据request header Content-Type的值来判断:

application/x-www-form-urlencoded, 必须;

multipart/form-data, 不能处理;

其他格式, 必须;

说明:request的body部分的数据编码格式由header部分的Content-Type指定;

@ResponseBody

作用:

该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。