Currently, I work on a project based on micro-services and web application. The product is developped with these technologies :
- Micro-service : API in ASP.NET Core
- Web Application : React and TypeScript (with axios)
During the production launch on the client servers, a few problems with different configurations occurred. In fact, on almost all the clients, all the requests returned a result
200 OK
On one particular server, requests with RESTful verbs like PUT
,PATCH
and DELETE
were returning with the following status:
405 Method not allowed
Indeed, some of them blocked any HTTP request not using the simple GET
+ POST
verbs, for security reasons.
Configuration
The product is hosted on a dedicated server at a service provider. This is behind a Nginx reverse proxy, which is global to all the provider’s servers. From then on, two solutions were available :
- Allow
PUT
,PATCH
,DELETE
requests on the reverse proxy - Adapt the micro-services with
X-Method-Override
header
As the first alternative is not possible, as the change may alter the security of the products hosted by the provider, the second alternative is chosen.
Typescript HTTP Client
The webapp is based on React and TypeScript with axios
library. The purpose is to define a custom API Client based on axios
and set the X-Method-Override
header.
Client interface
Below appears the interface of our custom API client :
interface ApiClient {
instance(): ApiClient;
get<T>(url: string): Promise<T>;
post<T>(url: string, body: any): Promise<T>;
put<T>(url: string, body: any): Promise<T>;
patch<T>(url: string, body: any): Promise<T>;
delete<T>(url: string): Promise<T>;
}
With this interface, the client will be able to use the 5 main HTTP verbs : GET
, POST
, PUT
, PATCH
and DELETE
.
The client is designed with Singleton pattern (you can notice the instance method !).
Client implementation
import Axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
type HttpVerb = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
class ApiClient {
private static singleton: ApiClient;
private readonly instanceAxios: AxiosInstance;
private URL: string = "https://localhost:44300/";
private constructor() {
this.instanceAxios = Axios.create({
baseURL: `${this.URL}api/`,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
}
public static get instance(): ApiClient {
if (!ApiClient.singleton) {
this.singleton = new ApiClient();
}
return ApiClient.singleton;
}
private async request<T>(method: HttpVerb, url: string, body?: any): Promise<T> {
const requestConfig: AxiosRequestConfig = {
method: method === "GET" ? "GET" : "POST",
url: url,
headers: {/*... */},
};
// Use X-HTTP-Method-Override to use all HTTP verbs
// Verbs will be encapsulated in POST Header
if (method !== "GET" && method !== "POST")
requestConfig.headers["X-HTTP-Method-Override"] = method;
if (body) {
requestConfig.data = body;
}
try {
const response: AxiosResponse = await this.instanceAxios(requestConfig);
return response.data as T;
} catch (error) {
throw new Error(error.response.data);
}
}
public async get<T>(url: string): Promise<T> {
try {
const response: AxiosResponse = await this.instanceAxios.get(url);
return response.data as T;
} catch (error) {
throw new Error(/*..*/);
}
}
public async post<T>(url: string, body: any): Promise<T> {
return this.request("POST", url, body);
}
public async put<T>(url: string, body: any): Promise<T> {
return this.request("PUT", url, body);
}
public async patch<T>(url: string, body?: any, ): Promise<T> {
return this.request("PATCH", url, body);
}
public delete<T>(url: string): Promise<T> {
return this.request("DELETE", url);
}
}
export default ApiClient.instance;
Now, it is easy to use this client on all our other *.ts files :
import { ApiClient} from "@services/ApiClient"
var data1 = await ApiClient.get("/myUrl");
var patchData = {
"property": "value"
};
// The PATCH method will add a X-HTTP-Method-Override header
var responseToPatch = await ApiClient.patch("/myUrl2", patchData);
Micro-services in ASP.NET Core
Now, it is important to tell to ASP.NET Core to check the X-HTTP-Method-Override
header. It is easily done with the IApplicationBuilder.UseHttpMethodOverride
method !
// All using...
namespace My.Awesome.Namespace.Api
{
public class Startup
{
// ...
public void ConfigureServices(IServiceCollection services)
{
// ...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
// ASP.NET Core API will handle X-HTTP-Method-Override header !
app.UseHttpMethodOverride();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Conclusion
If you encounter servers that do not allow HTTP verbs to comply with REST principles, don’t worry. It is possible to encapsulate REST verbs in the X-HTTP-Method-Override
header, as demonstrated in this article !
Ressources
- Illustration : Business vector created by fullvector — www.freepik.com
Top comments (0)