springboot---接收前端请求
对于Url类请求(注意是纯url请求),客户端是不能通过body写出数据的,服务端拿到的body是空的。因此一般情况下,我们提供的HttpClient被Url类的请求getOutputStream()的时候会抛一个异常,而HttpServer接受到Url类的请求后getInputStream()的时候也会抛一个异常,这都是为了告诉调用者这是不合理的。因此综上所述,如果为get请求时,后台接收参数的
1. SpringBoot获取http请求参数的方法
原文:https://www.cnblogs.com/wjw1014/p/11611312.html
1.1. 七种Java后台获取前端传来参数的方法,稍微罗列一下
-
直接把表单里面的参数写进 Controller 相应方法的形参中去,(貌似这个获取参数的方法适合get提交,而不适合post提交,未亲自验证,我觉得应该各种请求都可以)
/** * 1.直接把表单的参数写在Controller相应的方法的形参中 * @param username * @param password * @return */ @RequestMapping("/addUser") public String addUser(String username,String password) { System.out.println("username is:"+username); System.out.println("password is:"+password); return "demo/index"; }
url形式:http://localhost/jayvee/demo/addUser?username=wjw&password=123456 提交的参数名称必须和Controller方法中定义的参数名称一致。
-
通过 @PathVariable 注解获取传进的参数
/** * 4、通过@PathVariable获取路径中的参数 * @param username * @param password * @return */ @RequestMapping(value="/addUser/{username}/{password}",method=RequestMethod.GET) public String addUser(@PathVariable String username,@PathVariable String password) { System.out.println("username is:"+username); System.out.println("password is:"+password); return "demo/index"; }
url形式:http://localhost/jayvee/demo/addUser4/wjw/123465时,则自动将URL中模板变量{username}和{password}绑定到通过@PathVariable注解的同名参数上,即入参后username=wjw、password=123465。
-
使用 HttpServletRequest 获取参数,适用于post和get方法。
/** * 2、通过HttpServletRequest接收 * @param request * @return */ @RequestMapping("/addUser") public String addUser(HttpServletRequest request) { String username=request.getParameter("username"); String password=request.getParameter("password"); System.out.println("username is:"+username); System.out.println("password is:"+password); return "demo/index"; }
-
建立一个bean来获取参数,适用于 post 和 get。
//首先创建一个和表单对应的bean package demo.model; public class UserModel { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
//使用创建的 bean 来封装接收到前端传来的参数 /** * 3、通过一个bean来接收 * @param user * @return */ @RequestMapping("/addUser") public String addUser(UserModel user) { System.out.println("username is:"+user.getUsername()); System.out.println("password is:"+user.getPassword()); return "demo/index"; }
-
使用@ModelAttribute注解获取POST请求的FORM表单数据
jsp表单<form action ="<%=request.getContextPath()%>/demo/addUser5" method="post"> 用户名: <input type="text" name="username"/><br/> 密 码: <input type="password" name="password"/><br/> <input type="submit" value="提交"/> <input type="reset" value="重置"/> </form>
Java Controller
/** * 5、使用@ModelAttribute注解获取POST请求的FORM表单数据 * @param user * @return */ @RequestMapping(value="/addUser",method=RequestMethod.POST) public String addUser(@ModelAttribute("user") UserModel user) { System.out.println("username is:"+user.getUsername()); System.out.println("password is:"+user.getPassword()); return "demo/index"; }
-
用注解@RequestParam绑定请求参数到方法入参
当请求参数username不存在时会有异常发生,可以通过设置属性required=false解决,例如: @RequestParam(value=“username”, required=false)/** * 6、用注解@RequestParam绑定请求参数到方法入参 * @param username * @param password * @return */ @RequestMapping(value="/addUser",method=RequestMethod.GET) public String addUser(@RequestParam("username") String username,@RequestParam("password") String password) { System.out.println("username is:"+username); System.out.println("password is:"+password); return "demo/index"; }
-
用注解@RequestBody绑定请求参数到方法入参 用于POST请求
/** * 7、用注解@Requestbody绑定请求参数到方法入参 * @param username * @param password * @return */ @RequestMapping(value="/addUser",method=RequestMethod.POST) public String addUser(@RequestBody UserDTO userDTO) { System.out.println("username is:"+userDTO.getUserName()); System.out.println("password is:"+userDTO.getPassWord()); return "demo/index"; } //UserDTO 这个类为一个实体类,里面定义的属性与URL传过来的属性名一一对应。
1.2. @RequestParam与@RequestBody
-
问题描述
由于项目是前后端分离,因此后台使用的是spring boot,做成微服务,只暴露接口。接口设计风格为restful的风格,在get请求下,后台接收参数的注解为RequestBody时会报错;在 post请求下,后台接收参数的注解为RequestParam时也会报错。 -
问题原因
由于spring的RequestParam注解接收的参数是来自于requestHeader中,即请求头,也就是在url中,格式为xxx?username=123&password=456,而RequestBody注解接收的参数则是来自于requestBody中,即请求体中。 -
解决方法
因此综上所述,如果为get请求时,后台接收参数的注解应该为RequestParam,如果为post请求时,则后台接收参数的注解就是为RequestBody。附上两个例子,截图如下: -
get请求
-
post请求
-
other
另外,还有一种应用场景,接口规范为resultful风格时,举个例子:如果要获取某个id下此条问题答案的查询次数的话,则后台就需要动态获取参数, 其注解为@PathVariable,并且requestMapping中的value应为value=“/{id}/queryNum”,截图如下:
1.3. @PathVaribale/@RequestParam/@GetMapping
各注解基本含义:
@PathVaribale 获取url中的数据
@RequestParam 获取请求参数的值
@GetMapping 组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写
@PathVaribale 获取url中的数据
看一个例子,如果我们需要获取Url=localhost:8080/hello/id中的id值,实现代码如下:
@RestController
public class HelloController {
@RequestMapping(value="/hello/{id}",method= RequestMethod.GET)
public String sayHello(@PathVariable("id") Integer id){
return "id:"+id;
}
}
同样,如果我们需要在url有多个参数需要获取,则如下代码所示来做就可以了。
@RestController
public class HelloController {
@RequestMapping(value="/hello/{id}/{name}",method= RequestMethod.GET)
public String sayHello(@PathVariable("id") Integer id,@PathVariable("name") String name){
return "id:"+id+" name:"+name;
}
}
以上,通过@PathVariable注解来获取URL中的参数时的前提条件是我们知道url的格式时怎么样的。
只有知道url的格式,我们才能在指定的方法上通过相同的格式获取相应位置的参数值。
但是,一般情况下,url的格式为:localhost:8080/hello?id=98,这种情况下该如何来获取其id值呢,这就需要借助于@RequestParam来完成了
@RequestParam 获取请求参数的值
直接看一个例子,如下
@RestController
public class HelloController {
@RequestMapping(value="/hello",method= RequestMethod.GET)
public String sayHello(@RequestParam("id") Integer id){
return "id:"+id;
}
}
在浏览器中输入地址:localhost:8080/hello?id=1000,可以看到如下的结果:
当我们在浏览器中输入地址:localhost:8080/hello?id ,即不输入id的具体值,此时返回的结果为null。具体测试结果如下:
但是,当我们在浏览器中输入地址:localhost:8080/hello ,即不输入id参数,则会报如下错误:
@RequestParam注解给我们提供了这种解决方案,即允许用户不输入id时,使用默认值,具体代码如下:
@RestController
public class HelloController {
@RequestMapping(value="/hello",method= RequestMethod.GET)
//required=false 表示url中可以不穿入id参数,此时就使用默认参数
public String sayHello(@RequestParam(value="id",required = false,defaultValue = "1") Integer id){
return "id:"+id;
}
}
测试结果如下;
如果在url中有多个参数,即类似于localhost:8080/hello?id=98&&name=wojiushimogui这样的url,同样可以这样来处理。具体代码如下:
/**
* Created by wuranghao on 2017/4/7.
*/
@RestController
public class HelloController {
@RequestMapping(value="/hello",method= RequestMethod.GET)
public String sayHello(@RequestParam("id") Integer id,@RequestParam("name") String name){
return "id:"+id+ " name:"+name;
}
}
@GetMapping 组合注解
@GetMapping是一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。该注解将HTTP Get 映射到 特定的处理方法上。
即可以使用@GetMapping(value = “/hello”)来代替@RequestMapping(value=”/hello”,method= RequestMethod.GET)。即可以让我们精简代码。
例子
@RestController
public class HelloController {
//@RequestMapping(value="/hello",method= RequestMethod.GET)
@GetMapping(value = "/hello")
//required=false 表示url中可以不穿入id参数,此时就使用默认参数
public String sayHello(@RequestParam(value="id",required = false,defaultValue = "1") Integer id){
return "id:"+id;
}
}
2. url的组成
以上为总结的七种后台springbooot获取前端传进参数的方法。上述为注解的单一用法,下面再拓展一下url的基本组成,先看以下例子,可以让你对url有更深的认识
-
参数由请求行携带 ,请求行!!!
GET:http://localhost:8080/demo/123?name=suki_rong//对应的java代码 //突然发现这些请求的参数是放在的url的请求行里, //原来请求行、请求头、请求体,都可以携带信息,并且后台获取时都有不同的方式。 //url的请求行构成也是很复杂的,平时postman中的Params添加的kv对其实是放在的请求行中!! @GetMapping("/demo/{id}") public void demo(@PathVariable(name = "id") String id, @RequestParam(name = "name") String name) { System.out.println("id="+id); System.out.println("name="+name); }
输出结果:
id=123
name=suki_rong上述例子对应的是请求行
-
参数由请求体携带,请求体!!!
url无法单独标识,由postman来构建
//对应的java代码 @PostMapping(path = "/demo1") public void demo1(@RequestBody Person person) { System.out.println(person.toString()); } //还可以是下面这种 @PostMapping(path = "/demo1") public void demo1(@RequestBody Map <String,String> person) { System.out.println(person.toString()); }
输出结果:
name:suki_rong;age=18;hobby:programing请求体可以有很多种类型,测试可以知道kv格式和json格式传输到后端是相同的格式
-
参数由请求头携带,请求头!!
请求头中也有很多重要的信息,参数似乎也可以放在请求头中,但是一般不这么放而已
@RequestHeader
@CookieValue@GetMapping("/demo3") public void demo3(@RequestHeader(name = "myHeader") String myHeader, @CookieValue(name = "myCookie") String myCookie) { System.out.println("myHeader=" + myHeader); System.out.println("myCookie=" + myCookie); } //也可以这样获取 @GetMapping("/demo3") public void demo3(HttpServletRequest request) { System.out.println(request.getHeader("myHeader")); for (Cookie cookie : request.getCookies()) { if ("myCookie".equals(cookie.getName())) { System.out.println(cookie.getValue()); } } }
3. http详解
-
http的8种请求方法
HTTP/1.1协议中共定义了八种方法(有时也叫“动作”),来表明Request-URL指定的资源不同的操作方式
HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。
HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法
1、OPTIONS
返回服务器针对特定资源所支持的HTTP请求方法,也可以利用向web服务器发送‘*’的请求来测试服务器的功能性
2、HEAD
向服务器索与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以再不必传输整个响应内容的情况下,就可以获取包含在响应小消息头中的元信息。
3、GET
向特定的资源发出请求。注意:GET方法不应当被用于产生“副作用”的操作中,例如在Web Application中,其中一个原因是GET可能会被网络蜘蛛等随意访问。Loadrunner中对应get请求函数:web_link和web_url
4、POST
向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 Loadrunner中对应POST请求函数:web_submit_data,web_submit_form
5、PUT
向指定资源位置上传其最新内容
6、DELETE
请求服务器删除Request-URL所标识的资源
7、TRACE
回显服务器收到的请求,主要用于测试或诊断
8、CONNECT
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
注意:
1)方法名称是区分大小写的,当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Mothod Not Allowed);当服务器不认识或者不支持对应的请求方法时,应返回状态码501(Not Implemented)。
2)HTTP服务器至少应该实现GET和HEAD/POST方法,其他方法都是可选的,此外除上述方法,特定的HTTP服务器支持扩展自定义的方法。 -
返回的状态码
-
一些常见的状态码为:
200 - 服务器成功返回网页
404 - 请求的网页不存在
503 - 服务不可用
详细分解:1xx(临时响应)
表示临时响应并需要请求者继续执行操作的状态代码。代码 说明
100 (继续) 请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。
101 (切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。2xx (成功)
表示成功处理了请求的状态代码。代码 说明
200 (成功) 服务器已成功处理了请求。通常,这表示服务器提供了请求的网页。
201 (已创建) 请求成功并且服务器创建了新的资源。
202 (已接受) 服务器已接受请求,但尚未处理。
203 (非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。
204 (无内容) 服务器成功处理了请求,但没有返回任何内容。
205 (重置内容) 服务器成功处理了请求,但没有返回任何内容。
206 (部分内容) 服务器成功处理了部分 GET 请求。3xx (重定向)
表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。代码 说明
300 (多种选择) 针对请求,服务器可执行多种操作。服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。
301 (永久移动) 请求的网页已永久移动到新位置。服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
303 (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
304 (未修改) 自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
305 (使用代理) 请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理。
307 (临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。4xx(请求错误)
这些状态代码表示请求可能出错,妨碍了服务器的处理。代码 说明
400 (错误请求) 服务器不理解请求的语法。
401 (未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
403 (禁止) 服务器拒绝请求。
404 (未找到) 服务器找不到请求的网页。
405 (方法禁用) 禁用请求中指定的方法。
406 (不接受) 无法使用请求的内容特性响应请求的网页。
407 (需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。
408 (请求超时) 服务器等候请求时发生超时。
409 (冲突) 服务器在完成请求时发生冲突。服务器必须在响应中包含有关冲突的信息。
410 (已删除) 如果请求的资源已永久删除,服务器就会返回此响应。
411 (需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。
412 (未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。
413 (请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。
414 (请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。
415 (不支持的媒体类型) 请求的格式不受请求页面的支持。
416 (请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。
417 (未满足期望值) 服务器未满足"期望"请求标头字段的要求。5xx(服务器错误)
这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。代码 说明
500 (服务器内部错误) 服务器遇到错误,无法完成请求。
501 (尚未实施) 服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。
502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。
503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。
505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。HttpWatch状态码Result is
200 - 服务器成功返回网页,客户端请求已成功。
302 - 对象临时移动。服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
304 - 属于重定向。自上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
401 - 未授权。请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
404 - 未找到。服务器找不到请求的网页。
2xx - 成功。表示服务器成功地接受了客户端请求。
3xx - 重定向。表示要完成请求,需要进一步操作。客户端浏览器必须采取更多操作来实现请求。例如,浏览器可能不得不请求服务器上的不同的页面,或通过代理服务器重复该请求。
4xx - 请求错误。这些状态代码表示请求可能出错,妨碍了服务器的处理。
5xx - 服务器错误。表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。
4. 参数写在body中和写在url中到底有什么区别?
要解释这个问题,必须先搞清楚Http协议,根据Http的请求方法对应的数据传输能力把Http请求分为Url类请求和Body类请求:
Url类请求包括但不限于GET、HEAD、OPTIONS、TRACE 等请求方法。
Body类请求包括但不限于POST、PUSH、PATCH、DELETE 等请求方法。
在语义上来说,例如GET请求是查询或者获取服务端的资源的,POST请求是向服务器push数据或者创建资源的。其它同类请求方法多少都有点相同的含义,不过它们谁有谁该做的事,例如支持缓存,探测响应头和断点续传等。
但是!基本所有的请求方式是通用的,协议都是tcp协议,至于为什么分为url请求和body请求,完全是为了丰富数据的传输,当你想传输文件时只能使用body的方式
4.1. Url类请求释义
对于Url类请求(注意是纯url请求),客户端是不能通过body写出数据的,服务端拿到的body是空的。因此一般情况下,我们提供的HttpClient被Url类的请求getOutputStream()的时候会抛一个异常,而HttpServer接受到Url类的请求后getInputStream()的时候也会抛一个异常,这都是为了告诉调用者这是不合理的。
这里的HttpClient和HttpServer不是特指某框架或者浏览器,而是个笼统的称呼。
因此Url类的请求想要提交参数,就把参数跟在url后边了,但是url一般是指向资源的,因此url后面跟的参数更多的是一种条件和要求
做了一张url的图,辅助一下:
看完图应该知道了,其实我们称为参数的这一段整体上叫做Query,把它们拆开后组成键值对的形式又叫做Parameters,大多数情况下一个Key对应一个Value,当然是允许一个Key对应多个Values的。
4.2. Body类请求释义
Body类请求在语义和能力上来说应该是兼容Url类请求的,但是根据Http协议它们谁有谁该做的事。例如RESTful风格就是根据Http协议来提出的。
对于Body类请求,客户端可以通过body写出数据,服务端可以通过body接受数据。因此Body类请求能上传到服务端的数据比Url类请求的大的多。
RFC2616没有规定url的最大长度,但是规定当服务器不能处理太长的url时就返回414状态码(Request-URI Too Long),因此几乎可以说URL不能直接传输Binary数据,除非ToString,例如Base64。
Body类请求最大的优点就是可以自定义丰富的数据传输类型:
- Content-Type是application/x-www-form-urlencoded时,客户端可以通过body发送键值对数据,例如key1=value1&key2=value2,这种请求其实等于url请求。
- Content-Type是multipart/form-data时,客户端可以通过body发送一个表单。这种请求其实也等于url请求。
- Content-Type是application/javascript时,可以通过body传输js字符串
- Content-Type是text/plain时,可以通过body传输字符串
- Content-Type是application/json时,客户端可以通过body发送json字符串
- Content-Type是application/xml时可以发送xml字符串。
- Content-Type是text/html时可以发送xml字符串。
- Content-Type是application/octet-stream时,客户端可以通过body发送Binary数据,例如文件或者任何能转成流的数据。
4.3. 参数接收实验
4.3.1. 传参是宽泛的而非严格的
@RequestMapping("/no")
public String no(HttpServletRequest request, String username, String password) {
System.out.println("username is:"+username);
System.out.println("password is:"+password);
System.out.println(request.getTrailerFields());
return res;
}
// 后端接收打印
username is:likewei,2222
password is:1231,1231sdds,222222222
{}
结论:
- 对于后端来说,param参数和body中的field参数是相同的,后端可以使用相同的方式进行接收。
- 对于http请求来说,请求端传参和响应端响应是非常宽泛的,你可以多传或者少传,我也可以多返或者少返,双方按需自取。所以生产上往往加注解进行校验(@RequestParam),方便做处理。
- request用于获取ip等信息,请求参数不需要使用request获取。
- 前端传参时可以重叠,后端获取时会爹加成字符串!
4.3.2. param和body完全兼容
@RequestMapping("/requestParam")
public String requestParam(@RequestParam("username") String username, @RequestParam("password") String password1, @RequestBody String password) {
System.out.println("username is:"+username);
System.out.println("password1 is:"+password1);
System.out.println("password is:"+password);
return res;
}
// 后端接收打印
username is:likewei,2222
username is:222222222
password is:{"password",1231}
总结:
- param和body完全兼容,无论任何请求,二者都是不冲突的,一个是在url中传输参数,一个是在请求体中传输参数,二者本来就是http协议的一部分,任何一个http都具有这些功能。
4.3.3. map、json、bean是相同的
/**
* 只会校验格式,不会校验key,符合字段名的就能接收到,不符合的就接收不到.
*
* @param apply
* @return
*/
@PostMapping(value = "/useMap", produces = "application/json;charset=utf-8")
public String useMap(@RequestBody Map<String,Object> apply) {
try {
System.out.println(apply);
return "success";
} catch (Exception e) {
return "fail";
}
}
@PostMapping(value = "/useJSON", produces = "application/json;charset=utf-8")
public String useJSON(@RequestBody JSONObject apply) {
try {
System.out.println(apply);
return "success";
} catch (Exception e) {
return "fail";
}
}
@PostMapping(value = "/useBean", produces = "application/json;charset=utf-8")
public String useBean(@RequestBody TestVO apply) {
try {
System.out.println(apply);
return "success";
} catch (Exception e) {
return "fail";
}
}
// 后端日志打印
useMap:{password=1231, name=lkw, age=234, vo={name=lkw, age=233334}}
useJSON:{"password":1231,"name":"lkw","vo":{"name":"lkw","age":233334},"age":234}
useBean:TestVO(name=lkw, age=234, vo=TestVO(name=lkw, age=233334, vo=null))
总结:
- spring框架进行后端接收参数时,使用map、json、bean的作用是完全相同的,会根据参数的key进行自动映射,有则映射,无则为null。
4.4. body参数和url参数的总述
- 其实没有什么核心区别,url请求和body请求其实是互相兼容的,http情求时二者是可以同时存在的,无论是什么请求类型,都可以同时存在url参数和body参数。这是http具有的能力!
- 对于http请求来说,请求端传参和响应端响应是非常宽泛的,你可以多传,我也可以多返,双方按需自取。
- url请求时将参数放在请求行后面;body请求就是将参数放在请求体中
- body参数分为两种,一种是key-value格式,代码中的体现就是.field(),对于后端来说它的接收方式与url请求的参数接收方式相同;另一种是非kv格式,包括json、txt、binary等等,代码中的体现就是.body()
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)