Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 53 additions & 26 deletions content/v2.3/actions/formats-and-mime-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,36 @@ Hanami maps [over 50 of the most common MIME types][built-in-formats] to simple

[built-in-formats]: https://github.com/hanami/controller/blob/dc5bb2a1db48b0ccf3faf52aac20eaef0fd135a3/lib/hanami/action/mime.rb#L15-L69

Configuring one or more formats for your actions will:
Configuring your actions to accept one or more formats will:

- Ensure the actions accept only appropriate requests based on their `Accept` or `Content-Type` headers
- Set an appropriate `Content-Type` header on responses
- Ensure the actions accept only matching requests based on their `Accept` or `Content-Type` headers
- Default to an appropriate `Content-Type` header on responses
- For certain formats, enable automatic parsing of request bodies

## Configuring a format for all actions
## Accepting a format across all actions

To configure a format for all actions, use `config.actions.format` in your app class.
To accept a format for all actions, use `config.actions.format` in your app class.

```ruby
# config/app.rb

module Bookshelf
class App < Hanami::App
config.actions.format :json
config.actions.formats.accept :json
end
end
```

You can also configure actions to use multiple formats:
You can also configure actions to accept multiple formats:

```ruby
config.actions.format :json, :html
config.actions.formats.accept :json, :html
```

## Configuring a format for particular actions
## Accepting a format for particular actions

You can also configure a format on any action class. `format` in an action class is analogous to `config.actions.format` in your app class, just as `config` in an action is analogous to `config.actions` in your app.
You can also accept formats on any individual action class. `config.formats` in an action class is
analogous to `config.actions.formats` in your app class.
Comment on lines +38 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a random thought, but maybe "works the same way as" is more precise than "is analogous to", which suggests similarity with differences (at least to this non-native English speaker)


```ruby
# app/actions/books/index.rb
Expand All @@ -44,7 +45,7 @@ module Bookshelf
module Actions
module Books
class Index < Bookshelf::Action
format :json # or `config.format :json`
config.formats.accept :json

def handle(request, response)
# ...
Expand All @@ -55,21 +56,21 @@ module Bookshelf
end
```

If you configure a format on a base action class, then it will be inherited by all its subclasses.
If you accept a format on a base action class, then it will be inherited by all its subclasses.

```ruby
# app/action.rb

module Bookshelf
class Action < Hanami::Action
config.format :json
config.formats.accept :json
end
end
```

## Request acceptance

Once you've configured a format, your actions will reject certain requests that do not match the format.
Once your actions accept a format, they will reject requests that do not match the format.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exactly related to this PR, but maybe it's worth to write a little more what rejection means in this context, i.e. what status will be returned (an which view rendered?)


The following kinds of requests will be accepted:

Expand All @@ -82,7 +83,7 @@ Whereas these kinds of requests will be rejected:
- `Accept` does not include the format's MIME type, rejected as `406 Not acceptable`
- No `Accept` header, but a `Content-Type` header is present and does not match the format's MIME type, rejected as `415 Unsupported media type`

For example, if you configure `format :json`, then requests with these headers will be accepted:
For example, if you set `config.formats.accept :json`, then requests with these headers will be accepted:

- `Accept: application/json`
- `Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"` (courtesy of the `*/*`)
Expand All @@ -96,14 +97,14 @@ While requests with these headers will be rejected:

## Parsing JSON request bodies

If you configure `:json` as your action format in the app, then any requests with `Content-Type: application/json` will have their request bodies parsed and made available as request params.
If you configure `:json` as an accepted format in your app class, then any requests with `Content-Type: application/json` will have their request bodies parsed and made available as request params.

```ruby
# config/app.rb

module Bookshelf
class App < Hanami::App
config.actions.format :json
config.actions.formats.accept :json
end
end
```
Expand All @@ -122,9 +123,22 @@ end

## Response format

Actions set a `Content-Type` response header based on your configured formats along with the MIME type and charset of the incoming request.
Actions set a default `Content-Type` response header, based on your accepted formats along with the MIME type and charset of the request.

For example, if a request's `Accept` header is `"text/html,application/xhtml+xml,application/xml;q=0.9"`, the action will return a content type of `"text/html; charset=utf-8"`, assuming that the action is configured with the `:html` format.
For example, if a request's `Accept` header is `"text/html,application/xhtml+xml,application/xml;q=0.9"`, the action will return a content type of `"text/html; charset=utf-8"`, assuming that the action is configured to accept the `:html` format.

When handling a request _without_ an `Accept` header, the response's `Content-Type` will be set to the action's _default format_. You can configure this directly:

```ruby
config.formats.default = :json
```

If you do not directly configure a default format, then the first of your accepted formats will be the default:

```ruby
config.formats.accept :json, :html
config.formats.default # => :json
```

You can also assign a particular format directly on the response inside your action.

Expand Down Expand Up @@ -172,7 +186,7 @@ module Bookshelf
module Actions
module Books
class Index < Bookshelf::Action
config.default_charset "koi8-r"
config.default_charset = koi8-r"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
config.default_charset = koi8-r"
config.default_charset = "koi8-r"

missing quote

end
end
end
Expand All @@ -181,30 +195,43 @@ end

## Registering additional MIME Types

If you need your actions to work with additional MIME types, you can configure these like so:
If you need your actions to work with additional MIME types, you can register these as formats:

```ruby
# config/app.rb

module Bookshelf
class App < Hanami::App
config.actions.formats.add :custom, "application/custom"
config.actions.formats.register custom: "application/custom"
end
end
```

This will add the `:custom` format for the `"application/custom"` MIME type and also configure your actions to use this format.
This will add the `:custom` format for the `"application/custom"` MIME type.

You can also configure a format to map to multiple MIME types:
You can also configure multiple MIME types to correspond to a format:

```ruby
# config/app.rb

module Bookshelf
class App < Hanami::App
config.actions.formats.add :json, ["application/json+scim", "application/json"]
config.actions.formats.register json: ["application/json+scim", "application/json"]
end
end
```

In this case, requests for both these MIME types will be accepted.
In this case, requests for either of these MIME types be considered as `:json`.

For your actions to accept requests with these registered MIME types, you must still configure your acctions to accept the corresponding format:

```ruby
# config/app.rb

module Bookshelf
class App < Hanami::App
config.actions.formats.register json: ["application/json+scim", "application/json"]
config.actions.formats.accept :json
end
end
```
8 changes: 4 additions & 4 deletions content/v2.3/upgrade-notes/v2.2.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
title: Upgrade to 2.3
title: Upgrade to 2.2
order: 20
---

These notes cover an upgrade from 2.1 to 2.3.
These notes cover an upgrade from 2.1 to 2.2.

## Update the app

- Edit `Gemfile` and update the versions of the Hanami gems to `"~> 2.3"`.
- Edit `Gemfile` and update the versions of the Hanami gems to `"~> 2.2"`.

- Remove `gem "guard-puma"` from your `Gemfile` (this is now a dependency of hanami-reloader).

Expand All @@ -31,7 +31,7 @@ Certain steps below apply to each of your app and slices. Where the code example
- Add the following gems to your `Gemfile`:

```ruby
gem "hanami-db", "~> 2.3"
gem "hanami-db", "~> 2.2"

# If you want a SQLite database
gem "sqlite3"
Expand Down
57 changes: 57 additions & 0 deletions content/v2.3/upgrade-notes/v2.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: Upgrade to 2.3
order: 30
---

These notes cover an upgrade from 2.2 to 2.3.

## Use new formats config for actions

`Hanami::Action.format`, `Hanami::Action::Config#format`, `Hanami::Action::Config::Formats#values`, and `Hanami::Action::Config::Formats#add` are now deprecated and will be removed in Hanami 2.4.

These methods are deprecated because they did not make it clear that configuring a format would immediately restrict requests based on that format, and conflated the registration of custom MIME types with immediately restricting based on those types, and did not allow for the default response format to be independently configured. This led to unexpected behaviors and made it difficult configure formats in the app or base action classes.

The new format config API is documented in [Formats and MIME types](/v2.3/actions/formats-and-mime-types). To use it, make the following changes wherever you configure your actions (in your app class using `config.actions`, or directly in your action classes using `config` or `format`).

Change `format` and `config.format` to `config.formats.accept`. For example:

```ruby
# In app or slice classes:
#
# Before:
# config.actions.format :json
config.actions.formats.accept :json

# In an action class:
#
# Before:
# format :json
# Or:
# config.format :json
config.formats.accept :json
```

Change `config.formats.add` to `config.formats.register`. For example:

```ruby
# In app or slice classes:
#
# Before:
# config.actions.formats.add :custom, "application/custom"
config.actions.formats.register custom: "application/custom"

# In an action class:
#
# Before:
# config.formats.add :custom, "application/custom"
config.formats.register custom: "application/custom"
```

With these changes, calling `register` no longer adds the format to the list of accepted formats. You must now do this separately:

```ruby
config.formats.register custom: "application/custom"
config.formats.accept :custom
```

The benefit of this change is that you can now more easily `register` custom MIME types in an app class or base action class, and then choose to `accept` them only in specific action classes.