I am getting the following error message when executing IRequest with IAsyncRequestHandler.
System.InvalidOperationException: 'No service for type 'MediatR.IRequestHandler`2[TestProject.Domain.Requests.Users.CreateUserRequest,TestProject.Domain.Requests.Users.CreateUserResponse]' has been registered.'
This is how i register it in the startup class
// Add framework services.
services.AddMvc();
services.AddMediatR(typeof(CreateUserRequest).GetTypeInfo().Assembly);
CreateUserRequest and Response
public class CreateUserRequest : IRequest<CreateUserResponse>
{
public string EmailAddress { get; set; }
public int OrganisationId { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class CreateUserResponse
{
public int UserId { get; set; }
public string EmailAddress { get; set; }
}
Request handler
public class CreateUserRequestHandler : IAsyncRequestHandler<CreateUserRequest, CreateUserResponse>
{
private readonly UserManager<User> _userManager;
public CreateUserRequestHandler()
{
}
public async Task<CreateUserResponse> Handle(CreateUserRequest request)
{
//create the user and assign it to the organisation
var user = new User
{
Email = request.EmailAddress,
OrganisationUsers = new List<OrganisationUser> { new OrganisationUser { OrganisationId = request.OrganisationId } }
};
//create new user with password.
await _userManager.CreateAsync(user, request.Password);
//create response.
var response = new CreateUserResponse{UserId = user.Id, EmailAddress = user.Email};
return response;
}
}
Controller class
public class UserController : Controller
{
private readonly IMediator _mediator;
public UserController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<CreateUserResponse> Post(CreateUserRequest request)
{
return await _mediator.Send(request);
}
}
the error occurs inside the controller class it does not hit the async request handler.
Is there anything wrong with the DI registration? I have looked at the examples but could not find anything specific to aspnet core.
Related
i am trying to setup a blazor server app, calling a webapi.
I keep getting a 400 error returned, when I call the API.
I have 3 Projects, projectserver and projectapi. projectserver is where the Blazor app sits and Project API is where the API sits.
I don't know if the apicall can find the API as it does not hit any breakpoints in the API section, I am totally confused, as if it cannot find the API then it should return a 404 or other error and not 400 ?
thank you for your efforts.
this is my code,
Projectserver, this is where I post the Register Model to the API
public string message { get; set; }
public RegisterModel r = new RegisterModel();
private async Task Create(MouseEventArgs e)
{
var json = Newtonsoft.Json.JsonConvert.SerializeObject(r);
var client = clientfactory.CreateClient("ServerApi");
var result = await client.PostAsJsonAsync("/Account/Register",json); // check the Startup file and check base address for the Full route.
message = result.StatusCode.ToString();
}
}
the ClientFactory returns the base address of what is defined in startup.cs
services.AddHttpClient("ServerApi", client => client.BaseAddress = new Uri("https://localhost:44302/"));
the API is Projectserver and defined as follows.
[Route("[controller]")]
[ApiController]
public class AccountContoller : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly SecurityOptions _securityOptions;
private readonly JwtIssuerOptions _jwtOptions;
// GET: api/<Account>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<Account>/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/<Account>
[HttpPost]
public void Post([FromBody] string value)
{
}
// POST api/<Account>
[HttpPost("Register")]
public async Task<ActionResult<RegisterResult>> Register(RegisterModel model)
{
RegisterResult r = new RegisterResult();
var Exisits = await _context.Users.Where(r => r.EmailAddress == model.Email).FirstOrDefaultAsync();
if(Exisits != null)
{
r.Sucsess = false;
r.ErrorMessage = "Email - Already Exisits";
return r;
}
else
{
try
{
User newuser = new User();
newuser.CreatedDateTime = DateTime.UtcNow;
newuser.UserID = Guid.NewGuid();
newuser.MobileNumber = model.MobileNumber;
newuser.Password = model.Password;
newuser.FirstName = model.FirstName;
newuser.Surname = model.LastName;
_context.Users.Add(newuser);
await _context.SaveChangesAsync();
r.Sucsess = true;
return r;
}
catch(Exception e)
{
r.Sucsess = false;
r.ErrorMessage = e.ToString();
return r;
}
}
}
the Model classes are defined as Serializable
[Serializable]
public class RegisterResult
{
public bool Sucsess { get; set; }
public string ErrorMessage { get; set; }
}
[Serializable]
public class RegisterModel
{
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string RoleID { get; set; }
public string EntityID { get; set; }
public string MobileNumber { get; set; }
}
Can you please modify your code as below and give it a try:-
var serializedBody = JsonConvert.SerializeObject(r);
var jsonRequestBodyContent = new StringContent(serializedBody, Encoding.UTF8,"application/json");
var client = clientfactory.CreateClient("ServerApi");
var result = await client.PostAsync("/Account/Register",jsonRequestBodyContent);
How to create the dropdown list in one to many relation. I want to populate the category data in Post form and then want to save using POST mode.
Here is my full code:
public class Category
{
public Category()
{
Posts = new Collection<Post>();
}
public int Id{get;set;}
public string Title { get; set; }
}
public class Post
{
public int Id
public string Title { get; set; }
public string Body { get; set; }
public Category Category { get; set; }
public int CategoryId { get; set; }
}
PostFormVM:
public class PostFormVM
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string Body { get; set; }
[Required]
public int CategoryId { get; set; }
public IEnumerable<Category> Categories { get; set; }
}
Mapping is here:
public class ApplicationProfile : AutoMapper.Profile
{
public ApplicationProfile()
{
CreateMap<Category, CategoryFormVM>().ReverseMap();
CreateMap<Post, PostFormVM>().ReverseMap();
}
}
Generic Repository implementation
public class GenericRepository<T>:IGenericRepository<T> where T:class
{
private readonly ApplicationDbContext _context;
public GenericRepository(ApplicationDbContextcontext)
{
_context = context;
}
public async Task<List<T>> GetAllAsync()
{
return await _context.Set<T>().ToListAsync();
}
}
ICategoryRepository:
public interface ICategoryRepository:IGenericRepository<Category>
{
}
CategoryRepository implementation
public class CategoryRepository :GenericRepository<Category>, ICategoryRepository
{
public CategoryRepository(ApplicationDbContext context):base(context)
{
}
}
PostRepo Implementation:
public class PostRepository : GenericRepository<Post>, IPostRepository
{
public PostRepository(ApplicationDbContext context) : base(context)
{
}
}
PostController:
public class PostItemController : Controller
{
private readonly IPostRepository _postRepository;
private readonly ICategoryRepository _categoryRepository;
private readonly UserManager<ApplicationUser> _userManager;
private readonly IMapper _mapper;
public PostItemController(IPostRepository postRepository, ICategoryRepository categoryRepository, IMapper mapper, UserManager<ApplicationUser> userManager)
{
_postRepository = postRepository;
_categoryRepository = categoryRepository;
_userManager = userManager;
_mapper = mapper;
}
public IActionResult Create()
{
//Here I want to populate the category data I have used the ViewBag and ViewData here
//I am unable to get the data from the database
ViewBag.Categories= _categoryRepository.GetAllAsync();
return View(new PostFormVM());
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(PostFormVM viewModel)
{
try
{
if (!ModelState.IsValid)
return View("Create", viewModel);
if (ModelState.IsValid) {
//Here I also want to map the selected category item and save to Post table.
var post = _mapper.Map<Post>(viewModel);
post.ApplicationUserId = _userManager.GetUserId(HttpContext.User);
if (viewModel.IsEdit.Equals("false"))
{
await _postRepository.CreateAsync(post);
}
else
{
await _postRepository.UpdateAsync(post);
}
}
}
catch (Exception)
{
}
return RedirectToAction(nameof(Index));
}
I want help to populate the category data in Post Entity Create form.
You can put a breakpoint on this line ViewBag.Categories = _categoryRepository.GetAllAsync();, you can see such a result prompt Result =" {Not yet computed} ", because the method in your generic repository uses the await keyword to operate Asynchronous method, it will wait for the end of the previous process before calculating the result.
Try change you code in Generic Repository like below:
public List<T> GetAllAsync()
{
return _context.Set<T>().ToList();
}
IGenericRepository
public interface IGenericRepository<T> where T : class
{
List<T> GetAllAsync();
}
Show the Category list ,controller
public IActionResult Create()
{
IEnumerable<Category> categories = _categoryRepository.GetAllAsync();
ViewBag.Categories = categories;
return View(new PostFormVM());
}
View
<select asp-for="CategoryId" asp-items="#(new SelectList(ViewBag.Categories,"Id","Title"))"></select>
Result:
I created Web API to receive daily temperature from OpenWeatherAPI.
I put the API call in the MVC project; (plan to create new project later for better microservice architecture.)
Someone had mentioned in the code:
in your HomeController you're attempting to simply just call the action like a method on an instance of WeatherController. You need to use HttpClient there as well. Also, don't new up HttpClient directly. It should be treated as a singleton
How would I conduct this? This is the original code, started programming month ago.
MVC Page:
namespace WeatherPage.Controllers
{
public class HomeController : Controller
{
public WeatherController weathercontroller = new WeatherController();
public IActionResult Index()
{
return View();
}
public Async Task<IActionResult> About()
{
ViewData["Message"] = "Your application description page.";
ViewData["test"] = weathercontroller.City("Seattle");
return View();
}
}
}
API Controller:
[Route("api/[controller]")]
public class WeatherController : ControllerBase
{
[HttpGet("[action]/{city}")]
public async Task<IActionResult> City(string city)
{
Rootobject rawWeather = new Rootobject();
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri("http://api.openweathermap.org");
var response = await client.GetAsync($"/data/2.5/weather?q={city}&appid=APIkey&units=metric");
response.EnsureSuccessStatusCode();
var stringResult = await response.Content.ReadAsStringAsync();
rawWeather = JsonConvert.DeserializeObject<Rootobject>(stringResult);
return Ok(rawWeather);
}
catch (HttpRequestException httpRequestException)
{
return BadRequest($"Error getting weather from OpenWeather: {httpRequestException.Message}");
}
}
}
}
public class Rootobject
{
public Coord coord { get; set; }
public Weather[] weather { get; set; }
public string _base { get; set; }
public Main main { get; set; }
public int visibility { get; set; }
public Wind wind { get; set; }
public Clouds clouds { get; set; }
public int dt { get; set; }
public Sys sys { get; set; }
public int id { get; set; }
public string name { get; set; }
public int cod { get; set; }
}
This works in my project:
https://localhost:55555/api/weather/city/washington
Retrieve Data From Third party Openweather Api
Should We Call Web Api from Mvc Application in Same Solution
It roughly means you should use dependency injection .
Don't create an instance of HttpClient every time when you need it , just ask for an instance of HttpClient instead .
Extract your code of getting weather in the weather controller into a service , and ask for the service both in weather controller api and home controller
The WeatherService :
public interface IWeatherService
{
Task<Rootobject> CityAsync(string city);
}
public class WeatherService : IWeatherService{
private HttpClient _httpClient ;
public WeatherService(IHttpClientFactory clientFactory){
this._httpClient = clientFactory.CreateClient();
}
public async Task<Rootobject> CityAsync(string city){
Rootobject rawWeather = new Rootobject();
this._httpClient.BaseAddress = new Uri("http://api.openweathermap.org");
var response = await this._httpClient.GetAsync($"/data/2.5/weather?q={city}&appid=APIkey&units=metric");
response.EnsureSuccessStatusCode();
var stringResult = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Rootobject>(stringResult);
}
}
The new WeatherController :
[Route("api/[controller]")]
public class WeatherController : ControllerBase
{
private IWeatherService _weatherService;
public WeatherController(IWeatherService wetherService ){
this._weatherService= wetherService;
}
[HttpGet("[action]/{city}")]
public async Task<IActionResult> City(string city)
{
try
{
var rawWeather=await this._weatherService.CityAsync(city);
return Ok(rawWeather);
}
catch (HttpRequestException httpRequestException)
{
return BadRequest($"Error getting weather from OpenWeather: {httpRequestException.Message}");
}
}
}
The new HomeController:
public class HomeController : Controller
{
private IWeatherService _weatherService;
public HomeController(IWeatherService wetherService ){
this._weatherService= wetherService;
}
public IActionResult Index()
{
return View();
}
public async Task<IActionResult> About()
{
ViewData["Message"] = "Your application description page.";
ViewData["test"] = await this._weatherService.CityAsync("Seattle");
return View();
}
}
The ConfigureServices:
services.AddHttpClient();
services.AddSingleton<IWeatherService ,WeatherService>();
My authentication is working fine on it is own but i need to use phoneNumber of users instead of user names.
There is my Provider class
using Identity.Infrastructure;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Identity.Providers
{
public class CustomOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
if (!user.EmailConfirmed)
{
context.SetError("invalid_grant", "User did not confirm email.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
}
}
in this class context is coming with only userName and Password,so it cant reach PhoneNumber even i send it as a parameter.I think problem will solve after if i can change
userManager.FindAsync(context.UserName, context.Password)
like this
userManager.FindAsync(context.PhoneNumber, context.Password)
VS doesn't allow me to interfere OAuthGrantResourceOwnerCredentialsContext
using Identity.Infrastructure;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Web.Http.Routing;
namespace Identity.Models
{
public class ModelFactory
{
private UrlHelper _UrlHelper;
private ApplicationUserManager _AppUserManager;
public ModelFactory(HttpRequestMessage request, ApplicationUserManager appUserManager)
{
_UrlHelper = new UrlHelper(request);
_AppUserManager = appUserManager;
}
public UserReturnModel Create(ApplicationUser appUser)
{
return new UserReturnModel
{
Url = _UrlHelper.Link("GetUserById", new { id = appUser.Id }),
Id = appUser.Id,
UserName = appUser.UserName,
FullName = string.Format("{0} {1}", appUser.FirstName, appUser.LastName),
Email = appUser.Email,
EmailConfirmed = true,
Level = appUser.Level,
JoinDate = appUser.JoinDate,
Roles = _AppUserManager.GetRolesAsync(appUser.Id).Result,
Claims = _AppUserManager.GetClaimsAsync(appUser.Id).Result,
PhoneNumber = appUser.PhoneNumber
};
}
public RoleReturnModel Create(IdentityRole appRole)
{
return new RoleReturnModel
{
Url = _UrlHelper.Link("GetRoleById", new { id = appRole.Id }),
Id = appRole.Id,
Name = appRole.Name
};
}
}
public class RoleReturnModel
{
public string Url { get; set; }
public string Id { get; set; }
public string Name { get; set; }
}
public class UserReturnModel
{
public string Url { get; set; }
public string Id { get; set; }
public string UserName { get; set; }
public string FullName { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public int Level { get; set; }
public DateTime JoinDate { get; set; }
public IList<string> Roles { get; set; }
public IList<System.Security.Claims.Claim> Claims { get; set; }
}
}
As result I stucked on authenticating with phoneNumber instead of userName and set deviceId as password
public override Task<ApplicationUser> FindAsync(string Phone, string password)
{
//Do your Stuff here
//return base.FindAsync(userName, password);
}
Overrride FIndAsync() in the IndentityConfig.cs
I am trying to call a 3rd party web service
Their REST API uses the following URL style.
http://www.VoiceBase.com/services?version=1.0&apikey=your-apikey&password=secret&action=list&status=processing
All of their service calls go to the same /services
How do I create a class so the following would work?
var client = new JsonServiceClient("http://www.voicebase.com");
var response = client.Get<ResponseVoiceBaseListClass>(new VoiceBaseListClass());
Additional classes I have created but I am not quite there yet
public class VoiceBaseBaseClass
{
public string version { get; set; }
public string apikey { get; set; }
public string password { get; set; }
public VoiceBaseBaseClass()
{
this.version = "1.0";
this.apikey = "API";
this.password = "password";
}
}
public class VoiceBaseListClass : VoiceBaseBaseClass, IReturn<ResponseVoiceBaseListClass>
{
public string action { get; set; }
public string status { get; set; }
public VoiceBaseListClass()
: base()
{
this.action = "list";
this.status = "processing";
}
}
public class ResponseVoiceBaseListClass
{
public string requestStatus { get; set; }
public string statusMessage { get; set; }
public string fileStatus { get; set; }
public List<string> mediaIds { get; set; }
public ResponseVoiceBaseListClass()
{
this.mediaIds = new List<string>();
}
}
Using the above classes the call that goes to the server is
/json/syncreply/VoiceBaseListClass?action=list&status=processing&version=1.0&apikey=API&Password=password
Is there a way I can force the service stack client to go to the
/Services
instead of
/json/syncreply/VoiceBaseListClass
I found a way to do this and it is working great for me.
[RestService("/services", "GET")]
public class VoiceBaseListClass : VoiceBaseBaseClass, IReturn<ResponseVoiceBaseListClass>
{
}
Although this is a deprecated attribute - the new attribute is called Route
https://github.com/ServiceStack/ServiceStack/wiki/Release-Notes
Chris