I am using the OAuth hooks provided with a fresh creation of an Aqueduct project. My data is URI encoded in the following way:
var form = body.keys
.map((key) => "$key=${Uri.encodeQueryComponent(body[key])}")
.join("&");
I received the following error when attempting to register a User:
DataModelException: Type mismatch for property username on _User, expected assignable type matching ManagedPropertyType.string but got _ImmutableList. #0 ManagedValueBacking.setValueForProperty
The request looks like this:
HttpRequest.request('/register', method: 'POST',
sendData: form,
requestHeaders: {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Basic $clientCredentials"
}).then((HttpRequest req) {
...
}).catchError((e) => _handleError(...));
I am just not too sure why the body is being interpreted as an ImmutableList.
I must be missing something!
The /register endpoint expects JSON data.
HttpRequest.request('/register', method: 'POST',
sendData: JSON.encode(body),
requestHeaders: {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic $clientCredentials"
});
Unfortunately, this isn't quite clear - HTTPController
defaults to allowing both JSON and form data and changing it now might break someone's code; the template could do a better job of restricting input to JSON only, though.
This specific reason for this error is that form data and query parameters can have multiple entries for the same key. Aqueduct treats form data and query parameters the same and always parses these types of inputs into lists. Since RegisterController
is a QueryController<T>
, it is expecting a JSON request body where each value is not a list.
Instead of switching to JSON, you could always modify RegisterController
to take only form data:
class RegisterController extends HTTPController {
RegisterController(this.authServer) {
acceptedContentTypes = [new ContentType("application", "x-www-form-urlencoded")];
}
AuthServer authServer;
@httpPost
Future<Response> createUser(
@HTTPQuery("username") String username,
@HTTPQuery("password") String password) async {
var query = new Query<User>()
..values.username = username
..values.password = password;
var salt = AuthUtility.generateRandomSalt();
...
Note: The endpoints for authorization do expect form data (per the OAuth 2.0 spec), but everything else in a new project expects JSON data.