redstone-dart / redstone

A metadata driven microframework for Dart.
http://redstone-dart.github.io/redstone
MIT License
342 stars 42 forks source link

Mapper Documentation #38

Closed RobertBlackhart closed 10 years ago

RobertBlackhart commented 10 years ago

I've been trying to get a simple mapper example working. On my server side, I have the following piece of code to handle posting a new auction to my database:

@app.Route('/ah/post', methods: const[app.POST])
Future addAuction(@Decode() Auction auction) => 
    postgreSql.execute("insert into auctions (item_name,total_cost,username) "
                       "values (@item_name, @total_cost, @username)",auction);

And there is an associated Auction class available:

class Auction
{
    @Field()
    String item_name;

    @Field()
    int total_cost;

    @Field()
    String username;
}

On my client side I have the following code:

void main()
{
    bootstrapMapper();

    Auction auction = new Auction()
        ..username = "thad2"
        ..item_name = "pick"
        ..total_cost = 500;
}

What is the correct way to post this object to the server so that it can be decoded and stored in the database? I'm unable to find any examples in the documentation of this step. I've tried using the http (client) library like this (after changing the total_cost to a string because it only allows Map<String,String>:

http.post("http://localhost:8181/ah/post", body:encode(auction)).then((response) => print(response.body));

But that will return a page with the error "form not supported for this handler". I've also tried to send it as a string by doing

http.post("http://localhost:8181/ah/post", body:JSON.encode(encode(auction))).then((response) => print(response.body));

But that will return a page with the error "string not supported for this handler".

Thanks for reading and sorry if I'm missing the obvious "do this" instruction.

luizmineo commented 10 years ago

You are right, the documentation should include a proper client side example.

As you noticed, the encode() function transforms your object in a map, so you have to use the JSON.encode() method from dart:convert to convert it to a json string.

Also, when sending a json object, you have to configure the content-type header of the request. I'm not familiar with the http library, but using the HttpRequest class from dart:html, you can do as follow:

HttpRequest.request("/ah/post", method: "POST", requestHeaders: {"content-type": "application/json"}, sendData: JSON.encode(encode(auction))).then((req) => print(req.responseText));

You can also take a look at AngularDart, which provides a http service that abstracts the API of HttpRequest.

Futhermore, note that if you are sending the request from a different domain, you'll have a cross-domain issue. In that case, you can add the following interceptor to your server:

@app.Interceptor(r'/.*')
crossOriginInterceptor() {
  if (app.request.method == "OPTIONS") {
    //overwrite the current response and interrupt the chain.
    app.response = new shelf.Response.ok(null, headers: _createCorsHeader());
    app.chain.interrupt();
  } else {
    //process the chain and wrap the response
    app.chain.next(() => app.response.change(headers: _createCorsHeader()));
  }
}

_createCorsHeader() => {"Access-Control-Allow-Origin": "*"};

Update: You can see a more complete example here: https://github.com/DartVoid/Redstone-Angular-Todo (take a look at the items_backend.dart file to see how the request can be sent with the angular API)

RobertBlackhart commented 10 years ago

Thanks. That definitely put me back on the right track. I wasn't using the http library for any particular reason other than a while ago I think I came across it in a SO question that recommended it over the built-in.

I did have an interceptor that was supposed to put the CORS header on every request, but apparently it did not work as well as yours.

Also, I had to change the _createCorsHeader() to

_createCorsHeader() => {"Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept"};

otherwise I was getting a 405 Method Not Allowed error.

Again, thanks for the help.