dotnet-sdk/samples/AspNetCore/ControllerSample
Aman Bhardwaj 51fc48fb74
Adding & enhancing client apis over gRPC (#244)
* adding IDAprClient interface

* Adding DaprClientBuilder and adding methods for Publish

* updating the method name for publish

* Adding unit tests for publishevent api

* Removing individual clients for publish

* Renaming base class to DaprClient and implementation calss to DparClientGrpc

* Moving State api to grpc and adding helpers to unittest grpc calls.

* More DaprClient logic

* metadata, etag, options, etc are optional

* Revert "metadata, etag, options, etc are optional"

This reverts commit ea5dc12c5d.

* Default params, 2 new Try- methods, overload refactor for InvokeMethodAsync

* Move some classes/enums to different files.  Documentation

* Code refactor and changing tests to use grpc

* Adding tests with state options

* Adding test for getting etag and state

* More refactoring and adding INvokeBinding test

* Fixing tests and tidying up things.

* Updating solutions for renamed project

* Updating projects for rename.

* Oneclient secret (#245)

* ADding Secret Apis

* Using Task for secret

* Format (#242)

* Updating samples.slm

* awaint the call to grpc in common method so that common error handling can be done there.

* Addressing review comments

* fixing test.sln

* updating prod.sln

* Addressing review comments from James.

* Add doc and example for method invocation on http app

* Updating arg validation.

* Updating example.

Co-authored-by: LM <lemai>
Co-authored-by: Carlos Mendible <cmendible@gmail.com>
2020-03-09 20:52:12 -07:00
..
Controllers Adding & enhancing client apis over gRPC (#244) 2020-03-09 20:52:12 -07:00
Properties ADding launchsettings.json to user port for local launch (#111) 2019-10-17 17:57:48 -07:00
Account.cs moving aspnetcore sample to its own folder (#95) 2019-10-13 17:37:32 -07:00
ControllerSample.csproj Build for net core 3.1 (#189) 2019-12-16 09:31:25 -08:00
Program.cs Removed unused usings (#122) 2019-10-21 09:53:55 -07:00
Readme.md Using 127.0.0.1 instead of localhost. (#214) 2020-02-05 12:32:41 -08:00
Startup.cs moving aspnetcore sample to its own folder (#95) 2019-10-13 17:37:32 -07:00
Transaction.cs moving aspnetcore sample to its own folder (#95) 2019-10-13 17:37:32 -07:00
appsettings.json moving aspnetcore sample to its own folder (#95) 2019-10-13 17:37:32 -07:00

Readme.md

ASP.NET Core Controller Sample

This sample shows using Dapr with ASP.NET Core routing. This application is a simple and not-so-secure banking application. The application uses the Dapr state-store for its data storage.

It exposes the following endpoints over HTTP:

  • GET /{account}: Get the balance for the account specified by id
  • POST /deposit: Accepts a JSON payload to deposit money to an account
  • POST /withdraw: Accepts a JSON payload to withdraw money from an account

The application also registers for pub-sub with the deposit and withdraw topics.

Running the Sample

To run the sample locally run this comment in this directory:

dapr run --app-id routing --app-port 5000 dotnet run

The application will listen on port 5000 for HTTP.

Examples

Deposit Money

On Linux, MacOS:

curl -X POST http://127.0.0.1:5000/deposit \
       -H 'Content-Type: application/json' \
       -d '{ "id": "17", "amount": 12 }'

On Windows:

curl -X POST http://127.0.0.1:5000/deposit -H "Content-Type: application/json" -d "{ \"id\": \"17\", \"amount\": 12 }"

Output:

 {"id":"17","balance":12}

Withdraw Money On Linux, MacOS:

curl -X POST http://127.0.0.1:5000/withdraw \
       -H 'Content-Type: application/json' \
       -d '{ "id": "17", "amount": 10 }'

On Windows:

curl -X POST http://127.0.0.1:5000/withdraw -H "Content-Type: application/json" -d "{ \"id\": \"17\", \"amount\": 10 }"

Outpt:

{"id":"17","balance":2}

Get Balance

curl http://127.0.0.1:5000/17

Output:

{"id":"17","balance":2}

Withdraw Money (pubsub)

Publish events using Dapr cli:

On Linux, MacOS:

dapr publish -t withdraw -p '{"id": "17", "amount": 15 }'

On Windows:

dapr publish -t withdraw -p "{\"id\": \"17\", \"amount\": 15 }"

Deposit Money (pubsub) Publish events using Dapr cli: On Linux, MacOS:

dapr publish -t deposit -p '{"id": "17", "amount": 15 }'

On Windows:

dapr publish -t deposit -p "{\"id\": \"17\", \"amount\": 15 }"

Code Samples

All of the interesting code in this sample is in Startup.cs and Controllers/SampleController.cs

 public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers().AddDapr();

    ...
}

AddDapr() registers the Dapr integration with controllers. This also registers the StateClient service with the dependency injection container. This service can be used to interact with the Dapr state-store.


app.UseCloudEvents();

UseCloudEvents() registers the Cloud Events middleware in the request processing pipeline. This middleware will unwrap requests with Content-Type application/cloudevents+json so that model binding can access the event payload in the request body directly. This is recommended when using pub/sub unless you have a need to process the event metadata yourself.


app.UseEndpoints(endpoints =>
{
    endpoints.MapSubscribeHandler();
    endpoints.MapControllers();
});

MapSubscribeHandler() registers an endpoint that will be called by the Dapr runtime to register for pub-sub topics. This is is not needed unless using pub-sub.


[Topic("deposit")]
[HttpPost("deposit")]
public async Task<ActionResult<Account>> Deposit(...)
{
    ...
}

[Topic(...)] associates a pub-sub topic with this endpoint.


[HttpGet("{account}")]
public ActionResult<Account> Get(StateEntry<Account> account)
{
    if (account.Value is null)
    {
        return NotFound();
    }

    return account.Value;
}

Dapr's controller integration can automatically bind data from the state-store to an action parameter. Since the parameter's name is account the value of the account route-value will be used as the key. The data is stored in the state-store as JSON and will be deserialized as an object of type Account.

This could alternatively be written as:

[HttpGet("{account}")]
public ActionResult<Account> Get([FromState] Account account)
{
    ...
}

[HttpGet("{id}")]
public ActionResult<Account> Get([FromState("id")] Account account)
{
    ...
}

Using [FromState] allows binding a data type directly without using StateEntry<>. [FromState(...)] can also be used to specify which route-value contains the state-store key.


[Topic("deposit")]
[HttpPost("deposit")]
public async Task<ActionResult<Account>> Deposit(Transaction transaction, [FromServices] StateClient stateClient)
{
    var state = await stateClient.GetStateEntryAsync<Account>(transaction.Id);
    state.Value.Balance += transaction.Amount;
    await state.SaveAsync();
    return state.Value;
}

The StateClient can be retrieved from the dependency injection container, and can be used to imperatively access the state-store.

state.SaveAsync() can be used to save changes to a StateEntry<>.