0
# Action Results
1
2
Comprehensive set of action result classes for structured HTTP responses. Implements the action result pattern for type-safe response handling with content negotiation support and automatic HTTP response generation.
3
4
## Capabilities
5
6
### Core Result Interface
7
8
Base interface that all action results implement for consistent response handling.
9
10
```typescript { .api }
11
/**
12
* Interface for HTTP action results that can be executed to produce HTTP responses
13
*/
14
interface IHttpActionResult {
15
/** Executes the action result to produce an HTTP response message */
16
executeAsync(): Promise<HttpResponseMessage>;
17
}
18
```
19
20
### HTTP Response Message
21
22
Core response message class containing status, headers, and content.
23
24
```typescript { .api }
25
/**
26
* HTTP response message containing status code, headers, and content
27
*/
28
class HttpResponseMessage {
29
/** Response content */
30
content: HttpContent;
31
/** HTTP response headers */
32
headers: OutgoingHttpHeaders;
33
/** HTTP status code */
34
statusCode: number;
35
36
/**
37
* Creates a new HTTP response message
38
* @param statusCode - HTTP status code (defaults to 200)
39
*/
40
constructor(statusCode?: number);
41
}
42
```
43
44
### Success Result Classes
45
46
Action results for successful HTTP responses (2xx status codes).
47
48
```typescript { .api }
49
/**
50
* Returns a 200 OK response without content
51
*/
52
class OkResult implements IHttpActionResult {
53
executeAsync(): Promise<HttpResponseMessage>;
54
}
55
56
/**
57
* Returns a 200 OK response with negotiated content
58
*/
59
class OkNegotiatedContentResult<T> implements IHttpActionResult {
60
constructor(content: T);
61
executeAsync(): Promise<HttpResponseMessage>;
62
}
63
64
/**
65
* Returns a 201 Created response with location and content
66
*/
67
class CreatedNegotiatedContentResult<T> implements IHttpActionResult {
68
constructor(location: string | URL, content: T);
69
executeAsync(): Promise<HttpResponseMessage>;
70
}
71
72
/**
73
* Returns a JSON response with custom status code
74
*/
75
class JsonResult implements IHttpActionResult {
76
readonly json: unknown;
77
readonly statusCode: number;
78
79
constructor(json: unknown, statusCode: number);
80
executeAsync(): Promise<HttpResponseMessage>;
81
}
82
83
/**
84
* Returns a streaming response
85
*/
86
class StreamResult implements IHttpActionResult {
87
constructor(readableStream: Readable, contentType: string, statusCode?: number);
88
executeAsync(): Promise<HttpResponseMessage>;
89
}
90
```
91
92
### Error Result Classes
93
94
Action results for error HTTP responses (4xx and 5xx status codes).
95
96
```typescript { .api }
97
/**
98
* Returns a 400 Bad Request response
99
*/
100
class BadRequestResult implements IHttpActionResult {
101
executeAsync(): Promise<HttpResponseMessage>;
102
}
103
104
/**
105
* Returns a 400 Bad Request response with error message
106
*/
107
class BadRequestErrorMessageResult implements IHttpActionResult {
108
constructor(message: string);
109
executeAsync(): Promise<HttpResponseMessage>;
110
}
111
112
/**
113
* Returns a 404 Not Found response
114
*/
115
class NotFoundResult implements IHttpActionResult {
116
executeAsync(): Promise<HttpResponseMessage>;
117
}
118
119
/**
120
* Returns a 409 Conflict response
121
*/
122
class ConflictResult implements IHttpActionResult {
123
executeAsync(): Promise<HttpResponseMessage>;
124
}
125
126
/**
127
* Returns a 500 Internal Server Error response
128
*/
129
class InternalServerErrorResult implements IHttpActionResult {
130
executeAsync(): Promise<HttpResponseMessage>;
131
}
132
133
/**
134
* Returns a 500 Internal Server Error response with exception details
135
*/
136
class ExceptionResult implements IHttpActionResult {
137
constructor(error: Error);
138
executeAsync(): Promise<HttpResponseMessage>;
139
}
140
```
141
142
### Specialized Result Classes
143
144
Action results for specialized HTTP responses and redirects.
145
146
```typescript { .api }
147
/**
148
* Returns a redirect response (302 Found by default)
149
*/
150
class RedirectResult implements IHttpActionResult {
151
constructor(uri: string | URL);
152
executeAsync(): Promise<HttpResponseMessage>;
153
}
154
155
/**
156
* Returns a response with custom status code
157
*/
158
class StatusCodeResult implements IHttpActionResult {
159
constructor(statusCode: number);
160
executeAsync(): Promise<HttpResponseMessage>;
161
}
162
163
/**
164
* Returns a response using a custom HTTP response message
165
*/
166
class ResponseMessageResult implements IHttpActionResult {
167
constructor(message: HttpResponseMessage);
168
executeAsync(): Promise<HttpResponseMessage>;
169
}
170
```
171
172
**Usage Examples:**
173
174
```typescript
175
import { controller, httpGet, httpPost, BaseHttpController } from "inversify-express-utils";
176
import {
177
JsonResult,
178
RedirectResult,
179
StreamResult,
180
BadRequestErrorMessageResult
181
} from "inversify-express-utils";
182
183
@controller("/files")
184
class FileController extends BaseHttpController {
185
@httpGet("/:id/download")
186
async downloadFile(@requestParam("id") id: string) {
187
const file = await this.fileService.getFile(id);
188
189
if (!file) {
190
return new NotFoundResult();
191
}
192
193
const stream = this.fileService.createReadStream(file.path);
194
return new StreamResult(stream, file.mimeType, 200);
195
}
196
197
@httpPost("/upload")
198
async uploadFile(@requestBody() fileData: any) {
199
if (!fileData || !fileData.name) {
200
return new BadRequestErrorMessageResult("File name is required");
201
}
202
203
try {
204
const uploadedFile = await this.fileService.upload(fileData);
205
const location = `/files/${uploadedFile.id}`;
206
return new CreatedNegotiatedContentResult(location, uploadedFile);
207
} catch (error) {
208
return new ExceptionResult(error);
209
}
210
}
211
212
@httpGet("/stats")
213
async getStats(@queryParam("format") format: string) {
214
const stats = await this.fileService.getStats();
215
216
if (format === "json") {
217
return new JsonResult(stats, 200);
218
}
219
220
return new OkNegotiatedContentResult(stats);
221
}
222
223
@httpGet("/:id/redirect-to-cdn")
224
async redirectToCdn(@requestParam("id") id: string) {
225
const file = await this.fileService.getFile(id);
226
227
if (!file) {
228
return new NotFoundResult();
229
}
230
231
const cdnUrl = `https://cdn.example.com/files/${file.hash}`;
232
return new RedirectResult(cdnUrl);
233
}
234
235
@httpPost("/custom-response")
236
async customResponse() {
237
// Create custom HTTP response message
238
const response = new HttpResponseMessage(202);
239
response.headers["X-Processing-Id"] = "12345";
240
response.headers["Retry-After"] = "30";
241
response.content = new JsonContent({
242
message: "File processing started",
243
status: "queued"
244
});
245
246
return new ResponseMessageResult(response);
247
}
248
}
249
```
250
251
### Content Classes
252
253
HTTP content classes used by action results for response content handling.
254
255
```typescript { .api }
256
/**
257
* Abstract base class for HTTP content
258
*/
259
abstract class HttpContent {
260
/** Content headers */
261
readonly headers: OutgoingHttpHeaders;
262
263
/** Reads the content asynchronously */
264
abstract readAsync(): Promise<unknown>;
265
}
266
267
/**
268
* JSON content implementation
269
*/
270
class JsonContent extends HttpContent {
271
constructor(json: unknown);
272
readAsync(): Promise<string>;
273
}
274
275
/**
276
* String content implementation
277
*/
278
class StringContent extends HttpContent {
279
constructor(content: string);
280
readAsync(): Promise<string>;
281
}
282
283
/**
284
* Stream content implementation
285
*/
286
class StreamContent extends HttpContent {
287
constructor(stream: Readable, mediaType: string);
288
readAsync(): Promise<Readable>;
289
}
290
```
291
292
**Usage Examples:**
293
294
```typescript
295
import { HttpResponseMessage, JsonContent, StringContent } from "inversify-express-utils";
296
297
@controller("/content")
298
class ContentController extends BaseHttpController {
299
@httpGet("/custom-json")
300
async customJson() {
301
const response = new HttpResponseMessage(200);
302
response.content = new JsonContent({
303
data: "custom data",
304
timestamp: Date.now()
305
});
306
response.headers["Cache-Control"] = "no-cache";
307
308
return this.responseMessage(response);
309
}
310
311
@httpGet("/custom-text")
312
async customText() {
313
const response = new HttpResponseMessage(200);
314
response.content = new StringContent(
315
"Custom plain text response",
316
"text/plain; charset=utf-8"
317
);
318
319
return this.responseMessage(response);
320
}
321
322
@httpGet("/xml-data")
323
async xmlData() {
324
const xmlData = "<root><item>data</item></root>";
325
const response = new HttpResponseMessage(200);
326
response.content = new StringContent(xmlData, "application/xml");
327
328
return this.responseMessage(response);
329
}
330
}
331
```
332
333
### Status Codes
334
335
The library uses standard HTTP status codes from the `http-status-codes` package for consistency.
336
337
```typescript { .api }
338
// Common status codes used by action results
339
const StatusCodes = {
340
OK: 200,
341
CREATED: 201,
342
ACCEPTED: 202,
343
NO_CONTENT: 204,
344
FOUND: 302,
345
BAD_REQUEST: 400,
346
UNAUTHORIZED: 401,
347
FORBIDDEN: 403,
348
NOT_FOUND: 404,
349
CONFLICT: 409,
350
INTERNAL_SERVER_ERROR: 500
351
};
352
```