Spring Controller

Spring Controller

基本概念

一個最基本的Spring Controller就是一個類別,首先,會定義package,請養成習慣把controller都放在controller的package裡,並定義所需要import的類別,接下來,會利用java的Annotation:「@」來定義這個類別是個Controller。在類別中,指定home這個方法的RequestMapping (詳參: http://peaceful-developer.blogspot.tw/2014/08/spring-2-requestmapping.html),也就是指定這個Controller的相對URI,以下面的範例,就是告訴web server,當瀏覽器要求"/"時,就呼叫home()。另外,@ResponseBody會將home()的回傳值當做是Response的內容。也就是網頁上會看到「Hello World」。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SampleController {

    @RequestMapping("/")
    @ResponseBody
    public String home() {
        return "Hello World";
    }

}

如 果不使用@ResponseBody的話,return值就變成要重導的網頁名稱了。如:

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class SampleController {

    @RequestMapping("/")
    public String home() {
        return "index.html";
    }

}

傳參數給View

Controller可以跟View互動,我們先介紹如何傳值到View。第一種方式是使用Model,Model提供addAttribute(),第一個參數是傳到view之後,對應變數的名稱,第二個參數是傳過去的內容,下面的範例就是將變數名稱「name」內容為「ben」傳到hello。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.ui.Model;

@Controller
public class HelloController {

    @RequestMapping("/hello")
    public String home(Model model) {
       model.addAttribute("name", "ben");
       return "hi";
    }

}

第二種方式是使用ModelAndView,在產生ModelAndView時,可以指定hi為呼叫的view。ModelAndView提供addObject(),第一個參數是傳到view之後,對應變數的名稱,第二個參數是傳過去的內容,下面的範例就是將變數名稱「name」內容為「ben」傳到hi。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {

    @RequestMapping("/hello")
    public ModelAndView home() {
       ModelAndView model = new ModelAndView("hello");
       model.addObject("name", "ben");
       return model;
    }

}

對應的hello.html (採用Thymeleaf)內容如下:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Hello!</title>
</head>
<body>
<h2>Hello! <span th:text="${name}">Wu</span></h2>
    
</body>
</html>

如果是物件的話,是以addObject的第一個參數(如:newcustomer)做為傳遞的對應名稱,而不是使用變數名稱(如:cus)。

  @RequestMapping(value = "/customerUpdate", method = RequestMethod.GET)
    public ModelAndView openFormUpdate(@RequestParam(value="id", required=false, defaultValue="1") Long id) {
       ModelAndView model = new ModelAndView("customerUpdate");
       Customer cus = dao.findOne(id);
       model.addObject("newcustomer",cus);
       
       return model;
    }

對應的view(customerUpdate.html):

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Update Customer</title>
</head>
<body>

<form action="customerUpdate" th:object="${newcustomer}" method ="post">

 name:<input type="text" name="name" th:field="*{name}"/><br/>
 address:<input type="text" name = "address" th:field="*{address}"/><br/>
 weight:<input type="text" name ="weight" th:field="*{weight}"/><br/>
 <input type="hidden" name ="id" th:field="*{id}"/>
 <input type="submit" value="Submit"/>

</form>

</body>
</html>

如果controller中的addObject是用物件傳送資料(單一參數),在Thymeleaf中,會以物件的類別名稱(如:Customer)來做為物件的名稱, 不過,會將第一個字母的大寫轉為小寫(如:customer),而不是使用變數名稱(如:cus)

  @RequestMapping(value = "/customerUpdate", method = RequestMethod.GET)
    public ModelAndView openFormUpdate(@RequestParam(value="id", required=false, defaultValue="1") Long id) {
       ModelAndView model = new ModelAndView("customerUpdate");
       Customer cus = dao.findOne(id);
       model.addObject(cus);
       
       return model;
    }

對應的view(customerUpdate.html):

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Update Customer</title>
</head>
<body>

<form action="customerUpdate" th:object="${customer}" method ="post">

 name:<input type="text" name="name" th:field="*{name}"/><br/>
 address:<input type="text" name = "address" th:field="*{address}"/><br/>
 weight:<input type="text" name ="weight" th:field="*{weight}"/><br/>
 <input type="hidden" name ="id" th:field="*{id}"/>
 <input type="submit" value="Submit"/>

</form>

</body>
</html>

從View接收參數

接收參數的方法有好幾種,第一種方式是利用HttpServletRequest,利用getParameter來存取對應的變數。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

@Controller
public class HelloController {

    @RequestMapping("/helloWithId")
    public ModelAndView home(HttpServletRequest request) {
       ModelAndView model = new ModelAndView("hi");
       String name = request.getParameter("id");
       model.addObject("name", name);
       return model;
    }

}

第二種方式是利用@RequestParam,可以設定這個參數不是必要的,也可以設定當參數不存在時的預設值。這樣程式碼簡潔多了。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {

    @RequestMapping("/helloWithId")
    public ModelAndView home(@RequestParam(value="id", required=false, defaultValue="world") String name) {
       ModelAndView model = new ModelAndView("hi");
       model.addObject("name", name);
       return model;
    }

}

第三種方式是利用@ModelAttribute。這樣程式碼簡潔多了。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {

    @RequestMapping("/helloWithId")
    public ModelAndView home(@ModelAttribute("id") String name) {
       ModelAndView model = new ModelAndView("hi");
       model.addObject("name", name);
       return model;
    }

}

物件的傳遞

使用@ModelAttribute的另一個好處是可以接受一個物件。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.example.demo.entity.Customer;

@Controller
public class CustomerController {
    @RequestMapping(value = "/customerCreate", method = RequestMethod.GET)
    public ModelAndView openForm() {
       ModelAndView model = new ModelAndView("customerCreate");
       return model;
    }

    @RequestMapping(value = "/customerCreate", method = RequestMethod.POST)
    public ModelAndView processForm(@ModelAttribute Customer cus) {
       ModelAndView model = new ModelAndView("customerDone");
       model.addObject("customer",cus);
       return model;
    }
    
}

上面的範例在RequestMapping中,除了指定URI之外,還指定了RequestMethod,這樣就可以兩個方法(openForm, processForm)使用同一個URI (細節請參閱後面對於RequestMapping的說明)。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.example.demo.entity.Customer;

@Controller
public class CustomerController {
    @RequestMapping(value = "/customerCreate", method = RequestMethod.GET)
    public ModelAndView openForm() {
       ModelAndView model = new ModelAndView("customerCreate");
       return model;
    }

    @RequestMapping(value = "/customerCreate", method = RequestMethod.POST)
    public ModelAndView processForm(@ModelAttribute Customer cus) {
       ModelAndView model = new ModelAndView("customerDone");
       model.addObject("customer",cus);
       return model;
    }
    
}

必須配合Customer.java,這樣的類別就屬於Model,其內容如下:

package com.example.demo.entity;

public class Customer {
  private String name;
 private String address;
 private int weight;

 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }

 public String getAddress() {
  return address;
 }
 public void setAddress(String address) {
  this.address = address;
 }

 public int getWeight() {
  return weight;
 }
 public void setWeight(int weight) {
  this.weight = weight;
 }
}

以及customerCreate.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Create New Customer</title>
</head>
<body>

<form action="customerCreate" method ="post">

 name:<input type="text" name="name"/><br/>
 address:<input type="text" name = "address"/><br/>
 weight:<input type="text" name ="weight"/><br/>
 <input type="submit" value="Submit"/>

</form>

</body>
</html>

及customerDone.html。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Create New Customer</title>
</head>
<body>

name: <span th:text="${customer.name}">Wu</span>
address: <span th:text="${customer.address}">Fu Jen</span>
weight: <span th:text="${customer.weight}">56</span>

</body>
</html>

另外,利用model.addObject時,是利用第一個參數(如:newcustomer)來指定view的接收變數名稱。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.example.demo.entity.Customer;

@Controller
public class CustomerController {
    @RequestMapping(value = "/customerCreate", method = RequestMethod.GET)
    public ModelAndView openForm() {
       ModelAndView model = new ModelAndView("customerCreate");
       return model;
    }

    @RequestMapping(value = "/customerCreate", method = RequestMethod.POST)
    public ModelAndView processForm(@ModelAttribute Customer cus) {
       ModelAndView model = new ModelAndView("customerDone");
       model.addObject("newcustomer",cus);
       return model;
    }
    
}

對應的view (customerDone.html)。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Create New Customer</title>
</head>
<body>

name: <span th:text="${newcustomer.name}">Wu</span>
address: <span th:text="${newcustomer.address}">Fu Jen</span>
weight: <span th:text="${newcustomer.weight}">56</span>

</body>
</html>

另外,如果省略第一個參數,在Thymeleaf中,會以物件的類別名稱(如:Customer)來做為物件的名稱, 不過,會將第一個字母的大寫轉為小寫(如:customer),而不是以變數名稱(如:cus)。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.example.demo.entity.Customer;

@Controller
public class CustomerController {
    @RequestMapping(value = "/customerCreate", method = RequestMethod.GET)
    public ModelAndView openForm() {
       ModelAndView model = new ModelAndView("customerCreate");
       return model;
    }

    @RequestMapping(value = "/customerCreate", method = RequestMethod.POST)
    public ModelAndView processForm(@ModelAttribute Customer cus) {
       ModelAndView model = new ModelAndView("customerDone");
       model.addObject(cus);
       return model;
    }
    
}

對應的view (customerDone.html)。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Create New Customer</title>
</head>
<body>

name: <span th:text="${customer.name}">Wu</span>
address: <span th:text="${customer.address}">Fu Jen</span>
weight: <span th:text="${customer.weight}">56</span>

</body>
</html>

RequestMapping

HTTP Request分為get與post,所以,同一個URI可以對應get及post。以下面的範例而言,呼叫GET時,打開hi.html,hi.html會呼叫POST,並傳送id給controller,controller將id內容存在name,傳回給hello.html。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {

    @RequestMapping(value = "/helloWithId", method = RequestMethod.GET)
    public ModelAndView openForm() {
       ModelAndView model = new ModelAndView("hi");
       return model;
    }
    @RequestMapping(value = "/helloWithId", method = RequestMethod.POST)
    public ModelAndView processForm(@ModelAttribute("id") String name) {
       ModelAndView model = new ModelAndView("hello");
       model.addObject("name", name);
       return model;
    }

}

hi.html

<!DOCTYPE html>
<html>
<body>

<form action="helloWithId" method ="post">

 id:<input type="text" name ="id"/><br>
 <input type="submit" value="Submit"/>

</form>

</body>
</html>

hello.html (採用Thymeleaf)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Hello!</title>
</head>
<body>
<h2>Hello! <span th:text="${name}">Wu</span></h2>
    
</body>
</html>

同一個方法可以對應多個uri,如:value = {"/helloWithId", "hello2"}。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {

    @RequestMapping(value = {"/helloWithId", "/hello2"}, method = RequestMethod.GET)
    public ModelAndView openForm() {
       ModelAndView model = new ModelAndView("hi");
       return model;
    }
    @RequestMapping(value = "/helloWithId", method = RequestMethod.POST)
    public ModelAndView processForm(@ModelAttribute("id") String name) {
       ModelAndView model = new ModelAndView("hello");
       model.addObject("name", name);
       return model;
    }

}

參考資料