How can I call the method that is in my Hub from my controller on ASP.NET Core? - asp.net-mvc

I'm using SignalR and I my hub is on a folder called Hubs. I need to call the method that's in my Hub "PostMarker()" on my controller. So, everytime something is posted, the SignalR updates all windows open. How should I do that?
[HttpPost]
public async Task<IActionResult> PostOcorrencias([FromBody] Ocorrencias ocorrencia, IFormFile UploadImag)
{
ocorrencia.DataOcorrencia = DateTime.Now;
//processar a fotografia
//ocorrencia.Fotografia = "(nome fotografia)";
string caminho = "";
if (UploadImag == null)
{
return BadRequest(ModelState);
}
else
{
if (UploadImag.ContentType == "imagens/jpg" ||
UploadImag.ContentType == "imagens/png")
{
string extensao = Path.GetExtension(UploadImag.FileName).ToLower();
Guid g;
g = Guid.NewGuid();
string nome = g.ToString() + extensao;
caminho = Path.Combine(environment.WebRootPath,"imagens", nome);
ocorrencia.Fotografia = nome;
// Cria o ficheiro no sistema
using (var stream = new FileStream(caminho, FileMode.Create))
{
await UploadImag.CopyToAsync(stream);
}
}
}
_context.Ocorrencias.Add(ocorrencia);
//CALL HUB METHOD PostMarker()
await _context.SaveChangesAsync();
}
return CreatedAtAction("GetOcorrencias", new { id = ocorrencia.Id }, ocorrencia);
}
Here is my Hub:
public class MyHub : Hub
{
public async Task PostMarker()
{
await Clients.All.SendAsync("RedesignMap");
}
}

A SignalR IHubContext provides a means of sending messages to your clients outside of a Hub instance. The sample at https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-3.1 provides an example. Using the code there as a starting point, you can:
Inject the IHubContext into your controller, assuming that it is named HomeController:
public class HomeController : Controller
{
private readonly IHubContext<MyHub> _hubContext;
public HomeController(IHubContext<MyHub> hubContext)
{
_hubContext = hubContext;
}
}
Use the hub context in your PostOcorrencias controller action:
...
_context.Ocorrencias.Add(ocorrencia);
await _hubContext.Clients.All.SendAsync("RedesignMap");
await _context.SaveChangesAsync();
...

Related

Confusion on getting access token from google api with mvc

I've been trying to follow a number of tutorials I can find to have an mvc application allow a user to authenticate the app and get the access and refresh tokens back. Unfortunately I can't find any that are clear enough to where I can follow what's going on. I started with google's sample code and then found some others like this one and this one.
When I run my app I'm trying to go to http://localhost:61581/Integration/Google/IndexAsync it hits that method which eventually hits the AppFlowMetadata.GetUserId method and then hits my custom TenixDataStore class' GetAsync method.
The things that are confusing are
First off, am I going to the right url/method? I think I am based on google's code example but not sure.
I thought that the key I would get would be the email address but instead is a GUID. Is that how google identifies a user?
If I'm going to the right url, why does the page just hang and never return. I expected it to open a google authorization page which didn't happen.
Here's my code.
AppFlowMetadata class
using System.Web.Mvc;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Gmail.v1;
using Tenix.Domain.Constants;
namespace MyApp.Areas.Integration.Controllers
{
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = APIConstants.GMailApiKey,
ClientSecret = APIConstants.GmailApiSecret
},
Scopes = new[] {GmailService.Scope.GmailReadonly},
DataStore = new TenixDataStore()
});
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
public override string GetUserId(Controller controller)
{
// In this sample we use the session to store the user identifiers.
// That's not the best practice, because you should have a logic to identify
// a user. You might want to use "OpenID Connect".
// You can read more about the protocol in the following link:
// https://developers.google.com/accounts/docs/OAuth2Login.
var user = controller.Session["UserID"];
if (user == null) return null;
return user.ToString();
}
}
}
GoogleController
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Gmail.v1;
using Google.Apis.Services;
namespace MyApp.Areas.Integration.Controllers
{
public class GoogleController : Controller
{
public async Task IndexAsync(CancellationToken cancellationToken)
{
if (Session["UserID"] == null)
{
Response.Redirect("~/Login.aspx", true);
}
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
if (result.Credential != null)
{
var service = new GmailService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "Tenix Gmail Integration"
});
}
}
}
}
TenixDataStore class
using System;
using System.Threading.Tasks;
using DataBaseUtilitiesTEN;
using Google.Apis.Json;
using Google.Apis.Util.Store;
using Newtonsoft.Json.Linq;
using Synergy.Extensions;
using Tenix.Domain.Data.Respositories;
using Tenix.Domain.Model.Integration;
using Tenix.Domain.Services;
namespace MyApp.Areas.Integration.Controllers
{
public class TenixDataStore : IDataStore
{
private readonly string conStr = ConnectionStrings.GeneralInfo;
private CredentialService _service;
public TenixDataStore()
{
_service = new CredentialService(new CredentialRepository(conStr));
}
public Task StoreAsync<T>(string key, T value)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key MUST have a value");
var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
var jObject = JObject.Parse(serialized);
var access_token = jObject.SelectToken("access_token");
var refresh_token = jObject.SelectToken("refresh_token");
if (access_token == null)
throw new ArgumentException("Missing access token");
if (refresh_token == null)
throw new ArgumentException("Missing refresh token");
_service.SaveUserCredentials(new UserCredential
{
EmailAddress = key,
AccessToken = (string)access_token,
RefreshToken = (string)refresh_token
});
return Task.Delay(0);
}
public Task DeleteAsync<T>(string key)
{
_service.DeleteCredentials(key);
return Task.Delay(0);
}
public Task<T> GetAsync<T>(string userId)
{
var credentials = _service.GetUserCredentials(userId.To<int>());
var completionSource = new TaskCompletionSource<T>();
if (!string.IsNullOrEmpty(credentials.AccessToken))
completionSource.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(credentials.AccessToken));
return completionSource.Task;
}
public Task ClearAsync()
{
return Task.Delay(0);
}
}
}
AuthCallbackController
using Google.Apis.Auth.OAuth2.Mvc;
namespace MyApp.Areas.Integration.Controllers
{
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
{
protected override FlowMetadata FlowData
{
get { return new AppFlowMetadata(); }
}
}
}
After spending days trying to figure this out and not making any headway with the google api .net libraries I ended up just going with my own implementation which after reading their documentation was at least something I could fully understand. In case anyone could use the code, here's what I ended up with. Still need to do some refactoring, but at this point it's working.
Just need to make sure the AuthorizeResponse and Authorize routes are registered as authorized redirect uris.
public class GoogleController : Controller
{
private readonly CredentialService _credentialService;
private readonly GoogleEndpoints _endpoints;
public GoogleController()
{
_endpoints = new GoogleEndpoints();
_credentialService = new CredentialService(new CredentialRepository(ConnectionStrings.GeneralInfo));
}
private string AuthorizeUrl
{
get
{
return "/Integration/Google/Authorize";
}
}
private string AuthorizeResponseUrl
{
get
{
return "/Integration/Google/AuthorizeResponse";
}
}
private string SaveResponseUrl
{
get
{
return "/Integration/Google/SaveResponse";
}
}
public void Authorize()
{
if (Session["UserID"] == null || Session["Email"] == null)
{
Response.Redirect("~/Login.aspx", true);
Session["LoginSource"] = AuthorizeUrl;
Response.End();
}
else
{
if (Session["SessionId"] == null || Session["SessionId"].ToString().Trim().Length == 0)
Session["SessionId"] = _credentialService.CreateSessionId(Session["UserID"].To<int>());
var url = _endpoints.AuthorizationEndpoint + "?" +
"client_id=" + APIConstants.GMailApiKey + "&" +
"response_type=code&" +
"scope=openid%20email&" +
"redirect_uri=" + AuthorizeResponseUrl + "&" +
"state=" + Session["SessionId"] + "&" +
"login_hint=" + Session["Email"] + "&" +
"access_type=offline";
Response.Redirect(url);
}
}
public ActionResult AuthorizeResponse()
{
var state = Request.QueryString["state"];
if (state == Session["SessionId"].ToString())
{
var code = Request.QueryString["code"];
var values = new Dictionary<string, object>
{
{"code", code},
{"redirect_uri", AuthorizeResponseUrl},
{"client_id", APIConstants.GMailApiKey},
{"client_secret", APIConstants.GmailApiSecret},
{"grant_type", "authorization_code"},
{"scope", ""}
};
var webmethods = new WebMethods();
var tokenResponse = webmethods.Post(_endpoints.TokenEndpoint, values);
var jobject = JObject.Parse(tokenResponse);
var access_token = jobject.SelectToken("access_token");
var refresh_token = jobject.SelectToken("refresh_token");
if (access_token == null || access_token.ToString().Trim().Length == 0)
{
//notify devs something went wrong
return View(new GoogleAuthResponse(tokenResponse, false));
}
var credentials = _credentialService.GetUserCredentials(Session["SessionId"].ToString());
credentials.AccessToken = access_token.ToString();
credentials.RefreshToken = refresh_token.ToString();
credentials.EmployeeId = Session["UserId"].To<int>();
_credentialService.SaveUserCredentials(credentials);
return View(new GoogleAuthResponse("Integration successful!", true));
}
return View(new GoogleAuthResponse("Missing state information.", false));
}
}
And the helper class to get the google endpoints.
public class GoogleEndpoints
{
public GoogleEndpoints()
{
using (var client = new WebClient())
{
var response = client.DownloadString("https://accounts.google.com/.well-known/openid-configuration");
var jobject = JObject.Parse(response);
AuthorizationEndpoint = jobject.SelectToken("authorization_endpoint").ToString();
TokenEndpoint = jobject.SelectToken("token_endpoint").ToString();
}
}
public string AuthorizationEndpoint { get; private set; }
public string TokenEndpoint { get; private set; }
}
The controller uses another couple of helper classes for parsing the json and posting the form data, but that should be pretty straightforward.

Testing a Mvc controller that gets data from an API endpoint

I am working on a Web project that gets data from an API endpoint. The API layer sits on top of Service Layer and Repository Layer at the bottom. I have written unit testing for the Service and API Layers. I am using Moq framework to mock dependencies.
Now i want to test the MVC controller. I am using a Request Manager class which is derived from HttpClient to get data from the API endpoints. So how do i test this controller. I have written a unit test but the test is getting data directly from my Database.
public class UserController : Controller
{
private RequestManager requestManager = new RequestManager();
// GET: User
public async Task<ActionResult> Index()
{
List<UserViewModel> allUsers = await requestManager.GetUsers();
if(allUsers == null)
{
throw new HttpException(404, "No Users Found");
}
return View(allUsers);
}
}
public class UserControllerTest
{
public UserController controller;
[OneTimeSetUp]
public void InIt()
{
controller = new UserController();
}
[Test]
public async Task TestIndex()
{
var view = await controller.Index() as ActionResult;
Assert.That(view is ViewResult);
Assert.That(view.Model is List<UserViewModel>);
}
}
You should decouple controller and manager. Extract interface from RequestManager and inject it into controller. That should not be a problem, Ioc container can do that for you.
public class UserController : Controller
{
private RequestManager _requestManager;
public UserController(IRequestManager requestManager)
{
_requestManager = requestManager;
}
// GET: User
public async Task<ActionResult> Index()
{
List<UserViewModel> allUsers = await _requestManager.GetUsers();
if(allUsers == null)
{
throw new HttpException(404, "No Users Found");
}
return View(allUsers);
}
}
Then in your unit tests you can mock IRequestManager.
public class UserControllerTest
{
[Test]
public async Task TestIndex()
{
//arrange
Mock<IRequestManager> requestManager = new Mock<IRequestManager>();
//setup what you want here...
UserController sut = new UserController(requestManager.Object);//suggest to use AutoMoqer for this.
//act
var view = await sut.Index() as ActionResult;
//assert
Assert.That(view is ViewResult);
Assert.That(view.Model is List<UserViewModel>);
}
}
Try this one..
public class UserController : Controller
{
private RequestManager requestManager = new RequestManager();
Mock<RequestManager> mockRepository = new Mock<RequestManager>();
Mock<UserViewModel> mockUserViewModel = new Mock<UserViewModel>();
ViewResult viewResult;
// GET: User
public async Task<ActionResult> Index()
{
List<UserViewModel> allUsers = await requestManager.GetUsers();
if(allUsers == null)
{
throw new HttpException(404, "No Users Found");
}
return View(allUsers);
}
}
public class UserControllerTest
{
public UserController controller;
[OneTimeSetUp]
public void InIt()
{
controller = new UserController();
}
[Test]
public async Task TestIndexWhenAllUsersNULL()
{
var view = await controller.Index() as ActionResult;
List<mockUserViewModel> listofusermodel = new List<mockUserViewModel>();
//add some dummy data in your List so it will not getting data directly from your Database
mockRepository.Setup(x => requestManager.GetUsers()).Returns(listofusermodel);
Assert.That(view is ViewResult);
Assert.That(view.Model is List<mockUserViewModel>);
}
}

MVC 6 How can I include a BaseRepository in my controller class

I am using an ORM to connect to the database it is called dapper. The issue with it is that it's database calls are synchronous and I recently found a way to make it asynchronous by following this short tutorial http://www.joesauve.com/async-dapper-and-async-sql-connection-management/ . My question is how can I bring this BaseRepository into my Controller class ? This is the code on that website and it's the same one I have
BaseRepository- by the way there is no issue in this code
public abstract class BaseRepository
{
private readonly string _ConnectionString;
protected BaseRepository(string connectionString)
{
_ConnectionString = connectionString;
}
protected async Task<T> WithConnection<T>(Func<IDbConnection, Task<T>> getData)
{
try {
using (var connection = new SqlConnection(_ConnectionString)) {
await connection.OpenAsync(); // Asynchronously open a connection to the database
return await getData(connection); // Asynchronously execute getData, which has been passed in as a Func<IDBConnection, Task<T>>
}
}
catch (TimeoutException ex) {
throw new Exception(String.Format("{0}.WithConnection() experienced a SQL timeout", GetType().FullName), ex);
}
catch (SqlException ex) {
throw new Exception(String.Format("{0}.WithConnection() experienced a SQL exception (not a timeout)", GetType().FullName), ex);
}
}
}
and now he brings it in like this
public class PersonRepository : BaseRepository
{
public PersonRepository(string connectionString): base (connectionString) { }
public async Task<Person> GetPersonById(Guid Id)
{
return await WithConnection(async c => {
// Here's all the same data access code,
// albeit now it's async, and nicely wrapped
// in this handy WithConnection() call.
var p = new DynamicParameters();
p.Add("Id", Id, DbType.Guid);
var people = await c.QueryAsync<Person>(
sql: "sp_Person_GetById",
param: p,
commandType: CommandType.StoredProcedure);
return people.FirstOrDefault();
});
}
}
The part I am having a problem with is this public class PersonRepository : BaseRepository because Asp.Net Controllers start with public class HomeController: Controller , I need access to the WithConnection method to get this working. My controller looks like this
public class HomeController : Controller
{
public class ConnectionRepository : BaseRepository
{
public ConnectionRepository(string connectionString) : base(connectionString) { }
}
public async Task<ActionResult> topfive()
{
// I get Error on WithConnection as it can't see the BaseRepository
return await WithConnection(async c =>
{
var topfive = await c.QueryAsync<Streams>("select * from streams ").ToList();
return View(topfive);
});
}
}
I obviously can not cover my ActionResult method with the BaseRepository because it gives all types of errors any suggestions ?
Why are you using inheritance instead of composition? What about something like:
public class PersonRepository : BaseRepository
{
public PersonRepository(string connectionString): base (connectionString) { }
public async Task<Person> GetPersonById(Guid Id)
{
return await WithConnection(async c => {
// Here's all the same data access code,
// albeit now it's async, and nicely wrapped
// in this handy WithConnection() call.
var p = new DynamicParameters();
p.Add("Id", Id, DbType.Guid);
var people = await c.QueryAsync<Person>(
sql: "sp_Person_GetById",
param: p,
commandType: CommandType.StoredProcedure);
return people.FirstOrDefault();
});
}
}
public class ConnectionRepository : BaseRepository
{
public ConnectionRepository(string connectionString) : base(connectionString) { }
}
public async Task<List<TopFileClass>> topfive()
{
// I get Error on WithConnection as it can't see the BaseRepository
return await WithConnection(async c =>
{
var topfive = await c.QueryAsync<Streams>("select * from streams ").ToList();
return topfive;
});
}
public class HomeController : Controller
{
private readonly PersonRepository _repo;
public HomeController(PersonRepository repo)
{
_repo = repo;
}
public async Task<ActionResult> TopFive()
{
var top5 = await _repo.topfive();
return View(top5);
}
}
If you are not familiar how to make the repository automatically get injected into the constructor, read up on dependency injection in MVC 6.
you have to intehirt the "BaseRepository" from "Controller". i think that will work for you. then just go with below code:
public abstract class BaseRepository : Controller
{
// do you work
}
public class PersonRepository : BaseRepository
{
public PersonRepository(string connectionString): base (connectionString) { }
public async Task<Person> GetPersonById(Guid Id)
{
return await WithConnection(async c => {
// Here's all the same data access code,
// albeit now it's async, and nicely wrapped
// in this handy WithConnection() call.
var p = new DynamicParameters();
p.Add("Id", Id, DbType.Guid);
var people = await c.QueryAsync<Person>(
sql: "sp_Person_GetById",
param: p,
commandType: CommandType.StoredProcedure);
return people.FirstOrDefault();
});
}
}

My controller is not able to retrieve my session list from a Repository file

I have created a repository in this application and a session list is created in order to keep data in memory. I am not able to retrieve my list from the controller in order to populate a grid view which I have.
This is my Repository
namespace ClientSearch.Repository
{
public class Repo
{
public HttpSessionStateBase session;
public Repo (HttpSessionStateBase session)
{
this.session = session;
}
public void SetClient(ClientInfo client)
{
List<ClientInfo> mylist = new List<ClientInfo>();
mylist = (List<ClientInfo>)this.session["test"];
}
public List<ClientInfo> GetClient()
{
return (List<ClientInfo>)session["list"];
}
}
}
This is my Controller function
public ActionResult SearchClient()
{
HttpSessionStateBase session = HttpContext.Session;
Repo repo = new Repo(session);
var result = repo.GetClient();
return Json(new
{
list = result,
count = result.Count
}, JsonRequestBehavior.AllowGet);
}
my setting control
public JsonResult ClientInformation(ClientInfo client)
{
JsonResult result = new JsonResult();
result.Data = client;
result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return result;
}

web api call from code behind

i am very new to mvc web api
I have crated a web api Post method which takes an object type "Bag" and return a HTMLString the code is as shown bellow
public HtmlString PostBag(Bag bagofItem)
{
return Utility.PostBagDiscountedItem(bagofItem);
}
now from my web site i wanted to call the API method PostBag from the controller PostBag()
and i am do not know how to and appreciate if some one can show me how to do this
what i have got in my web application is as bellow.
public class HomeController : Controller
{
private Bag _bag = new Bag();
private string uri = "http://localhost:54460/";
public ActionResult PostBag()
{
// would some one show me how to POST the _bag to API Method PostBag()
return View();
}
public class Bag
{
private static List<Product> _bag { get; set; }
public List<Product> GetBag ()
{
if (_bag == null)
_bag = new List<Product>();
return _bag;
}
}
Try this:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:54460/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(_bag);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Success");
}
else
{
Console.WriteLine("Error with feed");
}
}

Resources