Blazor HttpClient injection into a ViewModel constructor - asp.net-core

I am following the referenced blog/article below, and I am unable to inject an HttpClient to my ViewModel. It works fine the default way (#inject in the razor file). But i am trying to inject into the ViewModel instead.
If i add to the services like the following, then the default injection doesnt work for other razor views which has the #inject HttpClient.
// manually add HttpClient to services.
services.AddTransient<IFetchViewModel, FetchViewModel>();
Question:
How can i inject the default injected HttpClient to my various
ViewModels?
Note that I am getting an exception:
WASM: Unhandled exception rendering component:
WASM: System.Reflection.TargetParameterCountException: Number of parameters specified does not match the expected number.
Reference:
https://itnext.io/a-simple-mvvm-implementation-in-client-side-blazor-8c875c365435
Update
After making suggested changes and then digging deeper during
debugging, i can see that there is something wrong with the json
deserialization. Could this be an issue?
https://github.com/aspnet/Blazor/issues/225
Note that deeper down the exception stack trace, i see the following:
WASM: at SimpleJson.SimpleJson.DeserializeObject (System.String
json, System.Type type, SimpleJson.IJsonSerializerStrategy
jsonSerializerStrategy) <0x2ebc4f0 + 0x00068> in
<8f8c03446dbf45f5bbcb1e109a064f6e>:0 WASM: at
SimpleJson.SimpleJson.DeserializeObject[T] (System.String json)
<0x2ef2490 + 0x0000a> in <8f8c03446dbf45f5bbcb1e109a064f6e>:0 WASM:
at Microsoft.JSInterop.Json.Deserialize[T] (System.String json)
<0x2ef2458 + 0x00004> in <8f8c03446dbf45f5bbcb1e109a064f6e>:0 WASM:
at
Microsoft.AspNetCore.Components.HttpClientJsonExtensions.GetJsonAsync[T]
(System.Net.Http.HttpClient httpClient, System.String requestUri)
<0x33182e0 + 0x000fa> in <13ab8f8dacb6489b93c9655168c56037>:0 WASM:
at WebUI.Features.Fetch.FetchViewModel.LoadAsync () <0x3300de0 +
0x00102> in :0
Updated 2
So i can confirm now that i was barking up the wrong tree.
Essentially, i had a deserialization issue. Once i resolved that issue, everything is working fine. Not sure if i had a DI issue from the beginning or not. Nonetheless, my issue resolved now. Thanks for all the enlightening perspectives.

This is not really an answer to your question; without complete display of your code, no answer is really possible. But let me relate to the following code snippet; perhaps the problem lies there:
// manually add HttpClient to services.
services.AddTransient<IFetchViewModel, FetchViewModel>();
HttpClient service is provided as a Singleton (CSB) by the Blazor framework. Thus you cannot inject HttpClient into a service which you add to your app as Transient. Your service should also be added as Singleton...
Hope this helps...
[Edit]
How can i inject the default injected HttpClient
to my various ViewModels?
If your ViewModels are Components, you may use the #inject directive like this:
#inject HttpClient httpClient
If your ViewModels are ordinary classes (.cs), you can
either pass a reference to an HttpClient object from your calling component methods or inject the HttpClient service to your ViewModels' constructors. Don't forget to add your Services or ViewModels in the Startup class:
services.AddSingleton<IFetchViewModel, FetchViewModel>();
Once again, use AddSingleton
No, your problem has got nothing to do with issue 225.
This issue is very old, and all the bugs referred to in this issue were rectified long before I've heard of Blazor...
Note: The exception stack trace clearly points out in the direction of HttpClient being the culprit. Do what I've suggested above, and tell us if the issue is still persisting.
Why don't you display your code, as others ask you to do. Please look for instruction on how to ask question in stack overflow.

The pattern is easy.
I got it working like this, starting from the standard starter template.
In FetchData.razor:
#page "/fetchdata"
#using ClientBlazor1.ViewModels
#inject FetchDataViewModel vm
... the html
protected override async Task OnInitAsync()
{
forecasts = await vm.GetForecasts();
}
And the ViewModel is below. You seem to be missing the constructor (-injection) part here.
Using an interface is optional, I didn't.
public class FetchDataViewModel
{
private HttpClient _httpClient;
public FetchDataViewModel(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<WeatherForecast[]> GetForecasts()
{
return await _httpClient.GetJsonAsync<WeatherForecast[]>("sample-data/weather.json");
}
}
and to finish it up, the registration part in Startup.cs :
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<FetchDataViewModel>();
}
In general this should not be a Singleton.

Related

Resharp DI and DNS resolution

I'm upgrading to version 107 restsharp and i'm wondering if both these options below are ok ways of doing dependency injection in dotnet with restsharp.
The documents say don't create a new restsharp for every request (connection pool exhaustion resaons) but if the httpclient is injected via the constructor will I be get the benefit of DNS resolution changes even though rest client is contained in transient scoped object (i think that's what AddHttpClient will do). I know there will be some price to pay for creating a transient object, but for a general purpose business app maybe that's fine?
Recommended way as described in the documentation
services.AddSingleton<IMyClient, MyClient>();
public class MyClient : IMyClient
{
protected readonly RestClient _restClient;
public MyClient()
{
_restClient = new RestClient();
}
}
OR: is it ok to do this?
services.AddHttpClient<IMyClient, MyClient>();
public class MyClient : IMyClient
{
protected readonly RestClient _restClient;
public MyClient(HttpClient httpClient)
{
_restClient = new RestClient(httpClient);
}
}
You should AddSingleton, not AddHttpClient as per official documentation:
https://restsharp.dev/v107/#restclient-lifecycle
If you use a dependency-injection container, register your API client as a singleton.
I believe it's becouse RestClient is managing the connection pools and addressing known issues, that AddHttpClient would typically address.
If you would use HttpClient directly, you should follow Microsofts recommendations from below URL, where you have a choice of Singleton or AddHttpClient:
https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines#recommended-use
Also, not sure how your 2nd option works at this point.
Also, great video explaining more about what AddHttpClient does (sets HttpClient as Transient etc):
https://www.youtube.com/watch?v=Z6Y2adsMnAA&t=335s

Asp Core 3.1 API JsonIgnore (not) ignored in deserialization

I have this setup
Asp Core 3.1 API
Shared Lib with MyClass that is sent between API and client
Client App with Com classes
On the MyClass that is sent between them I have a field ComField that references a com class, this is only used on the client app and should not be (de)serialized, therefore I have it marked with [JsonIgnore]
class MyClass{
[JsonIgnore]
public ComThingy ComField {
get{// code here that throws the error when deserilaized on the API}
set{// code here}
}
}
When I write the API to accept the class like this, I get an error when the class is deserialized. The debugger throws the error while deserializing the MyClass, before it enters the method:
[HttpPost]
public async Task<ActionResult<MyClassReply>> Post([FromBody] MyClass myclass){
// code here
}
The API throws an exception that accessing the getter on MyClass throws an error (because that Com stuff isn't on the API).
If I deserialize manually it works fine, but then my swagger doesn't generate the whole API correctly.
[HttpPost]
public async Task<ActionResult<MyClassReply>> Post(){
// this works fine
var rdr = new StreamReader(Request.Body);
var mcj = await rdr.ReadToEndAsync();
var myclass = Newtonsoft.Json.JsonConvert.DeserializeObject<MyClass>(mcj);
// code here
}
So my question is: how come the ASP API builtin deserialization ignores the JsonIgnore attribute and still tries to deal with that property (throwing an error), and why does deserializing manually work as expected (ie ignore that property)? The default pipeline still uses NewtonSoft rght?
And how do I make the default deserialization work correctly?
Starting from ASP.NET Core 3.0, the default JSON serializer is System.Text.Json, and not Newtonsoft.Json. You need to call .AddNewtonsoftJson() in your Startup.cs to use it (see for example this answer).
Your issue might simply be that you're not using the proper JsonIgnore attribute. Both serializers have the same named attribute:
System.Text.Json.Serialization.JsonIgnoreAttribute
Newtonsoft.Json.JsonIgnoreAttribute
Maybe your using statement are importing the Newtonsoft.Json one instead of the System.Text.Json one?

How to add Custom Interceptor in Spring data rest (spring-data-rest-webmvc 2.3.0)

I am working on the spring data rest services & facing some issue in the custom interceptors. Earlier I used spring-data-rest-webmvc 2.2.0 & added interceptor in following way.
public RequestMappingHandlerMapping repositoryExporterHandlerMapping() {
RequestMappingHandlerMapping mapping = super
.repositoryExporterHandlerMapping();
mapping.setInterceptors(new Object[] { new MyInterceptor() });
return mapping;
}
It worked perfectly fine for me. But when i upgraded to spring-data-rest-webmvc 2.3.0 version, I noticed that handlerMapping is hidden behind DelegatingHandlerMapping. Hence I tried to add interceptor in following way.
In one of my config class I have extended RepositoryRestMvcConfiguration class & override its method.
public class AppConfig extends RepositoryRestMvcConfiguration {
#Autowired ApplicationContext applicationContext;
#Override
public DelegatingHandlerMapping restHandlerMapping()
{
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(super.resourceMappings(), super.config());
repositoryMapping.setInterceptors(new Object[] { new MyInterceptor()});
repositoryMapping.setJpaHelper(super.jpaHelper());
repositoryMapping.setApplicationContext(applicationContext);
repositoryMapping.afterPropertiesSet();
BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(super.config());
basePathMapping.setApplicationContext(applicationContext);
basePathMapping.afterPropertiesSet();
List<HandlerMapping> mappings = new ArrayList<HandlerMapping>();
mappings.add(basePathMapping);
mappings.add(repositoryMapping);
return new DelegatingHandlerMapping(mappings);
}
}
But after adding this some of my repository operations (findAll() operation on repository) start failing. If I removed this interceptors those operations worked fine. (In this interceptor I am just authenticate the user.)
Hence I am unable to understand problem here. Am I adding the interceptor in wrong way? Is there any other way to add the interceptor?
You should not use repositoryMapping.setInterceptors() - it destoys the internal interceptors Spring placed there, and that's probably the reason some methods stopped working.
I suggest you override jpaHelper() method and put your interceptors into the JpaHelper object in RepositoryRestMvcConfiguration. Spring will should them to the global interceptor list.
But, again, if all you need is authentication, why not use a Spring Security filter?
EDIT: the solution above works only for RepositoryRestHandlerMapping, not for BasePathAwareHandlerMapping.
I suggest you declare a custom MappedInterceptor bean somewhere:
#Bean
public MappedInterceptor myMappedInterceptor() {
return new MappedInterceptor(new String[]{"/**"}, new MyInterceptor());
}
From my understanding of the source code Spring should automatically add this interceptor to all request handlers.

Ninject interception WCF service

I'm a newbie on the subject, so I'll try to make this as clear as I can...
I created a WcfModule, where I load the following package:
Bind<IDistributorService>().To<DistributorService>().InRequestScope().Intercept().With<ExceptionInterceptor>();
At first, I don't receive any error, but I put an InterceptAttribute on my function:
[AttributeUsage(AttributeTargets.Method)]
public sealed class HandleExceptionsAttribute : InterceptAttribute
{
public override IInterceptor CreateInterceptor(IProxyRequest request)
{
return request.Kernel.Get<ExceptionInterceptor>();
}
}
[HandleExceptions]
public virtual Result<List<DistributorDataContract>> GetDistributor(string id)
{
//...code...
I get an error in this function: (first line in method)
private ServiceHost CreateNewServiceHost(Type serviceType, Uri[] baseAddresses, WebHttpBehavior webBehavior, WebHttpBinding webHttpBinding)
{
var host = base.CreateServiceHost(serviceType, baseAddresses);
//...
}
With the error:
InvalidProxyConstructorArgumentsException was unhandled by user code
Can not instantiate proxy of class:
My.Namespace.DistributorService.
Could not find a parameterless constructor.
Anyone who knows what the problem could be? Thanks!
This exception is thrown by castle core dynamic proxy when it is instructed to create a "class proxy" which does not have a parameterless (default) constructor and no constructor-arguments are passed to castle (see source).
My best guess is, that when you use ninject interception by attributes, ninject will instruct castle core to create a class-proxy, no matter whether your binding is Bind<IFoo>().To<Foo>() or Bind<Foo>().ToSelf().
It seems a bit strange, however, that ninject is not resolving and passing along all required constructor parameters.
What is the implementation of DistributorService and what's the implementation of the base class of the class containing CreateNewServiceHost?
Workaround:
Of course, switching to the Intercept().With<TInterceptor>() syntax will probably also enable you to use interception (see http://codepyre.com/2010/03/using-ninject-extensions-interception-part-2-working-with-interceptors/)

How to resolve dependency inside AuthorizeAttribute with WebApi and Ninject

My current setup is using Ninject for simple IoC, everything goes fine, but I'm not able to resolve one of the classes I need inside my AuthorizeAttribute. I need to access a class that does ClaimsVerification:
Here's my code:
IoC Config:
var kernel = new StandardKernel(); // Ninject IoC
// These registrations are "per instance request".
// See http://blog.bobcravens.com/2010/03/ninject-life-cycle-management-or-scoping/
kernel.Bind<RepositoryFactories>().To<RepositoryFactories>()
.InSingletonScope();
kernel.Bind<IRepositoryProvider>().To<RepositoryProvider>();
kernel.Bind<ISmartDocumentorUow>().To<SmartDocumentorUow>();
kernel.Bind<IClaimsVerification>().To<ClaimsVerification>();
// kernel
//kernel.BindFilter<MyAuthorizeAttribute>(FilterScope.Controller, 0).WhenControllerHas<RequireRolesAttribute>();
// Tell WebApi how to use our Ninject IoC
config.DependencyResolver = new NinjectDependencyResolver(kernel);
MyAuthorizeAttribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
[Inject]
IClaimsVerification clamisverify { get; set; }
public MyAuthorizeAttribute()
{
//var x = System.Web.Mvc.DependencyResolver.Current.(typeof(IClaimsVerification));
}
Yap, sorry, the problem was injecting the iClaimsverification that isn't working in web api..
I tryed with the public property and still it didn't work.
the bindfilter is commented out, because it doesn't exist in the core NInject api (dll), it does exists in the MVC dll of ninject but it works for Action filters in the web mvc, and not in the api mvc for what i can tell..
i do solved the issue like this, though i don't like a lot of this fix:
private IClaimsVerification verifier
{
get
{
return (GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IClaimsVerification)) as IClaimsVerification);
}
}
The property you have marked with Inject is private - you need to initialize Ninject with a custom configuration to opt into what would be a much less efficient process
(You didnt state the problem in your question. I see you were trying BindFilter, but it's commented out (why?) - this is the correct approach. I recommend reading the Ninject.MVC3 wiki article on BindFilter for an example)