WebApi with Axios - rest

Goal:
Use Axios with put and post method from react TS to Backend WebApi c#.
Problem:
The code doesn't work in relation to CORS.
What part from the backend source code am I missing in order to make backend to retrieve data that is PUT or POST?
Thank you!
React TS
import React from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';
function App() {
const get = () => {
axios.get("https://localhost:7177/WeatherForecast/GetTestData")
.then(
response => console.log(response)
);
};
const update = () => {
const data =
{
CandyNumber: Number("1"),
Name: "asdf"
};
axios.put("https://localhost:7177/WeatherForecast/UpdateTestData", data)
.then(
response => console.log(response)
);
};
const add = () => {
const data =
{
CandyNumber: Number("1"),
content: "asdf"
};
axios.post("https://localhost:7177/WeatherForecast/AddTestData", data)
.then(
response => console.log(response)
);
};
return (
Get
Add
Uppdate
);
}
export default App;
backend
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace WebTest2.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet("GetTestData", Name = "GetTestData")]
[ProducesResponseType(typeof(List<Test>), StatusCodes.Status200OK)]
[ProducesResponseType(204)]
[Produces("application/json")]
public async Task<IActionResult> GetTestData()
{
List<Test> mytest = new List<Test>();
Test myTest = new Test()
{
CandyNumber = 1,
Name = "asdf"
};
Test myTest2 = new Test()
{
CandyNumber = 2,
Name = "asdf"
};
mytest.Add(myTest);
mytest.Add(myTest2);
return Ok(mytest);
}
[HttpPost("AddTestdata", Name = "AddTestdata")]
public async Task<IActionResult> AddTestdata(Test test)
{
List<Test> mytest = new List<Test>();
Test myTest = new Test()
{
CandyNumber = 1,
Name = "asdf"
};
Test myTest2 = new Test()
{
CandyNumber = 2,
Name = "asdf"
};
mytest.Add(myTest);
mytest.Add(myTest2);
return StatusCode(StatusCodes.Status204NoContent, null);
}
[HttpPut("UpdateTestdata", Name = "UpdateTestdata")]
public async Task<IActionResult> UpdateTestdata(Test test)
{
return StatusCode(StatusCodes.Status204NoContent, null);
}
}
public class Test
{
public int CandyNumber { get; set; }
public string Name { get; set; }
}
}
Program.cs
using Microsoft.OpenApi.Models;
using System.Reflection;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = ""
});
});
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAllHeaders",
corsbuilder =>
{
corsbuilder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
.WithOrigins("http://localhost:3000/");
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
//--
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

Add
app.UseCors(x => x
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(origin => true) // allow any origin
.AllowCredentials()); // allow credentials
after " app.UseSwaggerUI();"

Related

Maui - FilePicker - Return TextFile FullPath Asynchronously

The goal is to have a Maui class library that will have a function that return a full path to read a text file in my Maui application.
Can you help me to fix the following code?
The error is when I try to return the FullPath as string
var FileFullPath = await result.FullPath.ToString();
Here the error description
Severity Code Description Project File Line Suppression State Error CS1061
'string' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) MPCFilePickerMauiLibrary (net7.0), MPCFilePickerMauiLibrary (net7.0-android), MPCFilePickerMauiLibrary (net7.0-ios), MPCFilePickerMauiLibrary (net7.0-maccatalyst) D:\repos\MPC-MassPropertiesCalculator\MPCFilePickerMauiLibrary\PickTxtFile.cs 35 Active
Here is the Maui class library that have created.
using Microsoft.Maui.Storage;
namespace MPCFilePickerMauiLibrary;
//Ref https://youtu.be/C6LV_xMGdKc - Intro To Class Libraries in C#
public class PickTxtFile
{
public static async Task<string> GetFilePathAsync()
{
//For custom file types
var customFileType = new FilePickerFileType(
new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.iOS, new[] { "public.text" } }, // UTType values
{ DevicePlatform.Android, new[] { "text/plain" } }, // MIME type
{ DevicePlatform.WinUI, new[] { ".Txt" } }, // file extension
{ DevicePlatform.Tizen, new[] { "*/*" } },
{ DevicePlatform.macOS, new[] { "Txt" } }, // UTType values
});
var result = await FilePicker.PickAsync(new PickOptions
{
PickerTitle = "Pick MPC Demo file Please",
FileTypes = customFileType
});
if (result == null)
return "";
var FileFullPath = await result.FullPath.ToString();
return FileFullPath;
}
Here is the code where I'm using the function
using MPCFilePickerMauiLibrary;
using Microsoft.UI.Xaml.Controls;
namespace MPC_MassPropertiesCalculator_MAUIapp.Views;
public partial class MPCFileDemo : ContentPage
{
public MPCFileDemo()
{
InitializeComponent();
}
private void MenuFlyoutItem_Clicked(object sender, EventArgs e)
{
String filePath = PickTxtFile.GetFilePathAsync();
if (File.Exists(filePath))
{
//TODO Read file
}
}
}
With Jason's comments, here is the solution.
Maui class library
using Microsoft.Maui.Storage;
namespace MPCFilePickerMauiLibrary;
//Ref https://youtu.be/C6LV_xMGdKc - Intro To Class Libraries in C#
public class PickTxtFile
{
public static async Task<string> GetFilePathAsync()
{
//For custom file types
var customFileType = new FilePickerFileType(
new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.iOS, new[] { "public.text" } }, // UTType values
{ DevicePlatform.Android, new[] { "text/plain" } }, // MIME type
{ DevicePlatform.WinUI, new[] { ".Txt" } }, // file extension
{ DevicePlatform.Tizen, new[] { "*/*" } },
{ DevicePlatform.macOS, new[] { "Txt" } }, // UTType values
});
var result = await FilePicker.PickAsync(new PickOptions
{
PickerTitle = "Pick MPC Demo file Please",
FileTypes = customFileType
});
if (result == null)
return "";
var FileFullPath = result.FullPath.ToString();
return FileFullPath;
}
}
Code in the application
using MPCFilePickerMauiLibrary;
namespace MPC_MassPropertiesCalculator_MAUIapp.Views;
public partial class MPCFileDemo : ContentPage
{
public MPCFileDemo()
{
InitializeComponent();
}
private async void MenuFlyoutItem_Clicked(object sender, EventArgs e)
{
string? filePath = await PickTxtFile.GetFilePathAsync();
if (File.Exists(filePath))
{
await DisplayAlert("File Path", $"File Path is: {filePath}", "OK");
}
else
{
await DisplayAlert("File Path", "Usre did not select a file", "OK");
}
}
}
Output result image.

How to set run time variable to postgresql in typeorm and nest js

Iam using the row level security in supabase with nest.js, So how can I set runtime variables safely to the DB so that I can be sure that the variables sync with each app user (due to the http request triggered the execution)?
I saw that it is possible to set local variables in a transaction but I wouldn't like to wrap all the queries with transactions.
Thanks & Regards
I tried to execute this with subscribers in nestjs it working fine . but it wont have a function like beforeSelect or beforeLoad , so i drop it
import { Inject, Injectable, Scope } from '#nestjs/common';
import { InjectDataSource } from '#nestjs/typeorm';
import { ContextService } from 'src/context/context.service';
import { DataSource, EntityManager, LoadEvent, RecoverEvent, TransactionRollbackEvent, TransactionStartEvent } from 'typeorm';
import {
EventSubscriber,
EntitySubscriberInterface,
InsertEvent,
UpdateEvent,
RemoveEvent,
} from 'typeorm';
#Injectable()
#EventSubscriber()
export class CurrentUserSubscriber implements EntitySubscriberInterface {
constructor(
#InjectDataSource() dataSource: DataSource,
private context: ContextService,
) {
dataSource.subscribers.push(this);
}
async setUserId(mng: EntityManager, userId: string) {
await mng.query(
`SELECT set_config('request.jwt.claim.sub', '${userId}', true);`,
);
}
async beforeInsert(event: InsertEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeTransactionRollback(event: TransactionRollbackEvent) {
console.log('hello')
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeUpdate(event: UpdateEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeRemove(event: RemoveEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
}
After i get to know that we can use query runner instead of subscriber . but its not working ,
also i need a common method to use all the queries
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Users } from 'src/common/entities';
import { DataSource, EntityManager, Repository } from 'typeorm';
#Injectable()
export class UsersService {
constructor(
#InjectRepository(Users) private userRepository: Repository<Users>,
private dataSource: DataSource,
private em: EntityManager,
) {}
getAllUsers(userId: string) {
const queryRunner = this.dataSource.createQueryRunner();
return new Promise(async (resolve, reject) => {
let res: any;
try {
await queryRunner.connect();
await queryRunner.manager.query(
// like this we can set the variable
`SELECT set_config('request.jwt.claim.sub', '${userId}', true);`,
);
// after setting config variable the query should return only one user by userId
res = await queryRunner.query('SELECT * FROM users');
// but it reurns every user
} catch (err) {
reject(err);
} finally {
await queryRunner.manager.query(`RESET request.jwt.claim.sub`);
await queryRunner.release();
resolve(res);
}
});
}
}
Thanks in advance....
Sorry to say, bro. But in currently state of development TypeORM does not have a feature that let us set conection variables. The roundabout for your problem is to do something like this.
/**
* Note: Set current_tenant session var and executes a query on repository.
* Usage:
* const itens = = await tenantTransactionWrapper( manager => {
* return manager.getRepository(Entity).find();
* });
*
* #param {function} callback - a function thar receives an Entity Manager and returns a method to be executed by tenantTransactionWrapper
* #param {string} providedTenantId - optional tenantId, otherwise tenant will be taken from localStorage
*/
async function tenantWrapper<R>(
callback: (manager: EntityManager) => Promise<R>,
providedTenantId?: string,
) {
const tenantId = providedTenantId || tenantStorage.get();
let response: R;
await AppDataSource.transaction(async manager => {
await manager.query(`SET LOCAL smsystem.current_tenant='${tenantId}';`);
response = await callback(manager);
});
return response;
}
Then create a custom repository to make use of the wraper a little bit simple.
const customRepository = <T>(entity: EntityTarget<T>) => ({
find: (options?: FindManyOptions<T>) =>
tenantTransactionWrapper(mng => mng.getRepository(entity).find(options))(),
findAndCount: (options?: FindManyOptions<T>) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).findAndCount(options),
)(),
save: (entities: DeepPartial<T>[], options?: SaveOptions) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).save(entities, options),
)(),
findOne: (options: FindOneOptions<T>) =>
tenantTransactionWrapper(async mng =>
mng.getRepository(entity).findOne(options),
)(),
remove: (entities: T[], options?: RemoveOptions) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).remove(entities, options),
)(),
createQueryBuilder: () => {
throw new Error(
'Cannot create queryBuilder for that repository type, instead use: tenantWrapper',
);
},
tenantTransactionWrapper,
});
And finally use our customRepository :
class PersonsRepository implements IPersonsRepository {
private ormRepository: Repository<Person>;
constructor() {
this.ormRepository = AppDataSource.getRepository<Person>(Person).extend(
customRepository(Person),
);
}
public async create(data: ICreatePersonDTO): Promise<Person> {
const newPerson = this.ormRepository.create(data);
await this.ormRepository.save(newPerson);
return newPerson;
}
public async getAll(relations: string[] = []): Promise<Person[]> {
return this.ormRepository.find({ relations });
}
I hope this may help someone and will be very glad if someone provides a better solution.
First you have to create a custom class for wrapping your userId or any stuff
custome_service.ts ==>
#Injectable()
export class UserIdWrapper {
constructor(private dataSource: DataSource) {}
userIdWrapper = (callback: (mng: QueryRunner) => Promise<any>, userId: string) => {
const queryRunner = this.dataSource.createQueryRunner();
return new Promise(async (resolve, reject) => {
let res: any;
try {
await queryRunner.connect();
await queryRunner.manager.query(
`SELECT set_config('your_variable_name', '${userId}', false)`,
);
//here is your funciton your calling in the service
res = await callback(queryRunner);
} catch (err) {
reject(err);
} finally {
await queryRunner.manager.query(`RESET your_variable_name`);
await queryRunner.release();
resolve(res);
}
});
};
}
Now here you have to call the function inside user service
user.service.ts ==>
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Users } from 'src/common/entities';
import { UserIdWrapper } from 'src/common/local-settup/userId_wrapper';
import { DataSource, EntityManager, QueryRunner, Repository } from 'typeorm';
#Injectable()
export class UsersService {
constructor(
#InjectRepository(Users) private userRepository: Repository<Users>,
private dataSource: DataSource,
private userIdWrapper: UserIdWrapper
) {}
async getAllUsers(userId: string) {
//This is your call back funciton that have to pass
const findOne = async (queryRunner: QueryRunner) => {
const res = await queryRunner.query('SELECT * FROM public.users');
return res;
};
try {
//hear we are passing the function in to the class funciton
return this.userIdWrapper.userIdWrapper(findOne, userId);
} catch (err) {
console.log(err);
}
}
}
Dont forgot to provide the custom class service inside the provider of user service.

I am using c# services and controllers with mongodb , facing issue in patch request I can patch using JsonPatchDocument but cannot save to mongo db

I am using MongoDB. I can post put and delete also get, can patch using JsonPatchDocument but face issue when trying to update in db.
Last Methods in UserService and UserController, which I want make for patch using JsonPatchDocument
This is my appsetting.json file
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"HealthCareDatabase": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "HealthApp",
"UserCollectionName":"User",
"JobCollectionName":"Jobs",
"JobApplicationCollectionName":"jobApplications",
"EducationCollectionName":"Education",
"ExperienceCollectionName":"Experience",
"TestCollectionName":"Test"
}
}
UserServices.cs
using backend.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using Microsoft.AspNetCore.JsonPatch;
namespace backend.Services;
public class UserService :ControllerBase
{
private readonly IMongoCollection<User> _userCollection;
public UserService(
IOptions<HealthCareDbSettings> HealthCareDbSettings)
{
var mongoClient = new MongoClient(
HealthCareDbSettings.Value.ConnectionString);
var mongoDatabase = mongoClient.GetDatabase(
HealthCareDbSettings.Value.DatabaseName);
_userCollection = mongoDatabase.GetCollection<User>(
HealthCareDbSettings.Value.UserCollectionName);
}
// User Services are down there
public async Task<List<User>> GetAsync() =>
await _userCollection.Find(_ => true).ToListAsync();
public async Task<User?> GetAsync(string id) =>
await _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
public User Login(string email, string password)
{
var user = _userCollection.Find(user => user.Email == email ).FirstOrDefault();
bool isPasswordValid = BCrypt.Net.BCrypt.Verify(password,user.Password);
if (isPasswordValid)
{
return user;
}
else{
return null;
}
}
public async Task CreateUser(User newUser) =>
await _userCollection.InsertOneAsync(newUser);
public async Task UpdateUser(string id,User user) =>
await _userCollection.ReplaceOneAsync(user => user.Id == id,user);
// This method has error
public async Task<User> updateUser(string id, JsonPatchDocument<User> patchDoc) {
var fromDb = await _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
var filter = _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
patchDoc.ApplyTo(fromDb,ModelState);
_userCollection.UpdateOne(filter,fromDb);
var user = await _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
return user;
}
}
UserController.cs
using Microsoft.AspNetCore.Mvc;
using backend.Services;
using backend.Models;
using Microsoft.AspNetCore.JsonPatch;
namespace backend.Controllers;
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly UserService _userService;
public UserController(UserService userservice)
{
_userService = userservice;
}
public string err = "Invalid Credentials";
[HttpGet]
public async Task<List<User>> Get() =>
await _userService.GetAsync();
[HttpGet("{id:length(24)}")]
public async Task<ActionResult<User>> Get(string id)
{
var user = await _userService.GetAsync(id);
if(user is null){
return NotFound();
}
return user;
}
[HttpPost("Login")]
public User userLogin([FromBody] Login login)
{
User usr = _userService.Login(login.email,login.password);
if(usr != null){
return usr;
}else {
return null;
}
}
// Creating New User
[HttpPost]
public async Task<IActionResult> Post(User newUser)
{
var usr = newUser;
var hashed = BCrypt.Net.BCrypt.HashPassword(newUser.Password);
usr.Password = hashed;
await _userService.CreateUser(usr);
return CreatedAtAction(nameof(Get), new { id = usr.Id }, usr);
}
//Update User
// [HttpPatch("up/{id:length(24)}")]
// public Task<User> update(string id,[FromBody] JsonPatchDocument<User> patchDoc)
// {
// var usr = _userService.GetAsync(id);
// return _userService.updateUser(id,patchDoc);
// }
}

In AspBoilerPlate - Unauthorized error when calling from Angular when Windows Authentication is On

Have already raised this before and thought I have addressed it as per what suggested on THIS and THIS but seems not!
I am using ABP template (Angular and ASP .NET CORE Application) on Full .Net Framework. I simply want to use Windows Authentication to Authenticate user.
I added [Authorize] to the Authenticate in the TokenAuthController and have finally got the HttpContext.User.Identity.Name populated but only when I call the Authenticate from the Swagger (http://localhost:21021/swagger). But I am getting Unauthorized error when calling the method from Angular (login.service.ts):
POST http://localhost:21021/api/TokenAuth/Authenticate 401 (Unauthorized)
Here is the steps I have taken so far:
Changed launchSetting.json:
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:21021/",
"sslPort": 0
}
},
Added ExternalAuthenticationSource:
public class WindowsAuthSource : DefaultExternalAuthenticationSource<Tenant, User>, ITransientDependency
{
public override string Name
{
get { return "Windows Authentication"; }
}
public override Task<bool> TryAuthenticateAsync(string userNameOrEmailAddress, string plainPassword, Tenant tenant)
{
return Task.FromResult(true);
}
}
Added it to CoreModule:
Configuration.Modules.Zero().UserManagement.ExternalAuthenticationSources.Add<WindowsAuthSource>();
4.Adjust AuthConfigurer:
services.AddAuthentication(opt => {
opt.DefaultScheme = IISDefaults.AuthenticationScheme;
opt.DefaultAuthenticateScheme = IISDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = IISDefaults.AuthenticationScheme;
});
Adjust StartUp.cs:
services.Configure<IISOptions>(iis =>
{
iis.AuthenticationDisplayName = "WINDOWS";
iis.AutomaticAuthentication = true;
});
Changed Authenticate method in the TokenAuthController:
public async Task<AuthenticateResultModel> Authenticate([FromBody]
AuthenticateModel model)
{
//var username = WindowsIdentity.GetCurrent().Name.Split('\\').Last();
var username = HttpContext.User.Identity.Name;
model.UserNameOrEmailAddress = username;
var loginResult = await GetLoginResultAsync(
model.UserNameOrEmailAddress,
model.Password,
null
);
var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
return new AuthenticateResultModel
{
AccessToken = accessToken,
EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
UserId = loginResult.User.Id
};
}
Sending dummy username and password from login.service.ts:
authenticate(finallyCallback?: () => void): void {
finallyCallback = finallyCallback || (() => { });
//Dummy data
this.authenticateModel.userNameOrEmailAddress = "DummyUsername";
this.authenticateModel.password = "DummyPassword";
this._tokenAuthService
.authenticate(this.authenticateModel)
.finally(finallyCallback)
.subscribe((result: AuthenticateResultModel) => {
this.processAuthenticateResult(result);
});
}

How to create a CDN server in dotnet core using MongoDB GridFS and AngularJs

We want to create a decoupled file server named CDN in .net core using MongoDB GridFS and angular js.
From various sources I tried my best to solve the issue.
And finally we done this.
I used Visual Studio 2017 and .NETCore 1.1
To do so I follow the followings:
1. Write Code in MongoDB GridFS
create an interface like
public interface IGridFsRepository : IDisposable
{
Task<string> UploadAsync(IFormFile file);
Task<bool> AnyAsync(ObjectId id);
Task<bool> AnyAsync(string fileName);
Task DeleteAsync(string fileName);
Task DeleteAsync(ObjectId id);
Task<GridFSDownloadStream<ObjectId>> DownloadAsync(string fileName);
Task<GridFSDownloadStream<ObjectId>> DownloadAsync(ObjectId id);
object GetAllFilesByContentType(string contentType, int skip, int
take);
object GetAllFiles(int skip, int take);
}
then create MongoDbCdnContext:
public abstract class MongoDbCdnContext
{
public IGridFSBucket GridFsBucket {get;}
protected MongoDbCdnContext(string connectionStringName)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var connectionString =
config.GetConnectionString(connectionStringName);
var connection = new MongoUrl(connectionString);
var settings = MongoClientSettings.FromUrl(connection);
//#if DEBUG
// settings.ClusterConfigurator = builder =>
builder.Subscribe<CommandStartedEvent>(started =>
// {
// Debug.Write(started.Command);
// });
//#endif
var client = new MongoClient(settings);
var database = client.GetDatabase(connection.DatabaseName);
GridFsBucket = new GridFSBucket(database);
}
}
then implement it:
public class GridFsRepository : MongoDbCdnContext,
IGridFsRepository
{
public GridFsRepository() : base("MongoCdn")
{
}
public async Task<string> UploadAsync(IFormFile file)
{
var options = new GridFSUploadOptions
{
Metadata = new BsonDocument("contentType", file.ContentType)
};
using (var reader = new
StreamReader((Stream)file.OpenReadStream()))
{
var stream = reader.BaseStream;
var fileId = await
GridFsBucket.UploadFromStreamAsync(file.FileName, stream,
options);
return fileId.ToString();
}
}
public async Task<bool> AnyAsync(ObjectId id)
{
var filter = Builders<GridFSFileInfo>.Filter.Eq("_id", id);
return await GridFsBucket.Find(filter).AnyAsync();
}
public Task<bool> AnyAsync(string fileName)
{
var filter = Builders<GridFSFileInfo>.Filter.Where(x =>
x.Filename == fileName);
return GridFsBucket.Find(filter).AnyAsync();
}
public async Task DeleteAsync(string fileName)
{
var fileInfo = await GetFileInfoAsync(fileName);
if (fileInfo != null)
await DeleteAsync(fileInfo.Id);
}
public async Task DeleteAsync(ObjectId id)
{
await GridFsBucket.DeleteAsync(id);
}
private async Task<GridFSFileInfo> GetFileInfoAsync(string fileName)
{
var filter = Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename,
fileName);
var fileInfo = await
GridFsBucket.Find(filter).FirstOrDefaultAsync();
return fileInfo;
}
public async Task<GridFSDownloadStream<ObjectId>>
DownloadAsync(ObjectId id)
{
return await GridFsBucket.OpenDownloadStreamAsync(id);
}
public async Task<GridFSDownloadStream<ObjectId>>
DownloadAsync(string fileName)
{
return await
GridFsBucket.OpenDownloadStreamByNameAsync(fileName);
}
public IEnumerable<GridFSFileInfoDto> GetAllFilesByContentType(string
contentType, int skip, int take)
{
var filter = Builders<GridFSFileInfo>.Filter
.Eq(info => info.Metadata, new BsonDocument(new
BsonElement("contentType", contentType)));
var options = new GridFSFindOptions
{
Limit = take,
Skip = skip,
};
var stream = GridFsBucket.Find(filter, options)
.ToList()
.Select(s => new GridFSFileInfoDto
{
Id = s.Id,
Filename = s.Filename,
MetaData = s.Metadata,
Length = s.Length + "",
UploadDateTime = s.UploadDateTime,
})
.ToList();
return stream;
}
public IEnumerable<GridFSFileInfoDto> GetAllFiles(int skip, int take)
{
var options = new GridFSFindOptions
{
Limit = take,
Skip = skip,
};
var stream = GridFsBucket.Find(new
BsonDocumentFilterDefinition<GridFSFileInfo<ObjectId>>(new
BsonDocument()), options)
.ToList()
.Select(s => new GridFSFileInfoDto
{
Id = s.Id,
Filename = s.Filename,
MetaData = s.Metadata,
Length = s.Length + "",
UploadDateTime = s.UploadDateTime,
})
.ToList();
return stream;
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
then create a controller in .netcore web api
[EnableCors("AllowAll")]
[ValidateModel]
[Route("api/files")]
public class FileController : Controller
{
private readonly IGridFsRepository _gridFsManager;
public FileController(IGridFsRepository gridFsRepository)
{
_gridFsManager = gridFsRepository;
}
[AllowAnonymous]
[HttpGet("{fileName}",Name = "GetByFileName")]
public async Task<IActionResult> GetByFileName(string fileName)
{
return Ok(await _gridFsManager.DownloadAsync(fileName));
}
[AllowAnonymous]
[HttpGet("{id}",Name = "GetById")]
public async Task<IActionResult> GetByFileName(ObjectId id)
{
return Ok(await _gridFsManager.DownloadAsync(id));
}
[HttpPost]
public async Task<IActionResult> Upload([FromForm] IFormFile file)
{
if (file != null)
{
if (file.ContentType.Contains("image"))
return BadRequest("Sorry only image jpg/jpeg/png
accepted");
if (file.Length >= (300 * 1024))
return BadRequest($"Sorry {file.FileName} is exceeds
300kb");
await _gridFsManager.DeleteAsync(file.FileName);
await _gridFsManager.UploadAsync(file);
}
return NoContent();
}
[HttpDelete]
public async Task<IActionResult> Delete(string id)
{
await _gridFsManager.DeleteAsync(id);
return NoContent();
}
}
please do not forget to resolve dependency:
services.AddScoped<IGridFsRepository, GridFsRepository>();
to file from html:
<div class="btn">
<span>Logo</span>
<input type="file" data-ng-model="cp.data.file"
id="selectedFile" name="selectedFile">
</div>
lets go to UI layer:
first create an angular factory:
(function () {
"use strict";
angular.module("appCdn", [])
.factory('fileUploader', ["$http", function ($http) {
return {
upload: function (url, file, fileMaxSize, fileName, callback) {
if (this.isValidFileSize(fileMaxSize, file)) {
var fd = new FormData(); //Create FormData object
if (fileName)
fd.append("file", file, fileName);
else
fd.append("file", file);
$http.post(url, fd, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
}).success(function (data) {
callback();
}).error(function (data) {
Materialize.toast("Sorry! error in file upload", 4000, 'red');
});
} else {
Materialize.toast("Sorry! " + file.name + " exceeds 300kb", 4000, 'red');
}
},
isValidFileSize: function (maximumAllowedSize, file) {
if (file.size >= maximumAllowedSize) {
return false;
} else {
return true;
}
}
};
}
]);
})();
after that create an angular controller:
angular.module('ecom').controller('companyProfileCtrl',
['$http', 'config', "confirmation", "fileUploader",companyProfile]);
function companyProfile($http, config, confirmation, fileUploader) {
var vm = this; vm.getProfile = function () {
$http.get(config.apiUrl + "companyProfile")
.success(function (response) {
vm.data = response;
});
};
vm.save = function (profile) {
confirmation.confirm(function () {
$http.post(config.apiUrl + "companyProfile", profile)
.success(function (data) {
var fileName = "";
if (profile.id) {
fileName = profile.id;
} else {
fileName = data;
}
vm.upload(fileName,
function () {
Materialize.toast("succeeded", 4000, 'green');
window.location.href = window.history.back();
});
})
.error(function (data) {
Materialize.toast(data, 4000, 'red');
});
});
};
vm.upload = function (fileName, callback) {
var photo = document.getElementById("selectedFile");
var file = photo.files[0];
if (file) {fileUploader.upload(config.cdnUrl + "files",
//300kb
file, 1024 * 300, fileName, callback);
}
};
};
finally to show the image in html:
<div><img src="http://localhost:41792/api/files/{{cp.data.id}}"
class="img-responsive visible-md visible-lg img-margin-desktop"
width="350" height="167" />
</div>
finally we done this. this is all.
I always looking forward to receiving criticism.