discord.net how to get user input data on slash command option? - discord.net

var guildCommand = new SlashCommandBuilder();
guildCommand.WithName("question")
.AddOption("answer", ApplicationCommandOptionType.String, "your answer", isRequired: true);
I created a slash command with option to get user input data like this.
public async Task EchoUserAnswer(SocketSlashCommand command)
{
await command.RespondAsync("Your answer is {UserAnswer}");
}
And I added this method on SlashCommandExecuted event.
I want to know how to get user answer.

You can use following code:
public async Task EchoUserAnswer(SocketSlashCommand command)
{
var userInput = command.Data.Options.First().Value.ToString();
await command.RespondAsync($"Your answer is {userInput}");
}

Related

Since 'func' is an async method that returns 'Task',a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'?

Hello My Program got this error and i dont know why?
Since 'InsertToHomeSecondPagesTableJob.Execute(IJobExecutionContext)' is an async method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task'?
this is my code
public async Task Execute(IJobExecutionContext context)
{
var PagesScoreNewsHomePageTable = new PagesScoreNewsHomePageTable()
{
PagesID = 1,
UserID = 22,
Author = "jack"
};
_db.AddAsync(PagesScoreNewsHomePageTable);
_db.SaveChangesAsync();
return Task.CompletedTask;
}
how can i solve this error?
The simple and recommended way to solve this problem is using await keywords.
The async and await keywords in C# are the heart of async programming.
By using those two keywords, you can use resources in .NET Framework,
.NET Core, or the Windows Runtime to create an asynchronous method
almost as easily as you create a synchronous method.
You just need to change your code like:
public async Task Execute(IJobExecutionContext context)
{
var PagesScoreNewsHomePageTable = new PagesScoreNewsHomePageTable()
{
PagesID = 1,
UserID = 22,
Author = "jack"
};
await _db.AddAsync(PagesScoreNewsHomePageTable);
await _db.SaveChangesAsync();
}
More details about async you can refer to this Docs.

GetAuthorizationContextAsync(returnUrl) returns null but I have no idea what I'm missing

I am trying to implement an Identity Server Solution that redirects the user to different Login Views depending on the client application they come from.
To do this, in the AccountController.cs file I have the following method:
private async Task<LoginViewModel> BuildLoginViewModelAsync(string returnUrl)
{
var isValid = this.interaction.IsValidReturnUrl(returnUrl);
var context = await this.interaction.GetAuthorizationContextAsync(returnUrl);
var schemes = await this.schemeProvider.GetAllSchemesAsync();
return new LoginViewModel
{
AllowRememberLogin = AccountOptions.AllowRememberLogin,
ReturnUrl = returnUrl,
Username = context?.LoginHint
};
}
I have set up a Configuration & Operational DbContexts as per this tutorial from the IdentityServer4 documentation.
Additionally, I have seeded the database with some rows in the Clients & ClientRedirectUris tables.
Presumably, that should be all I need to access the AuthorizationContext from the IIdentityServerInteractionService API, but the method above always returns null, and the isValid variable is always false too.
I have made sure that the returnUrl I am passing in is exactly the same as the redirectUri stored in my database (I am using localhost and running all this locally, if that matters)
Can someone please help? I have no idea what I'm doing wrong...
You have to do like this:
Html.BeginForm("Login", "Account", new {ReturnUrl = Request.QueryString["ReturnUrl"] })
[HttpPost]
public async Task<IActionResult> Login(LoginInputModel model, string ReturnUrl) {
...
}

Using Attribute and ActionFilters for logging request and response of controller and actions

I am trying to find an elegant way of logging every request and response in my Web API using Filters in Asp.net Core 3.1 rather than have them in each action and each controller.
Haven't found a nice solution that seems performable well to deploy in production.
I've been trying to do something like this (below) but no much success.
Any other suggestion would be appreciated.
public class LogFilter : IAsyncActionFilter
{
private readonly ILogger _logger;
public LogFilter(ILogger logger)
{
_logger = logger;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var requestBodyData = context.ActionArguments["request"];
var responseBodyData = "";//how to get the response result
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Request Body: {requestBodyData}");
await next();
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Response Body: {responseBodyData}");
}
}
I think logging the response should be done in debugging mode only and really can be done at your service API (by using DI interception). That way you don't need to use IActionFilter which actually can provide you only a wrapper IActionResult which wraps the raw value from the action method (which is usually the result returned from your service API). Note that at the phase of action execution (starting & ending can be intercepted by using IActionFilter or IAsyncActionFilter), the HttpContext.Response may have not been fully written (because there are next phases that may write more data to it). So you cannot read the full response there. But here I suppose you mean reading the action result (later I'll show you how to read the actual full response body in a correct phase). When it comes to IActionResult, you have various kinds of IActionResult including custom ones. So it's hard to have a general solution to read the raw wrapped data (which may not even be exposed in some custom implementations). That means you need to target some specific well-known action results to handle it correctly. Here I introduce code to read JsonResult as an example:
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var requestBodyData = context.ActionArguments["request"];
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Request Body: {requestBodyData}");
var actionExecutedContext = await next();
var responseBodyData = "not supported result";
//sample for JsonResult
if(actionExecutedContext.Result is JsonResult jsonResult){
responseBodyData = JsonSerializer.Serialize(jsonResult.Value);
}
//check for other kinds of IActionResult if any ...
//...
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Response Body: {responseBodyData}");
}
IActionResult has a method called ExecuteResultAsync which can trigger the next processing phase (result execution). That's when the action result is fully written to the HttpContext.Response. So you can try creating a dummy pipeline (starting with a dummy ActionContext) on which to execute the action result and get the final data written to the response body. However that's what I can imagine in theory. It would be very complicated to go that way. Instead you can just use a custom IResultFilter or IAsyncResultFilter to try getting the response body there. Now there is one issue, the default HttpContext.Response.Body is an HttpResponseStream which does not support reading & seeking at all (CanRead & CanSeek are false), we can only write to that kind of stream. So there is a hacky way to help us mock in a readable stream (such as MemoryStream) before running the code that executes the result. After that we swap out the readable stream and swap back the original HttpResponseStream in after copying data from the readable stream to that stream. Here is an extension method to help achieve that:
public static class ResponseBodyCloningHttpContextExtensions
{
public static async Task<Stream> CloneBodyAsync(this HttpContext context, Func<Task> writeBody)
{
var readableStream = new MemoryStream();
var originalBody = context.Response.Body;
context.Response.Body = readableStream;
try
{
await writeBody();
readableStream.Position = 0;
await readableStream.CopyToAsync(originalBody);
readableStream.Position = 0;
}
finally
{
context.Response.Body = originalBody;
}
return readableStream;
}
}
Now we can use that extension method in an IAsyncResultFilter like this:
//this logs the result only, to write the log entry for starting/beginning the action
//you can rely on the IAsyncActionFilter as how you use it.
public class LoggingAsyncResultFilterAttribute : Attribute, IAsyncResultFilter
{
//missing code to inject _logger here ...
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var readableStream = await context.HttpContext.CloneBodyAsync(() => next());
//suppose the response body contains text-based content
using (var sr = new StreamReader(readableStream))
{
var responseText = await sr.ReadToEndAsync();
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Response Body: {responseText}");
}
}
}
You can also use an IAsyncResourceFilter instead, which can capture result written by IExceptionFilter. Or maybe the best, use an IAsyncAlwaysRunResultFilter which can capture the result in all cases.
I assume that you know how to register IAsyncActionFilter so you should know how to register IAsyncResultFilter as well as other kinds of filter. It's just the same.
starting with dotnet 6 asp has HTTP logging built in. Microsoft has taken into account redacting information and other important concepts that need to be considered when logging requests.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
/* enabled HttpLogging with this line */
app.UseHttpLogging();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.MapGet("/", () => "Hello World!");
app.Run();
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-logging/?view=aspnetcore-6.0#enabling-http-logging

Asp.net core identity change username/email

The default identity change username/email with confirmation logic doesn't make sense.
Set app with require email confirmation.
Set require confirmed email to sign in.
User then changes email, enters email incorrectly, logs out.
Now the user is locked out. Email has changed but requires
confirmation to sign in and no email confirmation link because
address entered incorrectly.
Have I setup my application wrong or did Microsoft not design Identity very well?
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
//...
var email = await _userManager.GetEmailAsync(user);
if (Input.Email != email)
{
var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email);
if (!setEmailResult.Succeeded)
{
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'.");
}
StatusMessage = "<strong>Verify your new email</strong><br/><br/>" +
"We sent an email to " + Input.Email +
" to verify your address. Please click the link in that email to continue.";
}
//...
await _signInManager.RefreshSignInAsync(user);
return RedirectToPage();
}
Your issue is using SetEmailAsync for this purpose. That method is intended to set an email for a user when none exists currently. In such a case, setting confirmed to false makes sense and wouldn't cause any problems.
There's another method, ChangeEmailAsync, which is what you should be using. This method requires a token, which would be obtained from the email confirmation flow. In other words, the steps you should are:
User submits form with new email to change to
You send a confirmation email to the user. The email address the user is changing to will need to be persisted either in the confirmation link or in a separate place in your database. In other words, the user's actual email in their user record has not changed.
User clicks confirmation link in email. You get the new email address they want to change to either from the link or wherever you persisted it previously
You call ChangeEmailAsync with this email and the token from from the confirmation link.
User's email is now changed and confirmed.
EDIT
FWIW, yes, this appears to be an issue with the default template. Not sure why they did it this way, since yes, it very much breaks things, and like I said in my answer, ChangeEmailAsync exists for this very purpose. Just follow the steps I outlined above and change the logic here for what happens when the user submits a new email address via the Manage page.
EDIT #2
I've filed an issue on Github for this. I can't devote any more time to it at the moment, but I'll try to submit a pull request for a fix if I have time and no one else beats me to it. The fix is relatively straight-forward.
EDIT #3
I was able to get a basic email change flow working in a fork. However, the team has already assigned out the issue and seem to be including it as part of a larger overhaul of the Identity UI. I likely won't devote any more time to this now, but encourage you to follow the issue for updates from the team. If you do happen to borrow from my code to implement a fix now, be advised that I was attempting to create a solution with a minimal amount of entropy to other code. In a real production app, you should persist the new email somewhere like in the database instead of passing it around in the URL, for example.
As already identified, the template definitely provides the wrong behaviour. You can see the source for the template in the https://github.com/aspnet/Scaffolding repo here.
I suggest raising an issue on the GitHub project so this is changed. When the templates are updated, they'll no doubt have to account for both the case when confirmation is enabled and when it's not. In your case, you can reuse the logic that already exists in OnPostSendVerificationEmailAsync() relatively easily.
A more general implementation would look something like this:
public partial class IndexModel : PageModel
{
// inject as IOptions<IdentityOptions> into constructor
private readonly IdentityOptions _options;
// Extracted from OnPostSendVerificationEmailAsync()
private async Task SendConfirmationEmail(IdentityUser user, string email)
{
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = userId, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(
email,
"Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
}
public async Task<IActionResult> OnPostAsync()
{
//... Existing code
var email = await _userManager.GetEmailAsync(user);
var confirmationEmailSent = false;
if (Input.Email != email)
{
if(_options.SignIn.RequireConfirmedEmail)
{
// new implementation
await SendConfirmationEmail(user, Input.Email);
confirmationEmailSent = true;
}
else
{
// current implementation
var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email);
if (!setEmailResult.Succeeded)
{
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'.");
}
}
var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email);
if (!setEmailResult.Succeeded)
{
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'.");
}
}
// existing update phone number code;
await _signInManager.RefreshSignInAsync(user);
StatusMessage = confirmationEmailSent
? "Verification email sent. Please check your email."
: "Your profile has been updated";
return RedirectToPage();
}
public async Task<IActionResult> OnPostSendVerificationEmailAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var email = await _userManager.GetEmailAsync(user);
await SendConfirmationEmail(user, email);
StatusMessage = "Verification email sent. Please check your email.";
return RedirectToPage();
}
}

retrieve int value from async task

I have an async task in a separate project that I need to call from another project but Im not sure how to accomplish that.
The async method is
public async Task<int> InsertNewPasswordResetRequest(UserPasswordReset upr)
{
string commandText = "Insert Into passwordresetrequests (reset_username, ResetToken, ResetRequestTime, ResetRequestTimeout) values(#username,#rt,#rrt,#rrtout);";
var parameters = new Dictionary<string, object>() {
{ "#username", upr.ResetUsername },
{ "#rt", upr.ResetToken },
{ "#rrt", upr.ResetRequestTime },
{ "#rrtout", upr.ResetRequestTimeout }
};
return await Task.FromResult( _database.Execute(commandText, parameters, false));
}
I am trying to call it from another project like so
Dim success As Integer = prr.InsertNewPasswordResetRequest(upr).FromResult()
I know its returning a task but how do I extract the int value from it?
You can await of your task if you want to run it async
Dim success As Integer = Await prr.InsertNewPasswordResetRequest(upr)
or run task synchronously as
Dim success As Integer = prr.InsertNewPasswordResetRequest(upr).Result
The recommended way is to await the results of the InsertNewPasswordResetRequest method. You can also call the Result property on the returned Task<int> but that could lead to your code blocking if you're executing within a synchronization context.
int result = await InsertNewPasswordResetRequest(...);
An additional note: There is no need to await Task.FromResult in that method. Simply remove the async modifier from the method signature and the await keyword and return Task.FromResult directly.
An additional note: If possible, consider making InsertNewPasswordResetRequest non-async completely since it doesn't execute any asynchronous code.
Edit for VB.NET calling code:
Dim result As Integer = Await InsertNewPasswordResetRequest(...)