Skip to content

Commit

Permalink
Add ResponseFormatException
Browse files Browse the repository at this point in the history
- Improve error messaging when response body cannot be decoded/encoded
  • Loading branch information
evanweible-wf committed Jun 3, 2016
1 parent b378fc4 commit 6206467
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 4 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [2.4.1](https://github.com/Workiva/w_transport/compare/2.4.0...2.4.1)
_TBD_

- **Error Messaging:** When a response body cannot be properly decoded/encoded
using the `Encoding` dictated by the `content-type` header, a
`ResponseFormatException` will now be thrown with a much more descriptive
message. The content-type, encoding, and body will be included.

## [2.4.0](https://github.com/Workiva/w_transport/compare/2.3.2...2.4.0)
_May 4, 2016_

Expand Down
15 changes: 13 additions & 2 deletions lib/src/http/http_body.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'dart:typed_data';

import 'package:http_parser/http_parser.dart' show MediaType;

import 'package:w_transport/src/http/response_format_exception.dart';
import 'package:w_transport/src/http/utils.dart' as http_utils;

abstract class BaseHttpBody {
Expand Down Expand Up @@ -82,15 +83,25 @@ class HttpBody extends BaseHttpBody {
/// Returns this request/response body as a list of bytes.
Uint8List asBytes() {
if (_bytes == null) {
_bytes = new Uint8List.fromList(encoding.encode(_body));
var encoded;
try {
encoded = encoding.encode(_body);
} catch (e) {
throw new ResponseFormatException(contentType, encoding, body: _body);
}
_bytes = new Uint8List.fromList(encoded);
}
return _bytes;
}

/// Returns this request/response body as a String.
String asString() {
if (_body == null) {
_body = encoding.decode(_bytes);
try {
_body = encoding.decode(_bytes);
} catch (e) {
throw new ResponseFormatException(contentType, encoding, bytes: _bytes);
}
}
return _body;
}
Expand Down
5 changes: 3 additions & 2 deletions lib/src/http/request_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ class RequestException implements Exception {
/// URL of the attempted/unsuccessful request.
final Uri uri;

/// Construct a new instance of [WHttpException] using information from
/// Construct a new instance of [RequestException] using information from
/// an HTTP request and response.
RequestException(this.method, this.uri, this.request, this.response,
[this.error]);

/// Descriptive error message that includes the request method & URL and the response status.
/// Descriptive error message that includes the request method & URL and the
/// response status.
String get message {
String msg;
if (request != null && request.autoRetry.numAttempts > 1) {
Expand Down
63 changes: 63 additions & 0 deletions lib/src/http/response_format_exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2015 Workiva Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

library w_transport.src.http.response_exception;

import 'dart:convert';
import 'dart:typed_data';

import 'package:http_parser/http_parser.dart' show MediaType;

/// An exception that is raised when a response to a request returns with a body
/// that cannot successfully be encoded or decoded based on the expected
/// content-type.
class ResponseFormatException implements Exception {
final String body;

final Uint8List bytes;

final MediaType contentType;

final Encoding encoding;

/// Construct a new instance of [ResponseFormatException] using information
/// from the body of the response.
ResponseFormatException(this.contentType, this.encoding,
{this.body, this.bytes});

/// Descriptive error message that includes the content-type, encoding, as
/// well as the string or bytes that could not be encoded or decoded,
/// respectively.
String get message {
String description;
String bodyLine;
if (body != null) {
description = 'Body could not be encoded.';
bodyLine = 'Body: $body';
} else {
description = 'Bytes could not be decoded.';
bodyLine = 'Bytes: $bytes';
}

String msg = description;
msg += '\n\tContent-Type: $contentType';
msg += '\n\tEncoding: ${encoding.name}';
msg += '\n\t$bodyLine';

return msg;
}

@override
String toString() => 'ResponseFormatException: $message';
}
2 changes: 2 additions & 0 deletions lib/w_transport.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ export 'package:w_transport/src/http/requests.dart'
show FormRequest, JsonRequest, MultipartRequest, Request, StreamedRequest;
export 'package:w_transport/src/http/response.dart'
show BaseResponse, Response, StreamedResponse;
export 'package:w_transport/src/http/response_format_exception.dart'
show ResponseFormatException;

export 'package:w_transport/src/web_socket/w_socket.dart' show WSocket;
export 'package:w_transport/src/web_socket/w_socket_close_event.dart'
Expand Down
37 changes: 37 additions & 0 deletions test/unit/http/http_body_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,43 @@ void main() {
new HttpBody.fromBytes(contentType, ASCII.encode('body'));
expect(body.asString(), equals('body'));
});

test('should throw ResponseFormatException if body cannot be encoded',
() {
MediaType contentType =
new MediaType('application', 'json', {'charset': ASCII.name});
HttpBody body = new HttpBody.fromString(contentType, 'bodyçå®');
var exception;
try {
body.asBytes();
} catch (e) {
exception = e;
}
expect(exception, isNotNull,
reason: 'should throw if body cannot be encoded');
expect(exception, new isInstanceOf<ResponseFormatException>(),
reason:
'should throw ResponseFormatException if body cannot be encoded');
});

test('should throw ResponseFormatException if bytes cannot be decoded',
() {
MediaType contentType =
new MediaType('application', 'json', {'charset': ASCII.name});
HttpBody body =
new HttpBody.fromBytes(contentType, UTF8.encode('bodyçå®'));
var exception;
try {
body.asString();
} catch (e) {
exception = e;
}
expect(exception, isNotNull,
reason: 'should throw if bytes cannot be decoded');
expect(exception, new isInstanceOf<ResponseFormatException>(),
reason:
'should throw ResponseFormatException if bytes cannot be decoded');
});
});

group('StreamedHttpBody', () {
Expand Down

0 comments on commit 6206467

Please sign in to comment.