Spring框架 - Web框架 實現Controller

實現Controller

實現基本的Controller並處理輸入輸出html

基本的Controller

添加了Controller Annotation聲明該類是Controller
也添加RequestMapping,經過value制定的/hello的路徑
咱們在函數處理以前也加了RequestMapping,經過value指定/spring這樣的結果是當碰到/hello/spring就會把請求轉接到Spring這個函數的處理
這個函數就輸出信息。web

@Controller
@RequestMapping(value = "/hello")
public class HelloController {
    
        @RequestMapping(value = "/spring")
        public void spring(HttpSerlvetResponse response) throw IOException{
            response.getWriter().write("Hello, Spring Web!!");
            }
}

定義Controller

在Spring配置文件中,經過context:component-scan來自動掃描,這樣咱們在所掃描包下的經過Controller Annotation定義Controller都會被加載WebApplicationContext做爲實例正則表達式

<!-- 自動掃描Controller,自動掃描目錄下的子包,不可以直接填寫對應controller,會自動掃描@componetns 等註釋,並註冊Bean-->
       <context:component-scan base-package="com.hava"/>

@RequestMapping參數

  • name:名稱
  • value&path:路徑,如"/hello"
    能夠不寫
  • method:請求方法,如"GET"
    HTTP協議的請求方法,這裏是爲了限制請求的方法,若是碰到DELETE就不會處理。添加RequestMapping實際是爲了限制哪些請求能夠進入到這個函數當中。好比咱們制定的path是須要特定的請求,纔可以進入到咱們的方法當中。method是指定HTTP請求的某些方法。才能被咱們的函數處理。
  • params:請求參數
    請求參數通常有兩種形式進行處理,一種是在請求地址裏面添加?,好比添加?key=value的這種形式的請求方法,能夠經過與操做添加多個請求參數。也是爲了限制特定的請求參數轉發到這裏
  • headers:請求頭
    限制請求的頭信息,好比自定義的header內容與信息,經過服務端的header進行配置
  • consumes:請求的媒體類型,與HTTP"Content-Type"對應
    只可以處理特定請求數據的Content-Type,好比json格式,咱們這裏聲明json格式,則請求方法就不能進行處理。
  • produces:響應的媒體類型,"Accept"
    能夠指定只輸出JSON,也能夠執行只輸出XML。一樣的,能夠根據Accept請求的類型輸出對應的媒體類型。能夠在請求的accept請求json,則返回json;accept請求xml返回xml。主要限制哪些請求能夠被這個函數所處理。

@RequestMapping路徑

  • RestAPI ?
    若是用原生Serlvet去處理,須要去作地址的解析,去解析對應的參數。在Spring當中,用Spring的,只須要定義以下的RequestMapping。咱們制定path的值,{userId}表明是變量,當訪問URL在users以後跟的參數,咱們都認爲是userId的值。而後在函數的參數部分添加@PathVariable,把userId的值做爲這個函數的參數添加到這裏。經過這種方式,只須要在這個方法上添加@RequestMapping就能夠很好的處理路徑參數匹配問題。
@RequestMapping(path = "/users/{userId}")
public String webMethod(@PathVariable String userId){
    // do work
}

@RequestMapping添加正則表達

咱們能夠在RequestMapping中使用正則表達式,不過對格式有限制。以下示例表示,只有字母的userId。spring

@RequestMapping(path = "/users/{userId:[a-z]+}")
public String webMethod(@PathVariable String userId){
}

函數實現

首先咱們經過RequestMapping經過匹配達到某些條件的請求進入函數,對於以下示例的函數,參數有HttpServletResponse,自動的注入到response對象中。在實現裏面,咱們經過response獲取客戶端的writer,把咱們想輸出的信息輸出到客戶端。
須要注意的是,以下示例的函數返回值爲void,爲何咱們在這個函數內寫response,response就能夠拿到上下文信息,把咱們想要的信息輸出到客戶端當中。主要是由於Spring會對請求處理函數,作一些限制:能夠注入哪些參數、有哪些種類的返回值。若是在請求參數裏面設定參數,例如response參數,會在進入到這個函數的時候運行時會把相關的信息注入到函數當中。apache

@RequestMapping(value = "/spring")
public void spring(HttpServletResponse response) throw Exception{
    response.getWriter().write("Hello, Spring Web!!");
}

函數參數類型

  • HttpServletRequest/HttpServletResponse,HttpSession(Servlet API)
  • Reader/Writer
    能夠直接注入Writer,是Java下面的包。Reader是和HttpRequest對應的,能夠經過Reader讀取請求內容
  • @PathVariable
    在請求地址中中匹配參數,叫作@PathVariable。能夠編寫@PathVariable的匹配請求路徑中間的參數
  • @RequestParam
    匹配請求參數,例如URL地址?後面的,或者經過表單提交的。在Post方法的請求體裏面。
  • @RequestHeader
  • HttpEntity
    Java裏面的Spring定義的HttpEntity,有兩種HttpEntity。一種是請求的HttpRequestEntity,一種是響應的HttpResponseEntity。
  • @RequestBody
    經過@RequestBody匹配整個請求體
  • Map/Model/ModelMap
    因爲SpringMVC的功能,咱們也能夠注入一些跟模型相關的功能。對於Spring來講,能夠獲取通用意義上的Map,key-value的這種形式。也能夠Spring本身定義的Model或者ModelMap。這兩種都是和Map相似的key-value結構。做爲模型注入函數當中。好比在參數中編寫Map,Spring會自動把東西轉化成Map對象,咱們就能夠設置與返回結果Model相關的一些屬性。

函數返回值

函數返回值是返回給用戶的View的名稱。json

  • void
    所表明的意義:函數返回值是返回用戶的View的名稱。可是若是這裏返回void,表明請求處理函數不用去返回View。函數內部會處理與View相關的邏輯。
  • String
    通常狀況下表明返回view的名稱,也能夠經過@ResponseBody這個Annotation,在函數級別作@RequestBody,表明返回的String,不是view名稱,而是響應內容。
  • HttpEntity
    在函數參數當中的HttpEntity表明請求的HttpEntity,在返回值也能夠加入HttpEntity(HttpResponseEntity),表明函數返回值。HttpEntity包含響應的Header、響應的內容。對應的函數參數的HttpEntity包含請求的Header、請求的內容。
  • View
    返回Spring所定義的View對象,表明View對象。返回View對象,是提示Spring如何查找最終輸出的View。View是與模板相關。
  • Map
    表明咱們所要返回的Model是什麼樣子的。與請求參數當中的Map一致。能夠經過返回值返回Model。也能夠在函數參數裏面注入一個對象,去修改Model。
  • Model
    Map與Model只是返回了一個Model,並無帶View的信息。
  • ModelAndView
    指定好視圖相關的內容,也指定了視圖所對應的Model。

函數參數與返回值示例

下面的示例和咱們以前的示例相似,首先返回值是void。以後是函數參數:api

  • 第一個參數@PathVariable
    匹配到的名稱叫作user,注入的對象是String類型的user。
  • 第二個參數@RequestParam
    好比在URL的?後段的參數,在表單提交時,請求體裏面也能夠添加這個參數。名稱叫作msg,咱們最終指望,要麼URL?後面的參數有msg,要麼經過Post請求體裏面有msg字段,來請求參數。
  • 第三個參數@RequestHeader
  • 第四個參數@HttpServletRequest
    HttpServletRequest是和HttpServletResponse相似。能夠經過這種方式,直接拿到request對象。經過request對象拿到請求相關的信息。例如RequestHeader、RequestParam均可以經過這個對象獲取,咱們是把咱們比較關心的信息直接匹配到參數當中。這樣咱們就不須要再實際的函數當中添加RequestHeader、RequestParam的請求邏輯。
  • 第五個參數Writer
    咱們能夠直接匹配Writer,在以前的示例裏面,並無直接獲取writer對象,而是經過注入的Response對象,獲取writer對象。在函數實現裏面,寫一些展示給用戶的內容。
@RequestMapping(value = "/spring/{user}")
public void spring(
    @PathVariable("user") String user,
    @RequestParam("msg") String msg,
    @RequestHeader("host") String host,
    HttpServletRequest request,
    Writer writer) throw IOException{
        writer.write("URI: " + request.getRequestURI());
        writer.write("Hello " + user + " : " + msg + " host=" + host);
}

使用場景

  • 簡單表單
    通常狀況下,最多的是表單,好比用戶登陸,就是把用戶名密碼提交到服務端。能夠經過@RequestParam獲取username和password,把這兩個字段的值匹配到對應的參數對象。咱們在實現邏輯就能夠處理username和password的處理邏輯。
@RequestMapping(value = "/spring/login")
public void login(
    @RequestParam("username") String username,
    @RequestParam("password") String password,
    Writer writer) throw IOException{
    // do something with name and password
}
  • 複雜表單
    註冊表單會有不少不少字段,若是咱們在函數內一一的注入字段,咱們須要編寫不少@RequestParam的注入。Spring提供了 @ModelAttribute,經過@ModelAttribute的Annotation來匹配稱做Model的Java對象,直接從參數裏面匹配Model,上面的示例,簡化匹配一個參數就能夠了。以下示例咱們定義了User的Model類。包含兩個字段,name&password。而後就能夠像以下示例,經過@ModelAttribute注入user對象。會自動把user的name和password,經過表單注入到對象內。而後就能夠經過對象,獲取屬性。
@RequestMapping(value = "/spring/login")
public void login(
    @ModelAttribute User user,
    Writer writer) throw IOException{
    // do something with name and password
}
  • 上傳文件
    在表單裏面有選擇按鈕,以後Button點擊上傳。在服務端處理,首先須要定義一個和文件上傳相關的Bean。這個Bean叫作CommonsMultipartResolver,咱們通常把文件上傳稱做Multipart,當在Spring定義完CommonsMultipartResolver就能夠簡單使用了。Spring的Multipart是經過Apache commons的第三方庫實現的。因此,若是要添加CommonsMultipartResolver,則須要在Maven中添加Apache Commons的依賴。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="100000" />
</bean>

當咱們定義好multipartResolver的Bean後,咱們就能夠在參數中,經過@RequestParam注入MultipartFile的對象,直接把文件內容匹配當對象中。若是限定了函數的method是RequstMethod.POST方式。文件上傳以POST方式上傳。瀏覽器

@RequestMapping(path="/form",method=RequestMethod.POST)
public String handleFormUpload(@RequestParam("file") MultipartFile file){
    //save file
}
  • HttpEntity
    以下示例,請求參數也是HttpEntity,返回結果也是HttpEntity。能夠看到函數匹配的參數是HttpEntity,也是有類型的,好比byte[],若是是寫其餘類型,則須要實現對應的轉換器。在以下示例中是byte[],在注入requestEntity之後,能夠經過requestEntity獲取和Request相關的信息,例如獲取到requestHeader、requestBody。最後,咱們能夠返回一個ResponseEntity,能夠設置responseHeader和responseBody相關的。以下示例返回的String類型。
@RequestMapping("/someting")
public ResponseEntity <String> handle(HttpEntity <byte[]> requestEntity){
    String requestHeader = requstEntity.getHeaders().getFirst("MyRequestHeader");
    byte [] requestBody = requestEntity.getBody();
    
    // do something with request header and body

    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.set("MyResponseHeader","MyValue");
    
    return new ResponseEntity<String>("Hello",responseHeaders,HttpStatus.CREATED);
}
  • @RequestBody & @RsponseBody 函數返回的是String對象,注意若是不添加@ResponseBody,則返回的是view的名稱。若是像以下的例子添加@responseBody,則是把String內容,放置到Response的body中。
@RequestMapping(value = "/spring")
@RequestBody
public String spring(@RequestBody String body) throws IOException{
    return "Hello " + body;
}
  • 返回對象? 咱們嘗試經過@ResponseBody返回User對象,須要實際去嘗試下,須要html頁面提交表單。
@RequestMapping(value = "/spring/login")
@RequestBody
public User login(){
    User user = new User();
    
    return user;
}

**注意:**Tomcat會報錯誤。描述的是所返回的內容,不符合指定的媒體類型。例如,咱們經過瀏覽器進行返回,或者經過http的測試工具,來執行accept header,若是經過瀏覽器訪問接口的話,accept通常會指定成textHtml頁面。返回的是純文本的內容。若是是經過特定指定的話,能夠指定返回JSON,或者是XML。若是沒有通過設置,則默認的是textHtml。本示例的意思爲,瀏覽器所請求的是textHtml,可是返回的是數據對象。 輸入圖片說明服務器

  • MessageConverter
    Spring當中有MessageConverter,就是爲了解決上面相似的問題,MessageConverter不只能夠處理ResponseBody,也能夠處理RequestBody,當處理RequestBody時,是指把RequestBody轉化成對象參數。MessageConverter能夠把Object轉爲成ResponseBody。Spring提供了一些基本的返回類型的Converter。這個須要在配置文件當中添加以下內容
<mvc:annotation-driven />

該配置就會自動添加一些Converter,好比說JSON相關的Converter還有XML相關的Converter,返回的User對象,因爲打開了Converter會根據默認的Converter進行轉換。因爲默認的JSON、XML處理須要引用外部包,則須要在Maven中添加mvc

<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
			<version>2.6.4</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.6.4</version>
		</dependency>

代碼開發實例

簡單的HttpServletResponse

@Controller
public class HavaHelloController {

    @RequestMapping(value = "/response")
    public void response(HttpServletResponse response) throws IOException {
        System.out.println(response);
        response.getWriter().write("Hello,Spring Web!!");
    }
}

因爲web.xml配置以下,訪問的路徑爲/api/*

<servlet>
		<servlet-name>example</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>example</servlet-name>
		<url-pattern>/api/*</url-pattern>
	</servlet-mapping>

訪問的URL:http://localhost:8080/api/response
後臺輸出

org.apache.catalina.connector.ResponseFacade@725005c5

注入Writer

@RequestMapping(value = "/write")
    public void write(Writer writer) throws IOException {
        System.out.println(writer);
       writer.write("Hello,Spring Web!!");
    }

訪問的URL:http://localhost:8080/api/write
後臺輸出

org.apache.catalina.connector.CoyoteWriter@2e6c953a

獲取URL當中的動態參數

@RequestMapping(value = "/user/{userId}")
    public void user(@PathVariable("userId") String userId,Writer writer) throws IOException {
        writer.write("userId= " + userId);
    }

訪問的URL路徑:http://localhost:8080/api/user/123
前臺結果

userId= 123

獲取RequestHeader信息

@RequestMapping(value = "/user-host/{userId}")
    public void user_host(@PathVariable("userId") String userId,
                          @RequestParam("msg") String msg,
                          @RequestHeader("host") String host,
                          Writer writer) throws IOException {
        writer.write("userId= " + userId + ", msg=" + msg + ", hostHeader=" + host);
    }

訪問的URL路徑:http://localhost:8080/api/user-host/123?msg=test_message 前臺結果

userId= 123, msg=test_message, hostHeader=localhost:8080

若是沒有添加msg,則Spring會給前段報異常
URL:http://localhost:8080/api/user-host/123

HTTP Status 400 - Required String parameter 'msg' is not present

type Status report

message Required String parameter 'msg' is not present

description The request sent by the client was syntactically incorrect.

用戶名密碼登陸

@RequestMapping(value = "/user/login")
    @ResponseBody
    public String login(@RequestParam("username") String username,
                      @RequestParam("password") String password,
                        @RequestHeader("host") String host) throws IOException{
        System.out.println("username:" + username);
        System.out.println("password:" + password);
        return "Username: " + username + "\n " + "Password: " + password + "\n Host:" + host;
    }

Html表單

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/api/user/login" method="post">
        <p>Name: <input type="text" name="username" /></p>
        <p>Password: <input type="text" name="password" /></p>
        <input type="submit" value="Submit" />
    </form>
</body>
</html>

注意:本例子中,html是沒有web應用名稱路徑的,這個是因爲編者使用的idea,直接運行的。而沒有經過Maven進行運行。正常運行狀態,還有Maven運行是帶有web應用名稱的,而idea須要單獨設置Edit Configurations裏面添加路徑。
頁面以及填入的信息 輸入圖片說明
後臺輸出

username:123
password:456

Chrome查看的跳轉結果,注意URL發生了變化跳轉到了http://localhost:8080/api/user/login而且顯示以下結果

Username: 123 Password: 456 Host:localhost:8080

須要注意的是,咱們在輸出的String添加\n字符做爲回車,可是在輸出的頁面當中,並無進行換行。這時咱們監控Chrome的Response能夠看到以下內容

Username: test123
 Password: test123
 Host:localhost:8080

Html須要添加html的換行,纔可以顯示換行。若是添加<p></p>則發生了換行。

重要錯誤

**注意:**若是函數返回類型爲String,而且使用@ResponseBody做爲函數的返回值,若是函數參數當中注入Writer writer,則會發生異常顯現。在表單提交時,可以正確發送post請求,服務器也可以正確接收到參數內容。可是在輸出時,會發生異常獲取空白頁面,經過Chrome的請求來看,僅僅可以看到response輸出不完整錯誤信息。錯誤代號500

錯誤代碼以下:

@RequestMapping(value = "/user/login")
    @ResponseBody
    public String login(@RequestParam("username") String username,
                      @RequestParam("password") String password,
                        @RequestHeader("host") String host,
                        Writer writer) throws IOException{
        System.out.println("username:" + username);
        System.out.println("password:" + password);
        return "Username: " + username + " " + "Password: " + password + " Host:" + host;
    }

Chrome的顯示
輸入圖片說明
不完整的顯示輸出
輸入圖片說明

相關文章
相關標籤/搜索