转载

Spring Web应用学习笔记

前前后后花了好几个月的时间学习Java基础,感觉都是零零散散的知识点,不仅不知道实际当中怎么用,而且容易忘。所以,开始一个真正的应用很有必要,不仅可以整合前面所学的知识,更重要的是开始接触真正的工作内容。

1. 从一个空的文件夹开始

一个基本的Spring项目包括:

pom.xml文件
src/main/java/hello/Application.java
src/main/java/hello/HelloController.java

Spring Web应用学习笔记

1.1 添加内容

pom(project-object-model),maven的配置文件,也是Java项目的核心文件。首先,按照官网的 提示 ,在一个空的文件夹中新建 pom.xml 文件,然后将 Build With Maven 中的内容粘贴到其中。

Application以及HelloController,同样按照提示将代码复制到其中。 刷新Maven 后,就可以发现这时候这个项目以及可以开始运行了。并且在浏览器中输入 lcoalhost:8080 就可以看到这个项目已经启动了:

Spring Web应用学习笔记

2. web应用的本质

  • 从HTTP请求中提取querystring(查询字符串)。
  • 从HTTP中接受payload(负载)中的参数。

例如:我们在谷歌浏览器搜索栏中填入 springboot 这时可以看到url变成了 https://www.google.com/search?q=springboot&oq=springboot&aqs=chrome..69i57l2j69i60j69i61j69i60l2.2979j0j7&sourceid=chrome&ie=UTF-8
那么 后面这一系列的键值对就都称为查询字符串。补充说明一下:search在产品维度上称为接口。

响应内容包括:

  1. status code
  2. HTTP response header
  3. HTTP response body

补充说明:

  1. 字节流中HTTP header与HTTP body之间用四个分隔符/r/n/r/n来分隔。
  2. body中又包括:JSON,HTML,CSS等

2.3 如何获取查询字符串

假设我们是Google,用户输入这样一个网址 http://localhost:8080//search?q=dog ,那么我们要怎么拿到这个 dog 关键字呢?回到代码中:

//将接口名指定为/search
@RequestMapping("/search")
   //请求的参数指定为q
    public String search(@RequestParam("q") String searchKeyWord) {
        return "you are searching:"+searchKeyWord;
    }
复制代码

重新点击运行,输入网址:

Spring Web应用学习笔记

可以看到这时候我们就拿到用户请求的关键字了。但是从前面Google的url请求中可以看出,实际上url中携带的请求参数肯定不止一个,并且用户肯定是不知道serach接口中存在多少个参数,那么如何设置多个参数,并设置默认不需要赋值? 代码如下:

@RequestMapping("/search")
   //将charset参数设置为非强制的
    public String search(@RequestParam("q") String searchKeyWord,
                         @RequestParam(required = false,value = "charset") String charset) {
        return "you are searching:"+searchKeyWord + " with charset is :"+charset;
    }
复制代码

GET请求通常用于传递非敏感信息,因为在GET请求中的账户名密码都是会显示在url中的,所以不能将敏感信息通过GET请求发送。

2.4 RESTful API

RESTful API是一套用于设计WEB数据接口的一套API设计规范,用以规范URL、状态码以及服务器的响应。 developer.github.com/v3/ (github的API被称为业界的标杆,值得学习)。

  • 使用HTTP动词来代表动作
  1. GET-获取资源
  2. POST-新建资源
  3. PUT-更新资源
  4. DELETE-删除资源
  • 使用URL来代表资源
  1. 资源里面没有名词
  2. 使用复数来代表资源列表

方法中的参数获取

以DELETE举例,spring中的代码如下:

@RestController
//把所有的请求映射到一个方法上
@RequestMapping("repos")
public class IssueController {
    @DeleteMapping("/{owner}/{repo}/issues/{issuesNumber}/lock")
    public void unlock(
            @PathVariable("owner") String owner,
            @PathVariable("repo") String repo,
            @PathVariable("issuesNumber") String issueNumber
    ) {
        System.out.println(owner+" "+repo+" "+issueNumber);
    }
}
复制代码

由于DELETE操作不能由浏览器发起,这里使用 Postman 模拟DELETE请求。

Spring Web应用学习笔记

在控制台中可以看到输出信息:

Spring Web应用学习笔记

这样便实现了第二种请求中参数的获取,既从路径中获取参数。

2.5 从HTTP post中获取body

当请求的参数非常多时,一般都是将参数至于HTTP post的body中,那么如何从HTTP的body中拿到参数呢?同样以GitHub的POST举例:

请求的路径为 POST /repos/:owner/:repo/issues

body为:

{
  "title": "Found a bug",
  "body": "I'm having a problem with this.",
  "assignees": [
    "octocat"
  ],
  "milestone": 1,
  "labels": [
    "bug"
  ]
}
复制代码

代码如下:

@PostMapping("/{owner}/{repo}/issues")
    public void createAnIssue(
            @PathVariable("owner") String owner,
            @PathVariable("repo") String repo,
            @RequestBody HashMap requestBody
    ){
        System.out.println(requestBody);
    }
复制代码

同样使用PostMan模拟post请求: url: http://localhost:8080/repos/hello/world/issues
body:

Spring Web应用学习笔记

注意,这时候需要设置header:

Spring Web应用学习笔记

否则spring是识别不出JSON的。

通过调试看到:

Spring Web应用学习笔记

可以看到,我们把post的body中的参数拿出来了,但是使用LinkedHashMap来存储参数,获取参数不是那么方便,我们可以生成一个内部类来存储。使用插件 GsonFormat 可以代替我们做这件事情:

具体操作:

新建一个类->alt+insert->Gsonformat->将JSON字符粘贴至其中->点击确定即可。

结果如下: PostBody{title='Found a bug', body='I'm having a problem with this.', milestone=1, assignees=[octocat], labels=[bug]}

2.6 从表单中获取body

代码如下:

@PostMapping("/getForm")
    public void getForm(
            @RequestParam("name") String name,
            @RequestParam("city") String city
    ){
        System.out.println(name);
        System.out.println(city);
    }
复制代码

post请求如下:

Spring Web应用学习笔记

结果:

zhangsan
beijin
复制代码

可以看到,实现了从form表单中获取参数,这种方式适用于参数比较少的情况。

3.生成HTTP响应

通过前面的几种方式,我们拿到了HTTP请求中的参数,接下来就是作出合理的处理,然后生成HTTP响应给前端了。

首先我们知道,HTTP响应返回的本质上就是一个HTML文本,使用一些标记语言实现对结果的美化。所以最简单的操作,我们也可以在Java代码中直接添加HTML标签:

@RequestMapping("/getForm")
    public String getForm(
            @RequestParam("name") String name,
            @RequestParam("city") String city
    ) {
    //为返回结果添加标签
        return ("<em>" + name + "</em>" + " " + "<strong>" + city + "</strong>");
    }
复制代码

结果:

Spring Web应用学习笔记

也可以直接操作HttpServletResponse对象(Servlet的核心接口)。

@RequestMapping("/Servlet")
    public void controlRawServlet(HttpServletRequest request, HttpServletResponse response) throws IOException {
    //返回一个status code=403错误
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
    //将信息写入http reponse中
        response.getWriter().write("hello world 403");
    }
复制代码

结果如下:

Spring Web应用学习笔记

当然,实际运用中是不会用这样简单粗暴的方式的,一般都是格式化的返回一个 JSON 字符串。在Spring中实现往http body中写入数据:

@RequestMapping("/GetBody")
    @ResponseBody
    public Object writeIntoBody(){
        Map<String,Object> map = new HashMap<>();
        map.put("result", Arrays.asList("zhangsan","beijin"));
        return map;
    }
复制代码

结果:

Spring Web应用学习笔记

注意:返回的文本,默认格式是JSON。但是,如果我们返回一个XML呢?在PostMan中这样设置:

Spring Web应用学习笔记

可以看到console显示: HttpMediaTypeNotAcceptableException 意思就是不接受这种返回类型。

其实这就是Spring最基本的实现了,获取一个Http请求返回一个Http reponse。我们需要做的就是在这个基础上继续扩展,包括数据库、Redis缓存、消息队列、以及RPC(Remote Procedure Call-远程过程调用)、微服务化等。

4.提问

4.1 HTTP GET不能带body嘛?

参考自WebTechGarden的个人博客以及stackoverflow的回答:

首先HTTP协议中并没有规定GET中不能带body,并且作者采用多种方式证明了,HTTP GET是可以带body的。但是GET被设计来用URI来识别资源,如果让它的请求体中携带数据,那么通常的缓存服务便失效了,URI 不能作为缓存的 Key。

但另一方面,如果仅仅是为了读取资源,而需要使用 Body 发送一大批数据时,改用 POST 请求却与 RESTFul 的 post 语义不相符。这时候或许可以 GET + body, 但是不能对该请求以 URI 作为 Key 进行缓存了。

所以,在GET中添加body是没有意义的。

5. 必备知识

5.1 Servlet、Servlet容器

摘自: 知乎.来之孤岛的鲸鱼

  • Servlet

Java Servlet(Java服务器小程序)是一个基于Java技术的Web组件,运行在服务器端,它由Servlet容器所管理,用于生成动态的内容。 Servlet是平台独立的Java类,编写一个Servlet,实际上就是按照Servlet规范编写一个Java类。Servlet被编译为平台独立 的字节码,可以被动态地加载到支持Java技术的Web服务器中运行。

  • Servlet容器

Servlet容器也叫做Servlet引擎,是Web服务器或应用程序服务器的一部分,用于在发送的请求和响应之上提供网络服务,解码基于 MIME的请求,格式化基于MIME的响应。Servlet没有main方法,不能独立运行,它必须被部署到Servlet容器中,由容器来实例化和调用 Servlet的方法(如doGet()和doPost()),Servlet容器在Servlet的生命周期内包容和管理Servlet。在JSP技术 推出后,管理和运行Servlet/JSP的容器也称为Web容器。

Spring Web应用学习笔记

6. 参考资料:

1.个人博客.《谁说 HTTP GET 就不能通过 Body 来发送数据呢?》 跳转至源文章

2. stackoverflow.“HTTP GET with request body” 跳转至源文章

3. 微信公众号: WebTechGarden.《99%的人都理解错了HTTP中GET与POST的区别》 跳转至源文章

4. 个人博客.《RESTful API 最佳实践》 跳转至源文章

5. 知乎.《几个概念:Servlet、Servlet容器、Tomcat》 跳转至源文章

原文  https://juejin.im/post/5dc3ae666fb9a04a6076b616
正文到此结束
Loading...