# Responding in Requests

## Mapping Requests

In Spring, a class is marked as a **Controller** and each of its methods is associated with an endpoint. The **method** is called to formulate a response to each request. This is done by two important annotations:

1. The `@RestController` annotation is used to declare a class as a controller that can provide application-specific types of data as part of an HTTP response. It combines the functionality of `@Controller` and `@ResponseBody`.
   * `@Controller` makes a class identifiable to Spring as a part of the web app.
   * `@ResponseBody` tells Spring to convert the return values of a controller's methods to JSON and bind that JSON to the HTTP response body.
2. The `@RequestMapping` annotation is used to wire request types and endpoints to specific class methods. It accepts many params:

* `path` determines where requests are mapped.
* `method` specified the HTTP method (defaults to `GET`).
* `params` filters requests based on given params.
* `consumes` specifies which media type can be consumed.

```java
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class VirtualLibrary{

  @RequestMapping("/books/all", RequestMethod.GET)
  public Book getAllBooks() {
    return getBook();
  }

}
```

## Base Paths

When the `@RequestMapping` is used at the class level, the specified path argument will become the base path and methods within the class can be further mapped.

```javascript
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;

@RestController
@RequestMapping("/books") 
// or @RequestMapping(value = "/books", method = RequestMethod.GET)
// or @GetMapping("/books")
public class VirtualLibrary
{
  @RequestMapping(value = "/thumbnails", method = RequestMethod.GET)
  public String[] getBookThumbnails() {
    //returns thumbnails for all available titles
  }
}
```

## Query Params

**Binding** is the process of a user passing data to the server. For instance, when a user submits a book's ISBN, we need a way to **bind their entry to the endpoint** that will provide the response. This is achieved using the `@RequestParam` annotation in Spring.

```html
# Example GET request
libraryapp.com/book?author_count=2&published_year=1995
```

{% code overflow="wrap" %}

```java
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.GetMapping;

@GetMapping("/book")
public Book isBookAvailable(@RequestParam int author_count, @RequestParam int published_year) {
  return books.find(author_count, published_year);
}
```

{% endcode %}

## Template Path Vars

The `@RequestParam` annotation is useful when we want to filter out the results or return serveral resources. However, if we want to return more specific entities we can use the `@PathVariable` annotation which maps template vars in the request URI directly to a method's parameters.

For instance, if we have defined a path such as `/books/{id}` and use the URI `localhost:4001/books/28937`, and we want to pass the path var `28937` to a method's `id` parameter, we would have the following code.

```java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@GetMapping("/{id}")
public Book isBookAvailable(@PathVariable int id)  {
  return book.findByID(id);
}
```

{% hint style="warning" %}
`@RequestParam` captures the `id` included in the URI `/books?id=28937` and `@PathVariable` captures the `id` included in the URI `/books/28937` as long as the path includes the `{id}` variable in `books/{id}`.
{% endhint %}

## Deserializing Into Objects

Instead of parameters, we might want to receive an entire form object via an HTTP request. The `@RequestBody` annotation allows Spring to automatically deserialize the HTTP request body into a Java object which can be bound to the method and further processed. The objects can be created by matching attribute with values in JSON.&#x20;

```java
import org.springframework.web.bind.annotation.RequestBody;

// Define a Book object
class Book {
  public int authorCount;
  public int publishedYear;
}

// Define the controller
@GetMapping("/book")
public Book isBookAvailable(@RequestBody Book book) {
  return book.find(book.authorCount, book.publishedYear);
}
```

For this to work, a `POST` request that has the data in the request body instead of the URL is needed.

{% code overflow="wrap" %}

```bash
curl -X POST --data '{"authorCount":"2", "publishedYear": "1995"}' "https://localhost:8080/.../book"
```

{% endcode %}

We can create a new object directly within the parameter field.

```java
  @PostMapping(path="/addNew")
  public String createNewSuperHero(@RequestBody SuperHero superHero) {
    superHeroRepository.save(superHero);
    return "New Super Hero successfully added!";
  }
```

## Fine Tuning Responses

The `@ResponseStatus` annotation can be used to customize messages in both failure and success responses. It accepts the `code` and `reason` parameters.

> [*HttpStatus codes*](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpStatus.html) *on Spring.*

{% code overflow="wrap" %}

```java
@PutMapping(path="/book")
@ResponseStatus(code = HttpStatus.CREATED, reason = "Book was successfully added")
public [string](https://www.codecademy.com/resources/docs/general/data-types/string) addNewBook(@RequestParam string title) {

}

```

{% endcode %}

## Exception Handling

If an error is encountered, we can use the `@ResponseStatusException` annotation which accepts three args:

1. `HttpStatus code`
2. `String reason`
3. `Throwable cause`

For instance, if we accept the `ID` from the client as a `String` and parse it as `int` and the parse fails, a `NumberFormatException` will be thrown which is not useful for the client.

{% code overflow="wrap" %}

```java
@GetMapping("/{id}")
public Book isBookAvailable(@PathVariable string id) 
{
  if (id.isNumeric()) {
    int id_int = Integer.parseInt(id)
    return book.findByID(id_int)
  } 
  else {
    throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "The ID contained a non-numerical value.");
  }
}
```

{% endcode %}
