IdentityResult to ActionResult - asp.net-core

Is there an easy way to convert an IdentityResult to an IActionResult taking into account errors?

IdentityResult is just a class which tells you if an ASP.NET (Core) Identity operation succeeded or not and in case of an error offers you error messages and is unrelated to MVC Action's results which implement IActionResult interface.
If you use WebApi/RestApi controllers , you'd translate it to something like
public IActionResult SomeActionName()
{
IdentityResult result = ...; // some identity operation
// all is okay, return http code 200
if(result.Success)
return Ok();
// error happened, return http code 400 + errors as json
return BadRequest(result.Errors);
}
Or if you are really lazy, write your own IActionResult:
public class IdentityActionResult : IActionResult
{
private readonly IdentityResult identityResult;
public IdentityActionResult(IdentityResult identityResult)
{
this.identityResult = identityResult;
}
public Task ExecuteResultAsync(ActionContext context)
{
IActionResult actionResult = null;
if(identityResult.Success)
{
actionResult = new OkResult();
}
else
{
actionResult = new BadRequestObjectResult(identityResult.Errors);
}
return actionResult.Execute(context);
}
}
Of course this can be further optimized so that you don't have to create two IActionResult objects per request, but that's an exercise left up to you ;)

You can write an extension method for IdentityResult which returns ObjectResult something like this:
public static class IdentityResultExtension
{
public static ObjectResult ToObjectResult(this IdentityResult result)
{
//
return new ObjectResult(result);
}
}
then use it in action:
public IActionResult YourAction()
{
IdentityResult result;
return result.ToObjectResult();
}

Related

Restore AJAX handling for ASP.NET Core to previous functionality

In previous MVC5 and below, you could make an ajax call that unwrapped the parameters properly:
JS:
$.post('/controller/endpoint',{intparam: 1, strparam: 'hello'})
CS:
public ActionResult endpoint(int intparam, string strparam){}
In the new aspnetcore, it has changed:
CS:
public CustomClassWrapper{
public int intparam {get;set;}
public string stringparam {get;set;}
}
public ActionResult endpoint([FromBody]CustomClassWrapper item){}
To sum it up, in the new framework, you need to write a wrapper class and can only pass one [FromBody] parameter to the method. Previously, the params would be unwrapped by variable name correctly.
So, i'm trying to re-implement this functionality in an aspnetcore middleware component. I'm having difficulty in how to accomplish calling the controller method properly with the parameters.
My current cut-down code:
public async Task Invoke(HttpContext context)
{
if (IsAjaxRequest(context.Request))
{
try
{
string bodyContent = new StreamReader(context.Request.Body).ReadToEnd();
var parameters = JsonConvert.DeserializeObject(bodyContent);
///What to do here?
}
catch (Exception ex)
{
throw new Exception("AJAX method not found ", ex);
}
}
else
{
await _next(context);
}
}
I'm really just not sure about what to do after deserializing the parameters. I have the URL for the endpoint and also the params correctly. Just need to know how to call the method and return the result as JSON. Should i be using Reflection to get the controller method? Or is there a better way using MVC?
Try implement custom IModelBinder.
public class BodyFieldModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
bindingContext.HttpContext.Request.EnableRewind(); // required to read request body multiple times
var inputStream = bindingContext.HttpContext.Request.Body;
if (inputStream.Position != 0L)
inputStream.Position = 0;
var bodyValue = new StreamReader(inputStream, Encoding.UTF8).ReadToEnd();
var jsonObject = (JObject)JsonConvert.DeserializeObject<object>(bodyValue);
if (jsonObject.TryGetValue(bindingContext.FieldName, out var jToken))
{
var jsonSerializer = JsonSerializer.Create();
var result = jToken.ToObject(bindingContext.ModelType, jsonSerializer);
bindingContext.Result = ModelBindingResult.Success(result);
return Task.CompletedTask;
}
bindingContext.Result = ModelBindingResult.Failed();
return Task.CompletedTask;
}
}
Be careful, the code above lacks error handling and etc.
And use it like this:
[HttpPost]
public IActionResult Endpoint([ModelBinder(typeof(BodyFieldModelBinder))] int intparam)
Also you could implement custom attribute to reduce complexity of declaration:
public class BodyFieldAttribute : ModelBinderAttribute
{
public BodyFieldAttribute()
: base(typeof(BodyFieldModelBinder))
{
}
}
it's very simple thing i don't know why it not working at your end
JS
$.post('actionMethodURl', { FirstName: '1', LastName: 'hello' }).done(Successfunction);
CS
[HttpPost]
public ActionResult endpoint(string FirstName,string LastName)
{
object Message = string.Empty;
if (ModelState.IsValid)
{
Message = "Pass";
}
else
{
Message = ModelState.Errors();
}
return Json(Message);
}

ASP .NET Entity Framework Core Cannot access a disposed object

Wanting to get into .NET Core, I created a WEB API that takes a file upload and then saves the transactions in the file into a DB table. I'm using .NET Core 2 with Entity Framework Core. I created my context using the example from here.
My problem is that I get the error "System.ObjectDisposedException Cannot access a disposed object" when it tries to save to the context object in my repository. It's a simple stack, so I'm hoping someone can help me out.
My container setup looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<SyncFinContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<ITransactionProcessor, TransactionProcessor>();
services.AddScoped<ITransactionRepository, TransactionRepository>();
}
My DBInitializer which I also got from the link above:
public static class DbInitializer
{
public static async Task Initialize(SyncFinContext context)
{
await context.Database.EnsureCreatedAsync();
// Look for any students.
if (context.Transactions.Any())
{
return; // DB has been seeded
}
var ts = new Transaction[]
{
// insert test data here
};
await context.SaveChangesAsync();
}
}
My DB Context:
public class SyncFinContext : DbContext
{
public SyncFinContext(DbContextOptions<SyncFinContext> options) : base(options)
{
}
public DbSet<Transaction> Transactions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Transaction>().ToTable("Transactions");
}
}
My Controller looks like this:
[Produces("application/json")]
public class TransactionController : Controller
{
ITransactionRepository _transactionRepository { get; set; }
ITransactionProcessor _transactionProcessor { get; set; }
public TransactionController(ITransactionRepository m, ITransactionProcessor p) : base()
{
_transactionRepository = m;
_transactionProcessor = p;
}
// POST: transaction/import
[HttpPost]
public async void Import(List<IFormFile> files)
{
if (files == null || files.Count == 0)
{
throw new FileNotFoundException("No file was received.");
}
// copy file to temp location so that it can be processed
var filepath = Path.GetTempFileName();
using (var stream = new FileStream(filepath, FileMode.Create))
{
await files[0].CopyToAsync(stream);
}
ImportTransactionRequest input = new ImportTransactionRequest
{
FilePath = filepath
};
var transactions = await _transactionProcessor.ReadDocument(filepath);
await _transactionRepository.AddBulk(transactions);
}
}
And my repository looks like this:
public class TransactionRepository : ITransactionRepository
{
// TODO: move the context
private SyncFinContext _context;
public TransactionRepository(SyncFinContext context)
{
_context = context;
}
public async Task AddBulk(List<Transaction> transactions)
{
foreach(var t in transactions)
{
await _context.Transactions.AddAsync(t);
}
_context.SaveChanges();
}
}
For full transparency, the transaction Processor just gets a list of rows from a csv:
public async Task<List<Transaction>> ReadDocument(string filepath)
{
try
{
var ret = new List<Transaction>();
var lines = await File.ReadAllLinesAsync(filepath);
foreach (var line in lines)
{
var parts = line.Split(',');
var tx = new Transaction
{
PostedDate = DateTime.Parse(parts[0]),
TransactionDate = DateTime.Parse(parts[1]),
Description = parts[2],
Deposit = ParseDecimal(parts[3]),
Withdrawal = ParseDecimal(parts[4]),
Balance = ParseDecimal(parts[5])
};
ret.Add(tx);
}
return ret;
}
catch(Exception e)
{
throw;
}
}
I've read where the whole stack must be async in order for the db context instance to be available, and, unless I'm doing it wrong, I seem to be doing that, as you can see above.
My expectations are that AddDbContext() will indeed properly scope the context to be available throughout the stack unless I explicitly dispose of it. I have not found anything to make me think otherwise.
I've tried hard-coding data in my DB Initializer also, as I read that may be a factor, but that does not solve the problem. Not sure what else to try. If someone can give me some ideas I would appreciate it.
The Import() action method needs to have a return type of Task. MVC will await execute an action method with a return type of Task.
Also, probably best to get in the habit of returning an IActionResult on your action methods. The task based equivalent is Task<IActionResult>. This makes your controllers easier to test.
Since the AddBulk(List<Transaction> transactions) method is public async Task, the DbContext will be disposed if any part returns void (not awaited) at any point.
Try changing _context.SaveChanges();
To await _context.SaveChangesAsync();
This would ensure a Task is being returned and not void.
https://stackoverflow.com/a/46308661/3062956
https://learn.microsoft.com/en-us/ef/core/saving/async

.Net Core 2.0 Web API controller not working and getting 404

I have something very very strange. I have 2 controllers. UploadController and AccountController. Theye were both working, and now when I try the AccountController it give error 404. ik don't get it.
This is how my AccountController looks like:
namespace CoreAngular.Controllers
{
//[Authorize]
[Produces("application/json")]
[Route("api/account")]
public class AccountController : Controller
{
private IRepository repository;
public AccountController(IDatabaseClient<DocumentClient> client)
: this ( new UserRepository(client))
{
}
public AccountController(IRepository repository)
{
this.repository = repository;
}
[HttpGet]
public async Task<ActionResult> Get(string id)
{
var start = DateTime.Now.TimeOfDay;
if (string.IsNullOrEmpty(id))
{
return BadRequest();
}
var user = await repository.GetAsync(id);
if (user == null)
{
return NotFound();
}
var userDTO = new UserGetDTO()
{
image = Convert.ToBase64String(user.image.image),
id = user.id,
time = DateTime.Now.Subtract(start).Millisecond
};
return Ok(userDTO);
}......
Do I miss something here? I know I comentet out the [Authorize], but i just wanted to try to connect.
You should specify route template in HttpGet attribute:
[HttpGet("{id}")]
public async Task<ActionResult> Get(string id)
{
// ...
}

WebApi for Email delivery

I created a Web API controller inside my ASP.NET MVC 4 Website to specifically send emails so that I can consume it for this purpose and eventually provide it to other consumers (Mobile, etc).
Everything is working fine but I want to use it asynchronously, altough it's not working that way, my website blocks until the work is finished.
I have a regular Controller where I call a helper class that calls the API.
Controller Code :
[HttpPost]
public async Task<ActionResult> ContactUs(ContactUsModel model)
{
ExternalApiCalls extApi = new ExternalApiCalls();
await extApi.PostContactUs(model).ConfigureAwait(false);
return RedirectToAction("ContactUsSuccess", "Account");
}
Helper Class Code :
public class ExternalApiCalls
{
public HttpClient client { get; set; }
public ExternalApiCalls()
{
client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:10239/");
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task PostContactUs(ContactUsModel model)
{
try
{
var response = await client.PostAsJsonAsync("api/WAPIMail", model).ConfigureAwait(false);
}
catch (HttpRequestException ex)
{
}
catch (System.FormatException)
{
}
finally
{
}
}
}
Web API Controller :
public class WAPIMailController : ApiController
{
public void PostContactUs(ContactUsModel model)
{
// Send Email Here
}
}
Thanks a lot for your help
I finally managed how to do it..
For brevity's sake, I am showing oversimplified code, see below:
Controller :
[HttpPost]
public ActionResult ContactUs(ContactUsModel model)
{
new ExternalApiCalls().MailContactUs(model);
return RedirectToAction("ContactUsSuccess", "Account");
}
Helper Class :
public void MailContactUs(ContactUsModel model)
{
client.PostAsJsonAsync("api/WAPIMail/MailContactUs", model).ConfigureAwait(false);
}
Web API Controller :
[HttpPost]
public void MailContactUs(ContactUsModel model)
{
//Email Logic Here
}

Returning 400 rather than 404 from ASP.NET MVC Web API

I have created the hello world of ASP.NET MVC web API projects using VS2012:
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
Issuing a get request to that controller returns some XML of the data with status 200. All good so far.
When I remove that method, like this:
public class ValuesController : ApiController
{
// GET api/values
//public IEnumerable<string> Get()
//{
// return new string[] { "value1", "value2" };
//}
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
I then get a 404 not found. What I want is a 400 bad request, because an ID must be provided. How can I achieve this?
You don't need to keep the Get() method just to throw an error.
Change the signature of your Get by ID method to:
public string Get(int? id = null)
{
if (id == null) throw new HttpResponseException(HttpStatusCode.BadRequest);
return "value";
}
One way is to include the method, but throw an exception instead:
public class ValuesController : ApiController
{
// GET api/values
public void Get()
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
You can also use Action Return types if you don't want to import extra packages for the exceptions.
Instead of throwing an exception you do return a new BadRequestResult instance. Mind the return Action type as well that must be declared.
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("/api/yourtype")]
public class ValuesController : ControllerBase
{
public ActionResult Get()
{
// 400
return new BadRequestResult();
}
public ActionResult<YourType> Get(int id)
{
// 404
return NotFound();
// 200
return Ok(object);
}
}