Skip to content

Commit

Permalink
Merge fix-koa-changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Sep 17, 2020
2 parents 99a5091 + d34da0a commit 8bec215
Show file tree
Hide file tree
Showing 53 changed files with 537 additions and 335 deletions.
59 changes: 21 additions & 38 deletions docs/docs/exceptions.md
Expand Up @@ -68,17 +68,13 @@ These are exposed from the @tsed/exceptions package, and represent many of the m

## Exception filter

All errors are intercepted by the @@PlatformExceptionMiddleware@@ since v5.64.0+, and by @@GlobalErrorHandlerMiddleware@@ before.
All errors are intercepted by the @@PlatformExceptionMiddleware@@.

By default, all HTTP Exceptions are automatically sent to the client, and technical errors are
sent as Internal Server Error.

The default format is an HTML content, but it couldn't be useful for your consumers.

### with Catch decorator <Badge text="v5.64.0+" />

The new [Platform API](/docs/platform-api.md) introduce a new way to catch an exception with the @@Catch@@ decorator and
to let you control the exact flow of control and the content of the response sent back to the client.
The [Platform API](/docs/platform-api.md) provide @@Catch@@ decorator to catch error.
It let you control the exact flow of control and the content of the response sent back to the client.

Let's create an exception filter that is responsible for catching exceptions which are an instance of the @@Exception@@ class,
and implementing custom response logic for them.
Expand All @@ -100,43 +96,30 @@ If you want to catch all errors, just use the @@Catch@@ decorator with the `Erro

<<< @/docs/docs/snippets/exceptions/error-filter.ts

### With legacy GlobalErrorHandlerMiddleware <Badge type="warning" text="deprecated" />

You can register your own exception handler and change the response sent to your consumers.

<<< @/docs/docs/snippets/exceptions/custom-global-error-handler.ts
## 404 ResourceNotFound

Then you just have adding this middleware in your `Server.ts` as following:
Ts.ED throw a @@ResourceNotFound@@ error when nothing routes are resolved by the router.
By using Exception filter, is now possible to manage this error and customize the
response sent to your consumer.

<<< @/docs/docs/snippets/exceptions/custom-global-error-handler-usage.ts
Create a new ResourceNotFoundFilter in the filters directories and copy/paste this example:

### Migrate to Exception filter
<<< @/docs/docs/snippets/exceptions/resource-not-found-filter.ts

Exception filter and GlobalErrorHandlerMiddleware can be used at the same time excepted if you register your own middleware
via the $afterRoutesInit hook.

To facilitate your migration, remove the line where you add you custom middleware in the server:

```typescript
$afterRoutesInit() {
this.app.use(CustomGlobalErrorHandlerMiddleware); // remove this
}
```
::: warning
`response.render()` require to configure the template engine before. See our page over [Templating engine](/tutorials/templating.html#installation) installation for more details.
:::

Then, use the @@OverrideProvider@@ decorator over your custom middleware:
Then import the custom filter in your server:

```typescript
import {OverrideProvider} from "@tsed/di";
import {GlobalErrorHandlerMiddleware} from "@tsed/common";

@OverrideProvider(GlobalErrorHandlerMiddleware)
export class CustomGlobalErrorHandlerMiddleware extends GlobalErrorHandlerMiddleware {

import {Inject} from "@tsed/di";
import {Configuration, PlatformApplication} from "@tsed/common";
import "./filters/ResourceNotFoundFilter"; // Importing filter with ES6 import is enough

@Configuration({
// ...
})
export class Server {
}
```

Now you are able to create your own exception filter. Start with the HttpException example:

<<< @/docs/docs/snippets/exceptions/http-exception-filter.ts

Then try with another error type and finally, remove your custom middleware.
13 changes: 0 additions & 13 deletions docs/docs/snippets/exceptions/custom-global-error-handler-usage.ts

This file was deleted.

12 changes: 0 additions & 12 deletions docs/docs/snippets/exceptions/custom-global-error-handler.ts

This file was deleted.

23 changes: 23 additions & 0 deletions docs/docs/snippets/exceptions/resource-not-found-filter.ts
@@ -0,0 +1,23 @@
import {Catch, ExceptionFilterMethods, PlatformContext, ResourceNotFound} from "@tsed/common";

@Catch(ResourceNotFound)
export class ResourceNotFoundFilter implements ExceptionFilterMethods {
async catch(exception: ResourceNotFound, ctx: PlatformContext) {
const {response} = ctx;

const obj = {
status: exception.status,
message: exception.message,
url: exception.url
};
// Json response
response
.status(exception.status)
.body(obj);

// Or with ejs/handlers/etc...
await response
.status(exception.status)
.render("404.ejs", obj);
}
}
32 changes: 32 additions & 0 deletions docs/migration.md
@@ -0,0 +1,32 @@



## GlobalErrorHandler middleware to Exception filter

[Exception filter]() is the

To facilitate your migration, remove the line where you add you custom middleware in the server:

```typescript
$afterRoutesInit() {
this.app.use(CustomGlobalErrorHandlerMiddleware); // remove this
}
```

Then, use the @@OverrideProvider@@ decorator over your custom middleware:

```typescript
import {OverrideProvider} from "@tsed/di";
import {GlobalErrorHandlerMiddleware} from "@tsed/common";

@OverrideProvider(GlobalErrorHandlerMiddleware)
export class CustomGlobalErrorHandlerMiddleware extends GlobalErrorHandlerMiddleware {

}
```

Now you are able to create your own exception filter. Start with the HttpException example:

<<< @/docs/docs/snippets/exceptions/http-exception-filter.ts

Then try with another error type and finally, remove your custom middleware.
33 changes: 11 additions & 22 deletions docs/tutorials/not-found-page.md
@@ -1,40 +1,29 @@
# Customize 404

The guide shows you how you can customize the 404 response error emitted by Express.js.
The guide shows you how you can customize the 404 response error when a resource or route isn't resolved by
the router.

To begin, create a new Middleware named `NotFoundMiddleware`:
Customizing error is possible by using the [Exception filter feature](/docs/exceptions.html#exception-filter) and by catching
the @@ResourceNotFound@@ error class. This error is thrown by Ts.ED when nothing routes are resolved.

```typescript
import {Middleware, Res, Next} from "@tsed/common";
Create a new ResourceNotFoundFilter in the filters directories and copy/paste this example:

@Middleware()
export class NotFoundMiddleware {
use(@Res() response: Res, @Next() next: Next) {
// Json response
response.status(404).json({status: 404, message: 'Not found'});
<<< @/docs/docs/snippets/exceptions/resource-not-found-filter.ts

// Or with ejs
response.status(404).render("404.ejs", {}, next);
}
}
```
::: warning
`response.render()` require to configure the template engine before. See our page over [Templating engine](/tutorials/templating.html#installation) installation for more details.
:::

Then register your middleware on the `$afterRoutesInit` in your Server:
Then import the custom filter in your server:

```typescript
import {Inject} from "@tsed/di";
import {Configuration, PlatformApplication} from "@tsed/common";
import {NotFoundMiddleware} from "./middlewares/NotFoundMiddleware";
import "./filters/ResourceNotFoundFilter"; // Importing filter with ES6 import is enough

@Configuration({
// ...
})
export class Server {
@Inject()
app: PlatformApplication;

public $afterRoutesInit() {
this.app.use(NotFoundMiddleware);
}
}
```
4 changes: 2 additions & 2 deletions packages/common/src/config/services/PlatformConfiguration.ts
Expand Up @@ -2,7 +2,7 @@ import {Env} from "@tsed/core";
import {DIConfiguration, Injectable, ProviderScope, ProviderType} from "@tsed/di";
import {$log} from "@tsed/logger";
import * as Https from "https";
import {PlatformLoggerSettings, EndpointDirectoriesSettings} from "../interfaces";
import {EndpointDirectoriesSettings, PlatformLoggerSettings} from "../interfaces";
import {ConverterSettings} from "../interfaces/ConverterSettings";

const rootDir = process.cwd();
Expand Down Expand Up @@ -123,7 +123,7 @@ export class PlatformConfiguration extends DIConfiguration {
}

get acceptMimes(): string[] {
return this.getRaw("acceptMimes") || ["application/json"];
return this.getRaw("acceptMimes");
}

set acceptMimes(value: string[]) {
Expand Down
14 changes: 0 additions & 14 deletions packages/common/src/mvc/models/EndpointMetadata.ts
Expand Up @@ -117,20 +117,6 @@ export class EndpointMetadata extends JsonEntityStore implements EndpointConstru
});
}

/**
* @deprecated Will be removed in v6
*/
get contentType(): string {
return this.store.get("contentType") as string;
}

/**
* @deprecated Will be removed in v6
*/
set contentType(url: string) {
this.store.set("contentType", url);
}

/**
* Get all endpoints from a given class and his parents.
* @param {Type<any>} target
Expand Down
Expand Up @@ -7,16 +7,13 @@ import {
BeforeRoutesInit,
Controller,
InjectorService,
OnReady,
PlatformContext
OnReady
} from "@tsed/common";
import {Type} from "@tsed/core";
import {Configuration} from "@tsed/di";
import {expect} from "chai";
import {join} from "path";
import * as Sinon from "sinon";
import {FakeRequest, FakeResponse} from "../../../../../test/helper";
import {stub} from "../../../../../test/helper/tools";
import {Platform} from "../../platform/services/Platform";
import {PlatformBuilder} from "./PlatformBuilder";

Expand Down Expand Up @@ -48,7 +45,8 @@ describe("PlatformBuilder", () => {
},
mount: {
"/rest": [RestCtrl]
}
},
acceptMimes: ["application/json"]
})
class ServerModule implements BeforeInit, AfterInit, BeforeRoutesInit, AfterRoutesInit, BeforeListen, AfterListen, OnReady {
$beforeRoutesInit(): void | Promise<any> {
Expand Down
@@ -1,4 +1,4 @@
import {classOf, constructorOf, Type} from "@tsed/core";
import {classOf, constructorOf, nameOf, Type} from "@tsed/core";
import {Container, InjectorService, IProvider} from "@tsed/di";
import {
GlobalAcceptMimesMiddleware,
Expand Down Expand Up @@ -36,6 +36,7 @@ export interface PlatformType<T = any> extends Type<T> {
*/
export abstract class PlatformBuilder {
protected startedAt = new Date();
protected PLATFORM_NAME: string = "";
protected _rootModule: any;
protected _injector: InjectorService;
protected locals: Container;
Expand Down Expand Up @@ -97,6 +98,7 @@ export abstract class PlatformBuilder {

static build<T extends PlatformBuilder>(platformBuildClass: PlatformType<T>): T {
const platform = new platformBuildClass();
platform.PLATFORM_NAME = nameOf(platformBuildClass).replace("Platform", "").toLowerCase();

return platform.useProviders(platformBuildClass.providers || []);
}
Expand Down Expand Up @@ -216,7 +218,10 @@ export abstract class PlatformBuilder {
}

protected async bootstrap(module: Type<any>, settings: Partial<TsED.Configuration> = {}) {
this.createInjector(module, settings);
this.createInjector(module, {
...settings,
PLATFORM_NAME: this.PLATFORM_NAME
});
this.createRootModule(module);

await this.runLifecycle();
Expand Down
Expand Up @@ -20,6 +20,7 @@ export class ErrorFilter implements ExceptionFilterMethods {
})
.setHeaders(this.getHeaders(error))
.status(err.status)
.contentType("application/json")
.body(env === Env.PROD ? "InternalServerError" : err);
}

Expand Down
Expand Up @@ -8,12 +8,11 @@ export class ExceptionFilter extends ErrorFilter {
catch(error: Exception, ctx: PlatformContext) {
const {response, logger, env} = ctx;
const err = this.mapError(error, env);

logger.error({
error: err,
stack: error.stack
});

response.setHeaders(this.getHeaders(error)).status(error.status).body(err);
response.setHeaders(this.getHeaders(error)).contentType("application/json").status(error.status).body(err);
}
}
11 changes: 11 additions & 0 deletions packages/common/src/platform-exceptions/errors/ResourceNotFound.ts
@@ -0,0 +1,11 @@
import {NotFound} from "@tsed/exceptions";

export class ResourceNotFound extends NotFound {
readonly url: string;

constructor(url: string) {
super(`Resource "${url}" not found`);

this.url = url;
}
}
3 changes: 3 additions & 0 deletions packages/common/src/platform-exceptions/index.ts
Expand Up @@ -10,3 +10,6 @@ export * from "./interfaces/ExceptionFilterMethods";

// middlewares
export * from "./services/PlatformExceptions";

// errors
export * from "./errors/ResourceNotFound";
Expand Up @@ -18,6 +18,7 @@ describe("PlatformExceptions", () => {
sandbox.stub(ctx.response, "body").returnsThis();
sandbox.stub(ctx.response, "setHeaders").returnsThis();
sandbox.stub(ctx.response, "status").returnsThis();
sandbox.stub(ctx.response, "contentType").returnsThis();

const error = "MyError";

Expand All @@ -33,6 +34,7 @@ describe("PlatformExceptions", () => {
sandbox.stub(ctx.response, "body").returnsThis();
sandbox.stub(ctx.response, "setHeaders").returnsThis();
sandbox.stub(ctx.response, "status").returnsThis();
sandbox.stub(ctx.response, "contentType").returnsThis();

const origin = new ValidationError("wrong ID", [
{
Expand Down Expand Up @@ -69,6 +71,7 @@ describe("PlatformExceptions", () => {
sandbox.stub(ctx.response, "body").returnsThis();
sandbox.stub(ctx.response, "setHeaders").returnsThis();
sandbox.stub(ctx.response, "status").returnsThis();
sandbox.stub(ctx.response, "contentType").returnsThis();

class Custom extends Error {}

Expand Down Expand Up @@ -101,6 +104,7 @@ describe("PlatformExceptions", () => {
sandbox.stub(ctx.response, "body").returnsThis();
sandbox.stub(ctx.response, "setHeaders").returnsThis();
sandbox.stub(ctx.response, "status").returnsThis();
sandbox.stub(ctx.response, "contentType").returnsThis();

const error = new Error("My message");

Expand Down

0 comments on commit 8bec215

Please sign in to comment.