How to always perform a function, regardless of the API route called? - asp.net-core-2.0

I am creating a NetCore 2 API and connecting to a SQL Server Database. Is it possible to always call a function no matter what the route is? My example:
For each call I am passing in the necessary parameters to connect to the SQL DB inside the headers. I am going to have hundreds of calls in this api, so I really don't want to duplicate this for every single call:
[HttpGet]
public async Task<IEnumerable<DEPARTMENT>> Get(
[FromHeader] string Server,
[FromHeader] string Database,
[FromHeader] string Username,
[FromHeader] string Password,
[FromHeader] string Trusted)
{
util.SetConnectionString(Server, Database, Username, Password, Trusted);
return await this.departmentDataProvider.GetDepartments();
}
[HttpGet("{DepartmentId}")]
public async Task<IEnumerable<DEPARTMENT>> Get(
string DepartmentId,
[FromHeader] string Server,
[FromHeader] string Database,
[FromHeader] string Username,
[FromHeader] string Password,
[FromHeader] string Trusted)
{
util.SetConnectionString(Server, Database, Username, Password, Trusted);
return await this.departmentDataProvider.GetDepartment(DepartmentId);
}
I would like to create an ancestor function that just has the header connection parameters, and the call to set the connection string. End goal would be something like this:
Ancestor:
[HttpGet]
public async Task<IEnumerable<?>> Get(
[FromHeader] string Server,
[FromHeader] string Database,
[FromHeader] string Username,
[FromHeader] string Password,
[FromHeader] string Trusted)
{
util.SetConnectionString(Server, Database, Username, Password, Trusted);
}
Children:
[HttpGet]
public async Task<IEnumerable<DEPARTMENT>> Get()
{
return await this.departmentDataProvider.GetDepartments();
}
[HttpGet("{DepartmentId}")]
public async Task<IEnumerable<DEPARTMENT>> Get(string DepartmentId)
{
return await this.departmentDataProvider.GetDepartment(DepartmentId);
}
Edit:
Adding Util.cs & DepartmentDataProvider.cs for more information.
Util.cs
public static class util
{
public static string ConnectionString { get; set; }
public static void SetConnectionString(string Server, string Database, string UserName, string Password, string Trusted)
{
if (Trusted == "true")
ConnectionString = "Server=" + Server + ";Database=" + Database + ";Trusted_Connection=True;";
else
ConnectionString = "Server=" + Server + ";Database=" + Database + ";User ID= " + UserName + ";Password=" + Password + ";Trusted_Connection=False;";
}
public static string GetConnectionString()
{
return ConnectionString;
}
}
DepartmentDataProvider.cs
public class DepartmentDataProvider : IDepartmentDataProvider
{
public async Task<DEPARTMENT> GetDepartment(int DepartmentId)
{
string connString = util.GetConnectionString();
using (var sqlConnection = new SqlConnection(util.GetConnectionString()))
{
await sqlConnection.OpenAsync();
var dynamicParameters = new DynamicParameters();
dynamicParameters.Add("#DepartmentID", DepartmentId);
return await sqlConnection.QuerySingleOrDefaultAsync<DEPARTMENT>(
"Select * From DEPARTMENT Where DEPARTMENT_ID = #DepartmentID",
dynamicParameters,
commandType: CommandType.Text);
}
}
}

Create a class to encapsulate the connection string data into which any relevant objects will be injected (e.g. maybe the HttpContext):
public class ConnectingStringParmsFromHeaders
{
public string Server { get; private set; }
// other properties
// ideally you wouldn't depend on ASP.NET Core constructs such as HttContext
// but I'm using it here for brevity
public ConnectionStringPropertiesFromHeaders( HttpContext httpContext )
{
ReadPropertiesFromHeaders( httpContext.Request.Headers );
}
private void ReadPropertiesFromHeaders( IHeaderDictionary headers )
{
// do your header-reading thing here
}
public string BuildConnectionString()
{
// your Util.SetConnectionString(...) code here
// using local instance properties and not static properties
}
}
Next, register this class with lifetime of Scoped (single object reused during the same HTTP request):
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<ConnectionStringParmsFromHeaders>();
}
Finally, add a parameter of this type to your controllers' constructors and set a property or field for use in action methods:
public class DepartmentsController : Controller
{
private ConnectionStringParmsFromHeaders ConnStrParms { get; private set; }
public DepartmentsController( ConnectionStringParmsFromHeaders connStrParms )
{
ConnStrParms = connStrParms;
}
public async Task<IActionResult> Get()
{
// for illustrative purposes
var connStr = connStrParms.BuildConnectionString();
// remainder of your action method's code
}
}
Note that your Util class setting a static property with per-http-request header data is a problem b/c there can be multiple concurrent threads reading and writing with that same property and different data - you'll have no idea if you're using the correct data! Move the connection string generating code to this new config class so that you can access it on a per-request basis as shown in above's code sample.
The above code may not be 100% accurate as I can't test it right now but it should be enough to get you where you need to go.

Related

Net 6 Using Model Binder to populate user parameter from context

I got a middleware in my application that reads a JWT token and adds to HttpContext.Items["User"] that user model.
I can read this during my request by reading it from the HttpContext.
What I was trying to do Is a model binder that automatically does that for me so the code looks cleaner.
Right now I have it working like the following:
[HttpPost]
[JwtAuthorize]
[Route("readerfrombinder")]
public async Task<IActionResult> ComesFromContext()
{
var user = Request.HttpContext.Items["User"] as TokenUser;
return Ok("You Have Access with binded user "+user.Id);
}
But what im trying to achieve is something like
[HttpPost]
[JwtAuthorize]
[Route("readerfrombinder")]
public async Task<IActionResult> ComesFromBind([ModelBinder] TokenUser comesFromBind)
{
return Ok("You Have Access with binded user "+comesFromBind.Id);
}
I wrote a binder to try this out:
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Login.Jwt;
public class UserBinder : IModelBinder
{
private readonly TokenUser _context;
public UserBinder(TokenUser context)
{
_context = context;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var model = bindingContext.HttpContext.Items["User"] as TokenUser;
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
And added this binder to my user object:
[ModelBinder(BinderType = typeof(UserBinder))]
public class TokenUser
{
public string Id { get; set; }
public string SessionTicket { get; set; }
}
But apparently I'm missing something.
Would appreciate any help ! Thanks !

How to make a Lightning Web Components?

I need to make a Lightning Web Components with this Apex Class but I don't know how to pass the data to the JS and the HTML and how to create that lwc. I want to make a lwc that shows every email received from a certain email every time that lwc components is used/called. Maybe is better to do an Aura Component? I don't know. Here's the code
public with sharing class Gmail {
public with sharing class GMResponseMessages {
public Integer resultSizeEstimate;
public GMThread[] messages;
}
public with sharing class GMThread {
public String id;
public String threadId;
}
public with sharing class GMMessage {
public String id;
public String threadId;
public String[] labelIds;
public String snippet;
public String historyId;
public String internalDate;
public GMMessagePart payload;
public String sizeEstimate;
public String raw;
}
public with sharing class GMMessagePart {
public String partId;
public String mimeType;
public String filename;
public Header[] headers;
public GMMessagePartBody[] body;
public GMMessagePart[] parts;
}
public with sharing class Header {
public String name;
public String value;
}
public with sharing class GMMessagePartBody {
public String attachmentId;
public String superinteger;
public String data;
}
#AuraEnabled
public static void getGmail_API() {
//GET https://gmail.googleapis.com/gmail/v1/users/{userId}/messages/{id}
Http http = new Http();
HTTPResponse response;
HttpRequest request;
String userEmail = 'myemail#gmail.com';
request = new HttpRequest();
request.setMethod('GET');
request.setEndpoint('callout:Gmail_API/gmail/v1/users/myemail#gmail.com/messages?q=from:othermail#mail.it');
response = http.send(request);
System.debug('START');
System.debug(response.getStatusCode());
if(response.getStatusCode() == 200) {
JSONParser parser = JSON.createParser(response.getBody());
System.debug(parser);
GMResponseMessages jsonResponse = (GMResponseMessages)JSON.deserialize(response.getBody(), GMResponseMessages.class);
System.debug(jsonResponse);
for(GMThread thread: jsonResponse.messages){
System.debug('THREAD FOR');
System.debug(thread);
Http httpMsg = new Http();
HTTPResponse responseMsg;
HttpRequest requestMsg;
requestMsg = new HttpRequest();
requestMsg.setMethod('GET');
requestMsg.setEndpoint('callout:Gmail_API/gmail/v1/users/myemail#gmail.com/messages/' + thread.id);
responseMsg = httpMsg.send(requestMsg);
if(responseMsg.getStatusCode() == 200) {
GMMessage jsonResponseMsg = (GMMessage)JSON.deserialize(responseMsg.getBody(), GMMessage.class);
System.debug(jsonResponseMsg);
}
}
}
}
}
You'll need to follow the documented pathways to call an Apex method in a Lightning Web Component.
You can do this by using an imperative call or a wire method. Since your Apex method takes no parameters, an imperative call is likely the way to go.

Error when adding Where or OrderBy clauses to Azure Mobile Apps request

I'm developing an Azure Mobile App service to interface to my Xamarin application.
I've created, connected and successfully populated an SQL Database, but when I try to add some filters to my request, for example an orderby() or where() clauses, it returns me a Bad Request error.
For example, this request: https://myapp.azurewebsites.net/tables/Race?$orderby=iRound%20desc,iYear%20desc&$top=1&ZUMO-API-VERSION=2.0.0 gives me {"message":"The query specified in the URI is not valid. Could not find a property named 'IYear' on type 'MyType'."}.
My configuration method is this:
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.AddTablesWithEntityFramework()
.ApplyTo(config);
config.MapHttpAttributeRoutes();
Database.SetInitializer(new CreateDatabaseIfNotExists<MainDataContext>());
app.UseWebApi(config);
and my DbContext is this:
public class MainDataContext : DbContext
{
private const string connectionStringName = "Name=MS_TableConnectionString";
public MainDataContext() : base(connectionStringName)
{
Database.Log = s => WriteLog(s);
}
public void WriteLog(string msg)
{
System.Diagnostics.Debug.WriteLine(msg);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
"ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
}
public DbSet<Race> Race { get; set; }
public DbSet ...ecc...
}
Following this guide, I added a migration after creating my TableControllers. So the TableController for the example type shown above is pretty standard:
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public class RaceController : TableController<Race>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MainDataContext context = new MainDataContext();
DomainManager = new EntityDomainManager<Race>(context, Request);
}
// GET tables/Race
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<Race> GetAllRace()
{
return Query();
}
// GET tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Race> GetRace(string id)
{
return Lookup(id);
}
// PATCH tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Race> PatchRace(string id, Delta<Race> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Race
public async Task<IHttpActionResult> PostRace(Race item)
{
Race current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteRace(string id)
{
return DeleteAsync(id);
}
}
As you can see, I already tried to add the EnableQuery attribute to my TableController, as seen on Google. I also tried to add these filters to the HttpConfiguration object, without any success:
config.Filters.Add(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
config.AddODataQueryFilter(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
I don't know what to investigate more, as things seems to be changing too fast for a newbie like me who's first got into Azure.
EDIT
I forgot to say that asking for the complete table, so for example https://myapp.azurewebsites.net/tables/Race?ZUMO-API-VERSION=2.0.0, returns correctly the entire dataset. The problem occurs only when adding some clauses to the request.
EDIT 2
My model is like this:
public class Race : EntityData
{
public int iRaceId { get; set; }
public int iYear { get; set; }
public int iRound { get; set; }
ecc..
}
and the database table that was automatically created is this, including all the properties inherited from EntityData:
Database table schema
Digging into the source code, Azure Mobile Apps sets up camelCase encoding of all requests and responses. It then puts them back after transmission accordign to rules - so iRaceId becomes IRaceId on the server.
The easiest solution to this is to bypass the auto-naming and use a JsonProperty attribute on each property within your server-side DTO and client-side DTO so that they match and will get encoding/decoded according to your rules.
So:
public class Race : EntityData
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("raceId")]
public int iRaceId { get; set; }
[JsonProperty("year")]
public int iYear { get; set; }
[JsonProperty("round")]
public int iRound { get; set; }
etc..
}

Abstract Azure IMobileServiceTable<T> behind repository

I want my repsository to be independent of the data access technology. Currently I am working on a Xamrin.Forms App that uses Azure Mobile App Services for data access. For performance and flexibility reasons I want my repository to look simmilar like the following:
Task<IEnumerable<IDomainObject>> GetDomainObjectAsync(Func<IQueryable<IDomainObject>, IQueryable<IDomainObject>> query)
Suppose my IDomainObject looks like the following:
public interface IDomainObject
{
string Name { get; }
}
and my DataAccess Object:
internal class AzureDomainObject : IDomainObject
{
public string Name { get; set; }
public string Id { get; set; }
}
As far as I found out and tested I can do the following to query the database within my repository implementation:
public async Task<IEnumerable<IDomainObject>> GetDomainObjectAsync(Func<IQueryable<IDomainObject>, IQueryable<IDomainObject>> query)
{
// _table of type IMobileServiceTable<AzureDomainObject> gotten by MobileServiceClient
var tableQuery = _table.GetQuery();
tableQuery.Query = tableQuery.Query.Take(4); // 1) this was for testing and it works (ordering etc also works)
// tableQuery.Query = query(tableQuery.Query); // 2) this was my initial idea how to use the input param
await _table.ReadAsync(tableQuery);
}
My poblem now is how to use the input param query to replace 1) with 2).
tableQuery.Query expects an IQueryable<AzureDomainObject> but query is of type IQueryable<IDomainObject>.
Neither .Cast<AzureDomainObject>() nor .OfType<AzureDomainObject>() work to convert. Nor does (IQueryable<IAzureDomainObject>)query; work.
Cast and OfType throw NotSupportedException and the hard cast throws an InvalidCastException.
I also tried to extract the Expression from the query input param and assign it to the tableQuery.Query. But then a runtime exception occurs that they are not compatible.
Another idea I had was to use the ReadAsync(string) overload and pass the string representation of the passed query param. But this way I don't know how to generate the string.
So the final question is: Does anyone knows how to hide both AzureDomainObject and IMobileServiceTable from the domain model but keep the flexibility and performance benefits of IQueryable in the repository interface?
According to your description, I checked this issue and here is my implementation for this scenario, you could refer to them.
Model:
public class TodoItem : IDomainObject
{
public string Id { get; set; }
[JsonProperty(PropertyName = "text")]
public string Text { get; set; }
[JsonProperty(PropertyName = "complete")]
public bool Complete { get; set; }
}
public interface IDomainObject
{
string Id { get; set; }
}
Repository:
public interface IAzureCloudTableRepository<T> where T : IDomainObject
{
Task<IEnumerable<T>> GetDomainObjectAsync(Func<IQueryable<T>, IQueryable<T>> query);
}
public class AzureCloudTableRepository<T> : IAzureCloudTableRepository<T> where T : IDomainObject
{
IMobileServiceTable<T> table;
public AzureCloudTableRepository(MobileServiceClient client)
{
this.table = client.GetTable<T>();
}
public async Task<T> CreateItemAsync(T item)
{
await table.InsertAsync(item);
return item;
}
public async Task<IEnumerable<T>> GetDomainObjectAsync(Func<IQueryable<T>, IQueryable<T>> query)
{
var tableQuery = this.table.CreateQuery();
tableQuery.Query = tableQuery.Query.Take(4); //the internal fixed query
tableQuery.Query = query(tableQuery.Query); //the external query
return await tableQuery.ToEnumerableAsync();
}
}
TEST:
var mobileService = new MobileServiceClient("https://{your-app-name}.azurewebsites.net");
var todoitem = new AzureCloudTableRepository<TodoItem>(mobileService);
var items = await todoitem.GetDomainObjectAsync((query) =>
{
return query.Where(q => q.Text!=null);
});

Load A Assembly in Runtime and call a Method and unload the assembly

Im creating an application, wich will conect to several sql database and get some details form the database,
In this application i have to encrypt the database connection details such as user name passwords. yes its pritty straight forward and simple just write a metod to decrypt the credentials.
but in my case i have to rely on third party encription mechanisam to decrypt the credentials. more over i have to connect to several sql servers which will again used some other encryption methods. hence im cording my application to load a encryption assembly dynamically and call the encryption method.
but when i load the assembly form Assembly.LoadFile("Path") i cannot unload the loaded assembly. i think i have load this assembly in separate app domain and call the relavant methods and unload that appdomain. im needing some help on this part. due to my lack of knoladge i cannot call the required method. my code as follows. please help me on this.
class ApplicationSettings
{
private static ApplicationSettings m_ApplicationSettings;
public String m_ServerName { get; private set; }
public String m_DatabaseName { get; private set; }
public String m_UserID { get; private set; }
public String m_Password { get; private set; }
public String m_EncryptionDLLPath{ get; private set; }
public String m_NameSpace { get; private set; }
public String m_ClassName { get; private set; }
public String m_EncryptionMethodName { get; private set; }
public String m_DecryptionMethodName { get; private set; }
private ApplicationSettings()
{
m_ApplicationSettings = this;
}
public static ApplicationSettings CurrentValues
{
get
{
return m_ApplicationSettings;
}
private set
{
m_ApplicationSettings = value;
}
}
internal static void Initialize()
{
CommonFunctions.DataEncryption _enc = new CommonFunctions.DataEncryption();
ApplicationSettings.CurrentValues = new ApplicationSettings();
ApplicationSettings.CurrentValues.m_EncryptionDLLPath = #"C:\Users\Administrator\Documents\Visual Studio 2010\Projects\TestApp\TestApp\bin\Debug\AppSec.dll";
ApplicationSettings.CurrentValues.m_NameSpace = "AppSec";
ApplicationSettings.CurrentValues.m_ClassName = "AppSecEncDec";
ApplicationSettings.CurrentValues.m_EncryptionMethodName = "Encrypt";
ApplicationSettings.CurrentValues.m_DecryptionMethodName = "Decrypt";
ApplicationSettings.CurrentValues.m_Password = _enc.Decrypt("pzBS3EJDoGM=");
ApplicationSettings.CurrentValues.m_UserID = "sa";
}
}
class DataEncryption
{
AppDomain DomainName;
//Call the Encryption Method
public String Encrypt(Object _DataToEncrypt)
{
}
//Call the Decryption Method
public String Decrypt(Object _DataToDecrypt)
{
String _Decrypt = "";
String assemblyFileName = ApplicationSettings.CurrentValues.m_EncryptionDLLPath;
String assemblyName = ApplicationSettings.CurrentValues.m_NameSpace;
//Setup the evidence
Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
AppDomain TestDomain = AppDomain.CreateDomain(
"TestDomain", //The friendly name of the domain.
evidence, //Evidence mapped through the security policy to establish a top-of-stack permission set.
AppDomain.CurrentDomain.BaseDirectory, // The base directory that the assembly resolver uses to probe for assemblies.
System.IO.Path.GetFullPath(assemblyFileName), // The path relative to the base directory where the assembly resolver should probe for private assemblies.
true // If true, a shadow copy of an assembly is loaded into this application domain.
);
string s = TestDomain.Load(assemblyName).FullName;
string[] myparam = new String[1];
myparam[0] = "test";
TestDomain.CreateInstance(TestDomain.Load(assemblyName).GetName().ToString(), ApplicationSettings.CurrentValues.m_NameSpace + "." + ApplicationSettings.CurrentValues.m_ClassName).CreateObjRef(GetType());
//her i need to execute the Encrypt method which will load form the third party encryption mechanisam
//method name will be returnd on this parameter in application settings Classes.ApplicationSettings.CurrentValues.m_EncryptionMethodName ;
UloadAssembly();
return _Decrypt;
}
public void UloadAssembly()
{
//Unload the loaded appdomain
AppDomain.Unload(DomainName);
GC.Collect();
}
}
Thanks in advance.
I have figured out how to do this and hope fully it will be successful please find the below code which if used to over come the situation
public String Encrypt(Object _DataToEncrypt)
{
try
{
String _Encrypt = "";
LoadAssembly();
ShowLoadedAssemblies();
if (ClassInstance != null)
{
MethodInfo EncryptionMethod = ClassInstance.GetType().GetMethod(Classes.ApplicationSettings.CurrentValues.m_EncryptionMethodName); ;
if (EncryptionMethod != null)
{
object[] myparam = new object[1];
myparam[0] = _DataToEncrypt;
_Encrypt = (string)EncryptionMethod.Invoke(null, myparam);
}
}
UloadAssembly();
ShowLoadedAssemblies();
return _Encrypt;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
//Call the Decryption Method
public String Decrypt(Object _DataToDecrypt)
{
String _Decrypt = "";
LoadAssembly();
ShowLoadedAssemblies();
if (ClassInstance != null)
{
MethodInfo DecryptionMethod = ClassInstance.GetType().GetMethod(Classes.ApplicationSettings.CurrentValues.m_DecryptionMethodName);;
if (DecryptionMethod != null)
{
object[] myparam = new object[1];
myparam[0] = _DataToDecrypt;
_Decrypt = (string)DecryptionMethod.Invoke(null, myparam);
}
}
UloadAssembly();
ShowLoadedAssemblies();
return _Decrypt;
}
//Loading the Assembly
public void LoadAssembly()
{
Evidence evi = new Evidence(AppDomain.CurrentDomain.Evidence);
DomainName = AppDomain.CreateDomain(Classes.ApplicationSettings.CurrentValues.m_NameSpace
, evi
, AppDomain.CurrentDomain.BaseDirectory
, Classes.ApplicationSettings.CurrentValues.m_EncryptionDLLPath
, true
);
String LoadingAssemblyName = AssemblyName.GetAssemblyName(Classes.ApplicationSettings.CurrentValues.m_EncryptionDLLPath).FullName;
ClassInstance = DomainName.CreateInstanceAndUnwrap(LoadingAssemblyName
, Classes.ApplicationSettings.CurrentValues.m_NameSpace
+ "."
+ Classes.ApplicationSettings.CurrentValues.m_ClassName
);
}
public void UloadAssembly()
{
//Unload the loaded appdomain
AppDomain.Unload(DomainName);
GC.Collect();
}

Resources