AddHttpClient in Blazor Server Side - core

I'm trying to create an httpclient in Blazor Server side which would create the least amount of configuration effort every time I call my webapi.
Essentially I would like achieve the following:
Named HTTPClient I can automatically call when I call a function in my webapi.
The webapi requires a bearer token, which I get by calling AcquireTokenSilent
Would be great if I don't have to specify the httpclient when I call the api
The webapi has been added as a service reference, so there is scaffold classes created under the namespace myapp.server.api
To start this off, I created the following in startup:
services.AddHttpClient<myapp.server.api.swaggerClient>(c =>
{
c.BaseAddress = new Uri("https://api.myapp.com/");
AzureADB2COptions opt = new AzureADB2COptions();
Configuration.Bind("AzureAdB2C", opt);
IConfidentialClientApplication cca =
ConfidentialClientApplicationBuilder.Create(opt.ClientId)
.WithRedirectUri(opt.RedirectUri)
.WithClientSecret(opt.ClientSecret)
.WithB2CAuthority(opt.Authority)
.WithClientName("myWebapp")
.WithClientVersion("0.0.0.1")
.Build();
IHttpContextAccessor pp;
string signedInUserID = context.User.FindFirst(ClaimTypes.NameIdentifier).Value;
new MSALStaticCache(signedInUserID, pp.HttpContext).EnablePersistence(cca.UserTokenCache);
var accounts = cca.GetAccountsAsync().Result;
AuthenticationResult result = null;
result = cca.AcquireTokenSilent(opt.ApiScopes.Split(' '), accounts.FirstOrDefault()).ExecuteAsync().Result;
c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
});
My hope is to be able to call my api in my views in this way:
myapp.server.api.swaggerClient t = new myapp.server.api.swaggerClient();
currentCount = t.WeatherForecastAsync().Result.FirstOrDefault().Summary;
calling a new instance of swaggerclient requires me to specify an httpclient, so my hopes is to inject the httpclient I am configuring on a global level for that type can be injected automatically.
The pieces I need help with:
Given that I have specified my httpclient scoped to a specific type, would it call automatically if I call a function in my webapi? (Does not seem to fire when debugging)
To get the bearer token, I need to get the current userID, which is in the authstateprovider... seeing that this is in Startup, is getting it from DI even possible?
Any easy way to inject the httpclient on the constructor of my webapi classes? would I be able to get the httpclient in the constructor so that I essentially have a parameterless constructor not asking for httpclient?

Concerning your first question, inject the Web API HttpClient like this in your view:
#inject myapp.server.api.swaggerClient MyClient
and then in the code block:
currentCount = MyClient.WeatherForecastAsync().Result.FirstOrDefault().Summary;
You should be able to debug the code inside AddHttpClient.

Related

Spring React and Sessions.. how to keep session

I have set up my spring to maintain a HTTP session on an object like so:
#Component
#SessionScope
public class Basket { .. }
controller:
#PostMapping(path="/basket/addItem/{user}", consumes = "application/json", produces = "application/json")
public Basket createBasket(#PathVariable String user, #RequestBody Item item) {
System.out.println("POSTING..................................");
return basketService.addItem(user, item);
}
now when i use a REST client, in firefox i can see that the session bean is created and maintained for the duration - multiple calls. I can append to the object. If i try another client, it gets its own session with its own bean. great..
spring logs the following:
Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [269] milliseconds.
However im trying to create a basic front end in react, when react makes a request using axios it gets a new bean every time, which means that the session must be ending after each call. IS that correct? or im not tying it to the react application...
Maybe the approach im taking is not correct, maybe i should use a a different approach, Im trying to learn about spring boot, so its a basic project... and right now i want to maintain user session for a cart. so subsequent calls i can append to the object...
by adding the following to my controller it all began to work.
#CrossOrigin(origins = { "http://localhost:3000" }, allowedHeaders = "*", allowCredentials = "true")

Flurl calling IdentityServer4

I'm attempting to call Identity Server 4 with Flurl
Im getting a 400 back from the ID server.
The credentials etc work when I make the call with PostMan or with the methods available in IdentityModel.Client.
I am unsure about the Post i.e. PostUrlEncodedAsync
As you can see I have tried various combinations.
This.. does not work
var address = "http://localhost:8027/connect/token";
var x = await address
.WithBasicAuth("clientName", "secretValue")
.SetQueryParams(
new
{
GrantType = "client_credentials",
Scope = "requiredScope"
}
)
.PostUrlEncodedAsync(new { });
//.SendAsync(HttpMethod.Post, null, CancellationToken.None);
//.SendAsync(HttpMethod.Post, y);
This.. does.
var apiClientCredentials = new ClientCredentialsTokenRequest()
{
Address = "http://localhost:8027/connect/token",
ClientId = "clientName",
ClientSecret = "secretValue",
Scope = "requiredScope"
};
var client = new HttpClient();
var tokenResponse = await client.RequestClientCredentialsTokenAsync(apiClientCredentials);
if (tokenResponse.IsError)
{
}
I can see that deep doen inside 'RequestClientCredentialsTokenAsync' it does parameters.Add to add grant type and scope hence I have added those as params.
Have also tried adding the client id and secret to the query params.
Same 400 message.
Any help greatly appreciated.
I believe the difference is that IdentityModel knows to serialize those ClientCredentialsTokenRequest property names like ClientId and GrantType to snake-case, i.e. client_id and grant_type, as required by OAuth2. When you hand Flurl an anonymous object, it just serializes the property names exactly as-is. To make it work with Flurl you can either create a class and decorate it with Json.NET serialization attributes, or (what I typically do) keep it simple take some liberties with C# property name conventions:
.SetQueryParams(
new
{
grant_type = "client_credentials",
scope = "requiredScope"
}

How to use a CookieCollection in multiple functions

I'm setting up a web page using cookies to determine if the user already logged in, using a cookie containing his id. Problem is : The cookie is either not written or the cookie collection is not updated.
I've tried reading the documentation, but it does not define the usage of CookieCollection.
Here's the function where i write my cookie :
function displayData(){
$id = $this->getRequest()->getSession()->read('id');
$cookies = CookieCollection::createFromServerRequest($this->getRequest());
if(!$cookies->has('id')){
$cookie = (new Cookie('id'))
->withValue($id)
->withExpiry(new DateTime('+999 year'))
->withPath('/')
->withDomain('break-first.eu')
->withSecure(true)
->withHttpOnly(true);
$cookies = $cookies->add($cookie);
}
// Other stuff
}
And where I try reading it :
function index(){
$cookies = $this->getRequest()->getCookieCollection();
dd($cookies);
}
I expect having a cookie named "id", but I don't have it. Only CAKEPHP and pll_language are showing up.
First things first, CakePHP provides authentication functionality with cookie authentication, you may want to have a look at that instead of driving a custom solution.
Cookbook > Plugins > Authentication
That being said, what you're doing there will create a cookie collection object, which however is just that, a lone object somewhere in space, it won't affect the state of your application, in order for that to happen you have to actually modify the response object.
However what you're trying to do there doesn't require cookie collections in the first place, you can simply read and write cookies directly via the methods provided by the request and response objects, like:
// will be `null` in case the cookie doesn't exist
$cookie = $this->getRequest()->getCookie('id');
// responses are immutable, they need to be reassinged
this->setResponse(
$this->getResponse()->withCookie(
(new Cookie('id'))
->withValue($id)
->withExpiry(new DateTime('+999 year'))
->withPath('/')
->withDomain('break-first.eu')
->withSecure(true)
->withHttpOnly(true)
)
);
And if you where to use a cookie collection for whatever reason, then you'd use withCookieCollection() to pass it into the response:
$this->setResponse($this->getResponse()->withCookieCollection($cookies));
If you run into strict typing errors, you could for example create a custom reponse class with an overridden Response::convertCookieToArray() method and cast the string to an integer there (make sure that PHP_INT_MAX covers your target date timestamp, 32-Bit incompatibility is why the fix that landed in CakePHP 4.x, probably won't come to 3.x), something like:
src/Http/Response.php
namespace App\Http;
use Cake\Http\Cookie\CookieInterface;
use Cake\Http\Response as CakeResponse;
class Response extends CakeResponse
{
protected function convertCookieToArray(CookieInterface $cookie)
{
$data = parent::convertCookieToArray($cookie);
$data['expire'] = (int)$data['expire'];
return $data;
}
}
You can pass that into the app in your webroot/index.php file, as the second argument of the $server->run() call:
// ...
$server->emit($server->run(null, new \App\Http\Response()));
See also
Cookbook > Request & Response Objects > Request > Cookies
Cookbook > Request & Response Objects > Response > Setting Cookies

How to get custom SOAP header from WCF service response in Silverlight?

I'm trying to get custom response message header in Silverlight application.
on server-side new MessageHeader added to response headers:
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("headerName", "headerNS", "The header value"));
and I can see this header in Fiddler:
s:Envelope [
xmlns:s=http://schemas.xmlsoap.org/soap/envelope/
]
s:Header
headerName [ xmlns=headerNS ] The
header value
But, I can't find a way to read header value in Silverlight application service callback:
using (new OperationContextScope(proxy.InnerChannel))
{
var headers = OperationContext.Current.IncomingMessageHeaders;
// headers is null :(
}
Does anyone encountered with similar issue?
Getting SOAP headers in responses on Silverlight isn't as easy as it should be. If you use the event-based callbacks, you're out of luck - it just doesn't work. You need to use the Begin/End-style operation call, like in the example below.
void Button_Click(...)
{
MyClient client = new MyClient();
IClient proxy = (IClient)client; // need to cast to the [ServiceContract] interface
proxy.BeginOperation("hello", delegate(IAsyncResult asyncResult)
{
using (new OperationContextScope(client.InnerChannel))
{
proxy.EndOperation(asyncResult);
var headers = OperationContext.Current.IncomingMessageHeaders;
// now you can access it.
}
});
}
Notice that you cannot use the generated client (from slsvcutil / add service reference) directly, you need to cast it to the interface, since the Begin/End methods are not exposed (explicitly implemented) on the client class.
To get headers from http request try to use Client HTTP stack.
The easies way to do it is to register the prefix, for example:
WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);

Good Async pattern for sequential WebClient requests

Most of the code I've written in .NET to make REST calls have been synchronous. Since Silverlight on Windows Phone only supports Async WebClient and HttpWebRequest calls, I was wondering what a good async pattern is for a Class that exposes methods that make REST calls.
For example, I have an app that needs to do the following.
Login and get token
Using token from #1, get a list of albums
Using token from #1 get a list of categories
etc
my class exposes a few methods:
Login()
GetAlbums()
GetCategories()
since each method needs to call WebClient using Async calls what I need to do is essentially block calling Login till it returns so that I can call GetAlbums().
What is a good way to go about this in my class that exposes those methods?
You might take a look at the Reactive (Rx) framework extensions:
http://www.leading-edge-dev.de/?p=501
http://themechanicalbride.blogspot.com/2009/07/introducing-rx-linq-to-events.html
[edit: ooh - found a good link:]
http://rxwiki.wikidot.com/101samples
They provide a way to "sequence" events, acting only upon certain conditions met - for example, say you had a method "AuthenticationResult Authenticate(string user, string pass)"
You could do something like:
var foo = Observable.FromAsyncPattern<string, string, AuthenticationResult>
(client.BeginAuthenticate, client.EndAuthenticate);
var bar = foo("username","password");
var result = bar.First();
Effectively turning an asynchronous method to a synchronous one. You can extend this to include "chaining":
var bar = foo("username", "password")
.Then(authresult => DoSomethingWithResult(authresult));
Neat stuff. :)
It really depends on what you want to do with this information. If for instance you are attempting to display the list of albums/categories etc, one way to model this would be to
Have one or more classes which implements the INotifyPropertyChanged interface, and are used as data sources for your views (look at the files under the Models folder in a new PhoneListApplication for an example)
Start an async operation to login and get the token, have the async method's callback store the token for you and call a function which will start an async operation to get list of albums and categories.
The callback for the async operation to get a list of albums/categories can update an ObservableList (by adding items to it). I'd imaging you have one class each for albums and categories, each with an observable list. Anyways, once you are done adding, just call NotifyPropertyChanged with the name of the property you changed, and your data should show up.
There is an obvious problem with cases where you want to wait and not proceed until you receive something back over the network (for instance if you want to keep the login page around until you know that you have successfully authenticated). In this case you could just change the page in the async callback.
You could obviously also do something fancier and have a thread wait for an event set by the async callback. I recommend not have the UI thread do this, since it limits your ability to have things like timeouts, and is generally very messy.
We wrote our client-side service layer with all async function signatures that look like this:
public void MyFunction(
ArtType arg,
Action<ReturnType> success,
Action<FailureType> failure);
The service code does an async call to the web service, and when that returns it calls the success callback if the call was successful, and the failure callback if there was a fault/exception. Then the calling code kinda looks like this:
MyServiceInstance.MyFunction(
blahVar,
returnVal => UIInvoker.Invoke(() =>
{
//some success code here
}),
fault => UIInvoker.Invoke(() =>
{
//some fault handling code here
}));
(UIInvoker is just a utility that dispatches back to the UI from a background thread.)
I put something together that is a bit more fluent.
Restful-Silverlight is a library I've created to help with both Silverlight and WP7.
I have included code below to show how you can use the library to retrieve tweets from Twitter.
Sample Usage of Restful-Silverlight retrieving tweets from Twitter:
//silverlight 4 usage
List<string> tweets = new List<string>();
var baseUri = "http://search.twitter.com/";
//new up asyncdelegation
var restFacilitator = new RestFacilitator();
var restService = new RestService(restFacilitator, baseUri);
var asyncDelegation = new AsyncDelegation(restFacilitator, restService, baseUri);
//tell async delegation to perform an HTTP/GET against a URI and return a dynamic type
asyncDelegation.Get<dynamic>(new { url = "search.json", q = "#haiku" })
//when the HTTP/GET is performed, execute the following lambda against the result set.
.WhenFinished(
result =>
{
textBlockTweets.Text = "";
//the json object returned by twitter contains a enumerable collection called results
tweets = (result.results as IEnumerable).Select(s => s.text as string).ToList();
foreach (string tweet in tweets)
{
textBlockTweets.Text +=
HttpUtility.HtmlDecode(tweet) +
Environment.NewLine +
Environment.NewLine;
}
});
asyncDelegation.Go();
//wp7 usage
var baseUri = "http://search.twitter.com/";
var restFacilitator = new RestFacilitator();
var restService = new RestService(restFacilitator, baseUri);
var asyncDelegation = new AsyncDelegation(restFacilitator, restService, baseUri);
asyncDelegation.Get<Dictionary<string, object>>(new { url = "search.json", q = "#haiku" })
.WhenFinished(
result =>
{
List<string> tweets = new List();
textBlockTweets.Text = "";
foreach (var tweetObject in result["results"].ToDictionaryArray())
{
textBlockTweets.Text +=
HttpUtility.HtmlDecode(tweetObject["text"].ToString()) +
Environment.NewLine +
Environment.NewLine;
}
});
asyncDelegation.Go();

Resources