question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Specified non-successful status codes should throw HttpOperationException<> with specific schema

See original GitHub issue

I have a controller called UserController with the following method:

[HttpPost]
[ResponseType(typeof(UserResource))]
[Route("api/v1/users")]
[ValidateRequest]
public async Task<HttpResponseMessage> AddUser(AddUserInput body) { }

ValidateRequest is my own attribute that returns a 400 Bad Request or a 422 Unprocessable Entity based on if the body parameter is null or the model state is invalid, respectively. They each provide their own resource in the response (ErrorResponse and ValidationResponse, respectively). I’ve configured Swashbuckle to generate the following Swagger json file:

"/api/v1/users": {
  "post": {
    "tags": [
      "User"
    ],
    "summary": "Adds a user.",
    "operationId": "User_AddUser",
    "consumes": [
      "application/json",
      "text/json"
    ],
    "produces": [
      "application/json",
      "text/json"
    ],
    "parameters": [
      {
        "name": "body",
        "in": "body",
        "description": "The body of the request.",
        "required": true,
        "schema": {
          "$ref": "#/definitions/AddUserInput"
        }
      }
    ],
    "responses": {
      "201": {
        "description": "The user was added successfully.",
        "schema": {
          "$ref": "#/definitions/UserResource"
        }
      },
      "400": {
        "description": "Request body is either empty or could not be properly parsed.",
        "schema": {
          "$ref": "#/definitions/ErrorResource"
        }
      },
      "422": {
        "description": "\r\nRequest body contains invalid fields. An error code will specify what is wrong with\r\nthe field. The possible validation error codes are:\r\n\r\n<table class='status-code-table'>\r\n<thead>\r\n    <tr>\r\n        <th>Code</th>\r\n        <th>Description</th>\r\n    </tr>\r\n</thead>\r\n<tbody>\r\n    <tr>\r\n        <td><code>missing_field</code></td>\r\n        <td>This means a required field on a resource has not been set.</td>\r\n    </tr>\r\n    <tr>\r\n        <td><code>invalid</code></td>\r\n        <td>This means the formatting of a field is invalid.</td>\r\n    </tr>\r\n    <tr>\r\n        <td><code>already_exists</code></td>\r\n        <td>\r\n            This means another resource has the same value as this field. This can happen in\r\n            resources that must have some unique key (such as Label names).\r\n        </td>\r\n    </tr>\r\n</tbody>\r\n</table>",
        "schema": {
          "$ref": "#/definitions/ValidationResource"
        }
      },
      "default": {
        "schema": {
          "$ref": "#/definitions/ErrorResource"
        }
      }
    },
    "deprecated": false
  }
}

My expectation is that the AutoRest generator would generate the following method signature:

public async Task<HttpOperationResponse<UserResource>> AddUserWithOperationResponseAsync(AddUserInput body, CancellationToken cancellationToken = default(System.Threading.CancellationToken)) {}

My other expectation is that if a non-successful status code is returned from the REST API, that this method would throw either a HttpOperationException<ErrorResource> or a HttpOperationException<ValidationResource>. Unfortunately, since I’ve specified that both 400 Bad Request and 422 Unprocessable Entity are possible responses, the generator thinks that they are valid (i.e. successful) operations so it’s generating this method signature:

public async Task<HttpOperationResponse<object>> AddUserWithOperationResponseAsync(AddUserInput body, CancellationToken cancellationToken = default(System.Threading.CancellationToken)) {}

My issues with this:

  • The client loses all strongly typing of UserResource when it really is a successful call (i.e. 201 Created). I understand that if there are multiple success status codes with different resource types (schemas), then returning object makes sense.
  • Not only does a developer using the client have to wrap a try/catch around the operation to catch HttpOperationException<ErrorResource> because it’s the default response, but they now also have to write their own logic when the method doesn’t throw an exception in order to figure out what the response code is and then deserialize the response’s content accordingly. It would be much easier if the developer could just catch HttpOperationException<ErrorResource> and HttpOperationException<ValidationResource> separately. And if the method returns without exception, they could assume they have a valid UserResource object.

Can the generator be changed (or an option added) so that all non-successful status codes returned from REST API throw a strongly-typed HttpOperationException<> based on the schema?

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
stevenkuhncommented, Apr 13, 2015

@matt-gibbs Thanks for the quick reply, Matt. I added those error responses to my Swagger file because I want the users of my API to know specifically what a specific error looks like (an unhandled exception error will be different than a validation error). I read that section of the specification as well and found it vague concerning errors. Quoting from the swagger specification:

However, it is expected from the documentation to cover a successful operation response and any known errors.

I personally assumed that this meant there would only be one successful response and that all other defined responses would be errors. But I think treating any defined 2xx responses as success and 4xx and 5xx as errors would be the best option. There could be an argument whether 1xx and 3xx should be treated as successes or errors.

The default can be used a default response object for all HTTP codes that are not covered individually by the specification.

The Responses Object MUST contain at least one response code, and it SHOULD be the response for a successful operation call.

I think throwing for default is great and definitely something worth preserving. What I would like to suggest is the following:

  1. Keep the default response the way it is (in my case throwing HttpOperationException<ErrorResource>).
  2. If a defined Swagger response is 2xx, then return the referenced schema (in my case, UserResource).
  3. If there are multiple 2xx responses defined with _different_ schemas, then return object.
  4. If a defined Swagger response is 4xx or 5xx then throw HttpOperationException<> with the referenced schema (in my case HttpOperationException<ErrorResource> for 400 and HttpOperationException<ValidationResource> for 422). You already generate separate code paths for different “successful” status codes, so something similar could be done for different “error” status codes. A common base type for errors or adding vendor extensions should not be necessary.
  5. If a defined Swagger response is 4xx or 5xx and a schema _is not_ provided, then throw HttpOperationException (non-generic).

On another note, awesome work on this this project! I’ve been able to automatically generate and build an API client project using the OWIN TestServer (to generate the swagger file) and AutoRest.exe (to generate the client code). I then reference that client project in my integration tests to test against my API locally. It’s been working great! My suggestions above would help me with testing the appropriate error responses.

0reactions
nour-scommented, Apr 7, 2020

Hello guys, sorry to comment on old issue, but I couldn’t find any reference for this to a recent issue. Has this been resolved?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Specified non-successful status codes should throw ... - GitHub
If a defined Swagger response is 4xx or 5xx then throw HttpOperationException<> with the referenced schema (in my case ...
Read more >
Status and error codes (REST API) - Azure Storage
Status and error codes for Azure Storage REST API operations. ... HTTP status codes, as defined in the HTTP/1.1 Status Code Definitions.
Read more >
REST API Azure Error: 'Microsoft.Rest.HttpOperationException ...
Azure source code CheckResponseStatusCodeFailed function, we may know that if the httpStatusCode. ... Then it will throw the exception.
Read more >
Azure SDK for Python Documentation - Read the Docs
handler_schema (str) – the schema defined by publisher, where extension consumers should provide settings in a matching schema.
Read more >
Orchestrator schedule failure Microsoft.Rest ... - UiPath Forum
HttpOperationException : Operation returned an invalid status code 'InternalServerError' at UiPath.Orchestrator.Client.Releases.d__5.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found