Skip to content

Issue161 add exception conversion#969

Closed
boillodmanuel wants to merge 6 commits intorestlet:masterfrom
boillodmanuel:issue161-add-exception-conversion
Closed

Issue161 add exception conversion#969
boillodmanuel wants to merge 6 commits intorestlet:masterfrom
boillodmanuel:issue161-add-exception-conversion

Conversation

@boillodmanuel
Copy link
Copy Markdown
Contributor

Work

Done:

  • Add exception conversion in ConverterService
  • Add content negotiation in StatusFilter
  • Replace @Status value type from String to int
  • Remove code in ServerResource

Not done:

  • Retrieve response entity in ClientResource after an error occurs in the ServerResource

Use cases explained

  • Simple request returning no entity (no content)

Not changed

curl -x PUT -H 'Accept: text/html’ ‘http://server.com/api/test'

Response headers:

HTTP/1.1 204 No Content

Response: N/A

  • Exception not annotated with @Status and with Content-Type "text/html"

Not changed

curl -x PUT -H 'Accept: text/html’ ‘http://server.com/api/exception'

Response headers:

HTTP/1.1 500 Internal Server Error
Content-type: text/html; charset=UTF-8
Content-length: 486

Response:

<html>
<head>
   <title>Status page</title>
</head>
<body style="font-family: sans-serif;">
<p style="font-size: 1.2em;font-weight: bold;margin: 1em 0px;">Internal Server Error</p>
<p>The server encountered an unexpected condition which prevented it from fulfilling the request</p>
<p>You can get technical details <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">here</a>.<br>
Please continue your visit at our <a href="/">home page</a>.
</p>
</body>
</html>
  • Exception not annotated with @Status and with Content-Type "application/json" or without Content-Type

New: Content Negotiation

curl -x PUT -H 'Accept: application/json’ ‘http://server.com/api/exception'

Response headers:

HTTP/1.1 500 Internal Server Error
Content-type: application/json

Response:

{
    "code": 500,
    "description": "The server encountered an unexpected condition which prevented it from fulfilling the request",
    "reasonPhrase": "Internal Server Error"
}
  • Exception annotated with @Status(400) and with Content-Type "text/html"

New: Status code

curl -x PUT -H 'Accept: text/html’ ‘http://server.com/api/notfound'

Response headers:

HTTP/1.1 404 Not Found
Content-type: text/html; charset=UTF-8
Content-length: 439

Response:

<html>
<head>
   <title>Status page</title>
</head>
<body style="font-family: sans-serif;">
<p style="font-size: 1.2em;font-weight: bold;margin: 1em 0px;">Not Found</p>
<p>The server has not found anything matching the request URI</p>
<p>You can get technical details <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">here</a>.<br>
Please continue your visit at our <a href="/">home page</a>.
</p>
</body>
</html>
  • Exception annotated with @Status(400) and with Content-Type "application/json" or without Content-Type

New: Status Code and Content Negotiation

curl -x PUT -H 'Accept: application/json’ ‘http://server.com/api/notfound'

Response headers:

HTTP/1.1 404 Not Found
Content-type: application/json

Response:

{
     "code":404,
     "reasonPhrase":"Not Found",
     "description":"The server has not found anything matching the request URI"
}
  • Exception annotated with @Status(value=400, serializeable=true) and with Content-Type "application/json" or without Content-Type

New: Status Code, Exception properties serialization and Content Negotiation

curl -x PUT -H 'Accept: application/json’ ‘http://server.com/api/badparameter'

Response headers:

HTTP/1.1 404 Not Found
Content-type: application/json

Response:

{
    "message": "Parameter age is not of the expected type: number. Value: male",
    "name": "age",
    "type": "number",
    "value": "male"
}

Exception class:

@Status(value = 400, serializeProperties = true)
public class BadParameterTypeException extends RuntimeException {

    private final String name;
    private final String value;
    private final String expectedType;

    public BadParameterTypeException(String name, String value, String expectedType) {
        super("Parameter " + name + " is not of the expected type: " + expectedType + ". Value: " + value);
        this.name = name;
        this.value = value;
        this.expectedType = expectedType;
    }

    @Override
    public String getMessage() {
        return super.getMessage();
    }

    public String getName() {
        return name;
    }

    public String getValue() {
        return value;
    }

    public String getExpectedType() {
        return expectedType;
    }
}

@boillodmanuel
Copy link
Copy Markdown
Contributor Author

WARNING: StatusService has been removed from GWT Edition. Need to be validated.

@boillodmanuel
Copy link
Copy Markdown
Contributor Author

Build failed because build of master failed since https://travis-ci.org/restlet/restlet-framework-java/builds/38889517

@jlouvel
Copy link
Copy Markdown
Collaborator

jlouvel commented Oct 29, 2014

Manuel, thanks it looks great overall!

Some comments:

  • can set the default value of "serializeProperties" to 'true' instead of 'false' to favor automatic conversion? I had in mind that only the presence of @Status would be necessary to trigger this behavior
  • code style: qualified names are used instead of imports (ex: StatusService)
  • why do we use a custom bean serializer (based on java.beans) instead of relying on the ConverterService directly? This would allow us to support JSON, YAML and potentially other media types, to use Jackson annotations and so on.
  • I don't understand the purpose of StatusRepresentation. We already have org.restlet.data.Status
  • why do we pass converterService, connegService & metadataService as parameters to toRepresentation(...) instead of member variables of StatusService?
  • why did we remove StatusService from GWT? Isn't it used at all by the code?
  • do we support deserialization of status back into proper client-side exception?
  • could we add a setting in StatusService for "debugMode:boolean". If set to false, we should first set those properties to null before invoking the ConverterService. Default setting should be "true"

@jlouvel
Copy link
Copy Markdown
Collaborator

jlouvel commented Oct 29, 2014

I've added a testException() method to JacksonTestCase in order to play with exception serialization and deserialization. I've also changed the default Jackson serialization setting in JacksonRepresentation to not add "null" and empty collections to the output, producing more compact representations.

Other comments:

  • we should rename "serializeProperties" into just "serialize" or "serializable" to control whether or not we autoconvert the exception into an error representation, or if we just change the HTTP error status.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you instantiating a StatusRepresentation?

@boillodmanuel
Copy link
Copy Markdown
Contributor Author

PR pushed as branch issue161-add-exception-conversion for additional work.

@boillodmanuel boillodmanuel deleted the issue161-add-exception-conversion branch October 30, 2014 17:58
@jlouvel
Copy link
Copy Markdown
Collaborator

jlouvel commented Oct 31, 2014

Just pushed those refactorings/changes:

  • Added connegService, metadataService and converterService properties to StatusService and pass them via the constructor instead of the toRepresentation and toStatus methods.
  • Renamed @Status#serializeProperties (default to false) into serialize (default to true).
  • Deprecated Component#statusService in favor of the Application#statusService property.

@jlouvel jlouvel assigned boillodmanuel and unassigned jlouvel Oct 31, 2014
@jlouvel jlouvel added this to the 2.3 M4 milestone Oct 31, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants