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>();
Related
When I am passing a single object like below then it is working as per below image
[HttpPost]
public async Task<ActionResult> Post([FromForm] MyModel Details)
{
}
but when I am passing the List of the object to API then it is not working. option to upload a file is not visible. and if I entered any values in the array then also I am getting count 0 for details.
[HttpPost]
public async Task<ActionResult> Post([FromForm] List<MyModel> Details)
{}
I want to pass the List of images and descriptions to API. How can I achieve it?
Thanks in advance!
You need custom model binding for the list model . Here is a similar demo:
custom model binding code:
public class MetadataValueModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
var values = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (values.Length == 0)
return Task.CompletedTask;
var options = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true };
var deserialized = JsonSerializer.Deserialize(values.FirstValue, bindingContext.ModelType, options);
bindingContext.Result = ModelBindingResult.Success(deserialized);
return Task.CompletedTask;
}
}
Add the model binder to the model class:
public class MasterDTO
{
public string Comments { get; set; }
public IFormFile File { get; set; }
public List<DetailDTO> Details { get; set; }
public MasterDTO()
{
this.Details = new List<DetailDTO>();
}
}
[ModelBinder(BinderType = typeof(MetadataValueModelBinder))]
public class DetailDTO
{
public Int64 ElementId { get; set; }
public double LowerLimit { get; set; }
public double HigherLimit { get; set; }
public string Status { get; set; }
public string UserAuthorization { get; set; }
public DateTime? AutorizationDate { get; set; }
}
controller/action
[HttpPost]
public async Task<IActionResult> CreateProjectLimit([FromForm] MasterDTO masterDto)
{
//...
return Ok();
}
You can just use postman to pass the list of images and Descriptions to API
Below is the right answer. we can use Postman to pass images in the array as shown below.
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 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.
I am new to .Net Core development. I have a model:
public class CoreGoal
{
[Key]
public long CoreGoalId { get; set; }
public string Title { get; set; }
public string Effect { get; set; }
public string Target_Audience { get; set; }
public string Infrastructure { get; set; }
public virtual ICollection<Image> Images { get; set; }
public CoreGoal()
{
}
}
And Image model is as following:
public class Image
{
[Key]
public long ImagelId { get; set; }
public string Base64 { get; set; }
[ForeignKey("CoreGoalId")]
public long CoreGoalId { get; set; }
public Image()
{
}
}
I am using Repository pattern. My repository:
public interface ICoreGoalRepository
{
void CreateCoreGoal(CoreGoal coreGoal);
}
public class CoreGoalRepository : ICoreGoalRepository
{
private readonly WebAPIDataContext _db;
public CoreGoalRepository(WebAPIDataContext db)
{
_db = db;
}
//Find specific
public CoreGoal Find(long key)
{
return _db.CoreGoals.FirstOrDefault(t => t.CoreGoalId == key);
}
//Add new
public void CreateCoreGoal(CoreGoal coreGoal)
{
_db.CoreGoals.Add(coreGoal);
_db.SaveChanges();
}
}
And controller:
[Route("api/[controller]")]
public class CoreGoalController : Controller
{
private readonly ICoreGoalRepository _coreGoalRepository;
//Controller
public CoreGoalController(ICoreGoalRepository coreGoalRepository) {
_coreGoalRepository = coreGoalRepository;
}
[HttpGet("{id}", Name = "GetCoreGoal")]
public IActionResult GetById(long id)
{
var item = _coreGoalRepository.Find(id);
if (item == null)
{
return NotFound();
}
return new ObjectResult(item);
}
//Create
[HttpPost]
public IActionResult Create([FromBody] CoreGoal item)
{
if (item == null)
{
return BadRequest();
}
_coreGoalRepository.CreateCoreGoal(item);
return CreatedAtRoute("GetCoreGoal", new { id = item.CoreGoalId }, item);
}
}
On POST request for CoreGoal- While creating a new CoreGoal, I would like to convert Image model's Base64 attribute from string to byte[]. I found this (https://adrientorris.github.io/aspnet-core/manage-base64-encoding.html) blogpost, but I am not sure where Am I supposed to write this piece of code.
Can someone help me?
Initially you should chage you database model to save you binary image to db (also, it's still not good idea, but let leave it for a now):
public class Image
{
[Key]
public long ImagelId { get; set; }
[NotMapped]
public string Base64 { get; set; }
public byte[] Binary {get; set;}
[ForeignKey("CoreGoalId")]
public long CoreGoalId { get; set; }
public Image()
{
}
}
next you just should convert your image inside controller:
[HttpPost]
public IActionResult Create([FromBody] CoreGoal item)
{
if (item == null)
{
return BadRequest();
}
item.Binary = Convert.FromBase64String(item.Base64);
_coreGoalRepository.CreateCoreGoal(item);
return CreatedAtRoute("GetCoreGoal", new { id = item.CoreGoalId }, item);
}
BTW:you code still not good. It's not necessary to use Repository pattern with EF core (https://www.thereformedprogrammer.net/is-the-repository-pattern-useful-with-entity-framework-core/). And you should introduce two model layers: public layer and model layer. You shouldn't expose EF Core contract to outside.