Http Method Spoofing & Helper Components in AdonisJS

In this lesson, we'll learn how we can utilize Http Method Spoofing to send PUT, PATCH, and DELETE requests using native HTML forms. We'll then create utility functions to make the implementation cleaner.

Published
Sep 04, 22
Duration
9m 17s

Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.

Adocasts

Burlington, KY

Native HTML only supports sending GET and POST requests with forms to the server. However, utilizing Method Spoofing with AdonisJS we can spoof POST requests to the remainder of the Http Methods, ie: PUT, PATCH, and DELETE so that AdonisJS utilizes the spoofed method to match the desired route.

In essence, if we wanted to target a PUT: /posts route, we can do so using Method Spoofing and a native HTML form.

Method Spoofing Requirements

In order to utilize Http Method Spoofing with AdonisJS a few conditions need to be met.

  1. http.allowMethodSpoofing within our config/app.ts file must be true. This is defaulted to false within new projects.

  2. Our native HTML form must use POST as its method.

  3. The route's URL must contain the spoofed method in its query string.

Turning On Method Spoofing

The most important thing to note from above if you’re looking to use Method Spoofing is that you need to enable it within your AdonisJS project. To do so, open your config/app.ts file and scroll down to the http config. allowMethodSpoofing should be one of the first properties under http, just set its value to true.

export const http: ServerConfig = {
  /*
  |--------------------------------------------------------------------------
  | Allow method spoofing
  |--------------------------------------------------------------------------
  |
  | Method spoofing enables defining custom HTTP methods using a query string
  | `_method`. This is usually required when you are making traditional
  | form requests and wants to use HTTP verbs like `PUT`, `DELETE` and
  | so on.
  |
  */
  allowMethodSpoofing: true,

  // ...
}

Method Spoofing Query String

In order to specify what Http Method we want our request spoofed to, we'll need to define it within our action URL's query string. The structure for this is ?_method=PUT, where PUT is the Http Method we want our request spoofed too.

// received by POST: /posts
<form method="POST" action="/posts"></form>

// received by PUT: /posts
<form method="POST" action="/posts?_method=PUT"></form>

// received by PATCH: /posts
<form method="POST" action="/posts?_method=PATCH"></form>

// received by DELETE: /posts
<form method="POST" action="/posts?_method=DELETE"></form>

Above the routes are hard coded for clearness, but we can also apply this using the route method as well.

// received by POST: /posts
<form 
  method="POST" 
  action="{{ route('posts.store') }}"
></form>

// received by PUT: /posts
<form 
  method="POST" 
  action="{{ 
    route('posts.update', {}, { qs: { _method: 'PUT' } }) 
  }}"
></form>

// received by PATCH: /posts
<form 
  method="POST" 
  action="{{ 
    route('posts.pubish', {}, { qs: { _method: 'PATCH' } }) 
  }}"
></form>

// received by DELETE: /posts
<form 
  method="POST" 
  action="{{ 
    route('posts.destroy', {}, { qs: { _method: 'DELETE' } }) 
  }}"
></form>

This is great, but using the power of components we can clean this up and make it a lot more readable and reusable!

Http Method Spoofing Helper Components

Now that we’re familiar with Method Spoofing and what it looks like, let’s extract these various spoofed methods into helper EdgeJS Components to make adding these to any page a breeze.

First, go ahead and create the following directory tree, if needed.

/resources/views/components/forms/

We’ll be adding all our form helper components here. By adding our EdgeJS Components inside the resources/views/components directory, we can simplify how we use our methods because AdonisJS binds these components directly to a tag name.

Here’s how we’d need to use them if we put them anywhere else:

@component('path/to/component/forms/post', {})
  {{-- form contents --}}
@end

Here’s how we can use them thanks to placing them at resources/views/components:

@forms.post({})
  {{-- form contents --}}
@end

As you can see, the latter is super concise and easy to read!

POST Helper Component

Our POST Method helper component will be the most straightforward since it’s the native HTML behavior, so we’ll start here. Let’s start by creating a file for it called post.edge.

{{-- /resources/views/components/forms/post.edge --}}

<form method="POST" action="{{ action }}">
  {{{ await $slots.main() }}}
</form>

Note: {{{ await $slots.main() }}} will render anything we put inside the component’s tags, it’s the same premise as slots in Vue or React.

Using the POST Helper Component

When it comes to using our post helper component, all we need to add is:

@forms.post({ action: route('posts.store') })
  {{-- form content --}}
@end

PUT Helper Component

From here out we’ll need to add our method spoofing designation to the action’s URL. There are several different ways to go about this. The most straightforward way I could think of was to add it directly to our action variable.

{{-- /resources/views/components/forms/put.edge --}}

@set('action', action.includes('?') 
  ? `${action}&_method=PUT` 
  : `${action}?_method=PUT`
)

<form method="POST" action="{{ action }}">
  {{{ await $slots.main() }}}
</form>

So, for our action variable we're checking to see if it already includes a query string by checking for the presence of the ? character. If it does we'll add it to the end of the query strings, otherwise, we'll start it as the query string.

Using the PUT Helper Component

Thanks to the component taking care of the spoofing for us, the usage for PUT will look as simple as our POST.

@forms.put({ action: route('posts.update') })
  {{-- form content --}}
@end

PATCH & DELETE Helper Components

For PATCH & DELETE, all we need to do is copy/paste our PUT component and update the Method Spoofing value in our action’s URL.

{{-- /resources/views/components/forms/patch.edge --}}

@set('action', action.includes('?') 
  ? `${action}&_method=PATCH` // 👈 set to PATCH
  : `${action}?_method=PATCH` // 👈 set to PATCH
)

<form method="POST" action="{{ action }}">
  {{{ await $slots.main() }}}
</form>

PATCH is now ready to go:

@forms.patch({ action: route('posts.publish') })
  {{-- form content --}}
@end

Then, do the same for DELETE

{{-- /resources/views/components/forms/delete.edge --}}

@set('action', action.includes('?') 
  ? `${action}&_method=DELETE` // 👈 set to DELETE
  : `${action}?_method=DELETE` // 👈 set to DELETE
)

<form method="POST" action="{{ action }}">
  {{{ await $slots.main() }}}
</form>

DELETE is now ready to go:

@forms.delete({ action: route('posts.destroy') })
  {{-- form content --}}
@end

Join The Discussion! (0 Comments)

Please sign in or sign up for free to join in on the dicussion.

robot comment bubble

Be the first to Comment!