I am trying to authorize the swagger api with the okta configuration using ASP.NET core 2.2.
Followed the instruction from this link
https://app.swaggerhub.com/help/enterprise/user-management/sso/okta
But quite not sure how can I do it.
Okta link
https://developer.okta.com/quickstart/?_ga=2.180885607.1554519477.1569975022-1481902663.1569975022#/angular/dotnet/aspnetcore
here is my Asp.net code
ConfigureSwagger(services);
protected virtual void ConfigureSwagger(IServiceCollection services)
{
// to view online help, goto ~/swagger/
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
services.AddSwaggerGen(options =>
{
// add a custom operation filter which sets default values
options.OperationFilter<SwaggerDefaultValues>();
});
services.ConfigureSwaggerGen(options => { });
}
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
readonly IApiVersionDescriptionProvider provider;
/// <summary>
/// Initializes a new instance of the <see cref="ConfigureSwaggerOptions"/> class.
/// </summary>
/// <param name="provider">The <see cref="IApiVersionDescriptionProvider">provider</see> used to generate Swagger documents.</param>
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider;
/// <inheritdoc />
public void Configure(SwaggerGenOptions options)
{
// add a swagger document for each discovered API version
// note: you might choose to skip or document deprecated API versions differently
foreach (var description in provider.ApiVersionDescriptions)
{
options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
}
options.OrderActionsBy(apiDesc => apiDesc.RelativePath);
options.IncludeXmlComments(Path.ChangeExtension(typeof(Startup).GetTypeInfo().Assembly.Location, "xml"));
options.DescribeAllEnumsAsStrings();
options.DescribeStringEnumsInCamelCase();
//options.AddSecurityDefinition("oauth2",
// new OAuth2Scheme
// {
// Type = "oauth2",
// Flow = "implicit",
// AuthorizationUrl = new Uri("/connect/authorize", UriKind.Relative).ToString(),
// Scopes = new Dictionary<string, string>
// {
// {"api1", "DEMO API"}
// }
// });
//options.AddSecurityRequirement(new[] { "oauth2", "api1" });
options.AddSecurityDefinition("oauth2",
new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
Implicit = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri("/connect/authorize", UriKind.Relative),
Scopes = new Dictionary<string, string>
{
{Program.ResourceIdentifier, Program.ApplicationName}
}
}
}
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "oauth2"
}
},
new[] {"oauth2", Program.ResourceIdentifier }
}
});
options.EnableAnnotations();
//options.DocInclusionPredicate((docName, apiDesc) =>
//{
// if (!apiDesc.TryGetMethodInfo(out MethodInfo methodInfo)) return false;
// var versions = methodInfo.DeclaringType
// .GetCustomAttributes(true)
// .OfType<ApiVersionAttribute>()
// .SelectMany(attr => attr.Versions);
// return versions.Any(v => $"v{v.ToString()}" == docName);
//});
}
static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
{
var info = new OpenApiInfo
{
Title = Program.ApplicationName,
Version = $"v{description.ApiVersion}",
Description = "A sample application with Swagger, Swashbuckle, and API versioning."
};
if (description.IsDeprecated)
{
info.Description += " This API version has been deprecated.";
}
return info;
}
}
public static void UseSwaggerMiddleware(this IApplicationBuilder app, IApiVersionDescriptionProvider provider)
{
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
// build a swagger endpoint for each discovered API version
foreach (var description in provider.ApiVersionDescriptions)
{
c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
}
//OAuth2
c.OAuthClientId("{clientId}");
//c.OAuth2RedirectUrl("");
//c.OAuthUseBasicAuthenticationWithAccessCodeGrant();
c.OAuthClientSecret("{ClientSecret}");
c.OAuthAppName("{AppName}");
c.OAuthScopeSeparator("openid profile email");
c.OAuthAdditionalQueryStringParams(new Dictionary<string, string>
{
{ "response_type","token"}
});
});
}
Errors
Hide
Auth error
{"state":"VGh1IE9jdCAwMyAyMDE5IDE1OjI4OjEyIEdNVCsxMDAwIChBVVMgRWFzdGVybiBTdGFuZGFyZCBUaW1lKQ==","error":"unsupported_response_type","error_description":"The+response+type+is+not+supported+by+the+authorization+server.+Configured+response+types:+[id_token,+code]."}
How do I configure with swagger authorization client with JWT token.
Finally found the solution
Need to do this configuration on asp.net core
public static void UseSwaggerMiddleware(this IApplicationBuilder app, IApiVersionDescriptionProvider provider, IConfiguration Configuration)
{
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
// build a swagger endpoint for each discovered API version
foreach (var description in provider.ApiVersionDescriptions)
{
c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
}
//c.SwaggerEndpoint("/swagger/v2/swagger.json", "DEMO Api v2");
//c.SwaggerEndpoint("/swagger/v1/swagger.json", "DEMO Api v1");
//OAuth2
var OktaConfig = new OktaConfig();
Configuration.GetSection("OktaConfig").Bind(OktaConfig);
c.OAuthClientId(OktaConfig.ClientId);
c.OAuth2RedirectUrl($"{OktaConfig.RedirectUrl}/swagger/oauth2-redirect.html");
c.OAuthUseBasicAuthenticationWithAccessCodeGrant();
c.OAuthClientSecret(OktaConfig.ClientSecret);
c.OAuthAppName(OktaConfig.ClientName);
c.OAuthScopeSeparator($"openid profile email {Program.ResourceIdentifier}");
c.OAuthAdditionalQueryStringParams(new Dictionary<string, string>
{
{ "response_type","token"},
{ "nonce", "nonce" }
});
//c.ConfigObject.DeepLinking = true;
});
}
And need to add policies and rule
Related
I've got an ASP.NET Core application.
The configuration regarding Openiddict is as follows:
builder.Services.AddOpenIddict()
// Register the OpenIddict core components.
.AddCore(options =>
{
options.UseEntityFrameworkCore().UseDbContext<IdentityDataContext>();
options.Services.TryAddTransient<OpenIddictQuartzJob>();
// Note: TryAddEnumerable() is used here to ensure the initializer is registered only once.
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<QuartzOptions>, OpenIddictQuartzConfiguration>());
})
// Register the OpenIddict server components.
.AddServer(options =>
options.SetAuthorizationEndpointUris("/connect/authorize")
.SetLogoutEndpointUris("/connect/logout")
.SetTokenEndpointUris("/connect/token")
.SetUserinfoEndpointUris("/connect/userinfo")
// Mark the "email", "profile" and "roles" scopes as supported scopes.
.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles)
// Note: the sample uses the code and refresh token flows but you can enable
// the other flows if you need to support implicit, password or client credentials.
.AllowAuthorizationCodeFlow()
.AllowRefreshTokenFlow()
// Register the signing and encryption credentials.
.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate()
// Register the ASP.NET Core host and configure the ASP.NET Core-specific options.
.UseAspNetCore()
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableUserinfoEndpointPassthrough()
.EnableTokenEndpointPassthrough()
.EnableStatusCodePagesIntegration())
// Register the OpenIddict validation components.
.AddValidation(options =>
{
// Import the configuration from the local OpenIddict server instance.
options.UseLocalServer();
// Register the ASP.NET Core host.
options.UseAspNetCore();
});
builder.Services.ConfigureApplicationCookie(options => options.LoginPath = "/account/auth");
In tests I use a server factory:
public class InMemoryWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup>
where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder) =>
builder.ConfigureServices(services =>
{
var descriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<IdentityDataContext>))!;
services.Remove(descriptor);
services.AddDbContext<IdentityDataContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
// without this I get a NPE
options.UseOpenIddict();
});
var sp = services.BuildServiceProvider();
using var scope = sp.CreateScope();
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<IdentityDataContext>();
db.Database.EnsureCreated();
});
protected override void ConfigureClient(HttpClient client)
{
base.ConfigureClient(client);
// without this I get Bad request due to Opeiddict filters
client.DefaultRequestHeaders.Host = client.BaseAddress!.Host;
}
}
The test looks like this (taken from here):
[Fact]
public async Task AuthorizedRequestReturnsValue()
{
var client = _factory.WithWebHostBuilder(builder => builder
.ConfigureTestServices(services => services.AddAuthentication("TestScheme")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("TestScheme", _ => { })))
.CreateClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("TestScheme");
var response = await client.GetAsync(new Uri("https://localhost/connect/userinfo"));
// I get Unauthorized here instead
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
The /connect/userinfo is as follows:
[HttpGet("~/connect/userinfo")]
[HttpPost("~/connect/userinfo")]
[IgnoreAntiforgeryToken]
[Produces("application/json")]
public async Task<IActionResult> Userinfo()
{
var user = await _userManager.FindByIdAsync(User.GetClaim(Claims.Subject)!);
if (user is null)
{
return Challenge(
properties: new AuthenticationProperties(new Dictionary<string, string?>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidToken,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The specified access token is bound to an account that no longer exists.",
}),
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
var claims = new Dictionary<string, object>(StringComparer.Ordinal)
{
// Note: the "sub" claim is a mandatory claim and must be included in the JSON response.
[Claims.Subject] = await _userManager.GetUserIdAsync(user),
};
claims[Claims.Email] = (await _userManager.GetEmailAsync(user))!;
claims[Claims.EmailVerified] = await _userManager.IsEmailConfirmedAsync(user);
claims[Claims.Name] = (await _userManager.GetUserNameAsync(user))!;
claims[Claims.PhoneNumber] = (await _userManager.GetPhoneNumberAsync(user))!;
claims[Claims.PhoneNumberVerified] = await _userManager.IsPhoneNumberConfirmedAsync(user);
claims[Claims.Role] = await _userManager.GetRolesAsync(user);
// Note: the complete list of standard claims supported by the OpenID Connect specification
// can be found here: http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
return Ok(claims);
}
As far as I understand by default the TestScheme should be used for authentication. But it is not and OpenidDict takes precedence.
Is there a way to make authenticated/authorized requests in integration tests?
P.S. The test was working OK until I added OpenIddict. Before that I used Asp Identity directly for authentication
I make log with CorrelationId in Serilog for email service sender and tracking pixel in asp.net core 6 web api , each message log with GUID correlationId and when user see the message we can also log the time he opens the email message but problem is I want to make same correlationId log for both email message and the time user see the message but each action in this app make their own correlationID.
I think the problem is in middleware correlation and program.cs. Does anyone know how I could do that? Or is it even possible?
Thanks in advance.
The class for send email and send pixel tracking
using MailKit.Net.Smtp;
using MailKit.Security;
using Microsoft.Extensions.Options;
using MimeKit;
using MimeKit.Text;
using DAL.Model;
using ImageTracker.Service;
namespace SimpleEmailApp.Services
{
public class MailService : IMailService
{
private readonly AppSetting _mailSetting;
private readonly ILogger<MailService> _logger;
private readonly IWhichMessageIsRead _whichMessageIsRead;
// IWhichMessageIsRead whichMessageIsRead
public MailService(IOptions<AppSetting> mailSetting, ILogger<MailService> logger,
IWhichMessageIsRead whichMessageIsRead)
{
_mailSetting = mailSetting.Value;
_logger = logger;
_whichMessageIsRead = whichMessageIsRead;
}
public async Task SendEmailAsync(EmailMessage message)
{
var email = new MimeMessage();
try
{
email.Sender = MailboxAddress.Parse(_mailSetting.Mail);
email.To.Add(MailboxAddress.Parse(messsage.Reciver));
email.Subject = messsage.Subject;
// a unique-id to attach with the mail
// var messageId = Guid.NewGuid();
// bool IsRead = false;
// the server that receives the request from the image and process it
// var deliveryProcessor = "https://localhost:7156/api/Track";
// constructing the image tag with "src" pointing to the external resource
// passing the unique-id in the query-string.
var imgTag = string.Format(#"<img src=""https://localhost:7156/api/Mail"" alt ="""" width = ""1"" height = ""1""
style=""width: 0px; height: 0px; border:0px;""/>");
var builder = new BodyBuilder();
builder.HtmlBody = imgTag;
email.Body = new TextPart(TextFormat.Html)
{
Text = message.Body + " " + imgTag
};
var lg = _logger.LogInformation("");
_logger.LogInformation("Email {#mailRequest} creates for {Sender} at {now}.", message, _mailSetting.Mail, DateTime.Now);
_whichMessageIsRead. GetMessage( ,messsage);
}
catch (Exception)
{
_logger.LogError("Email is not sent.");
throw;
}
using var smtp = new SmtpClient();
try
{
smtp.Connect(_mailSetting.Host, _mailSetting.Port, SecureSocketOptions.StartTls);
smtp.Authenticate(_mailSetting.Mail, _mailSetting.Password);
await smtp.SendAsync(email);
smtp.Disconnect(true);
}
catch (Exception)
{
_logger.LogError(" SMTP Server Configuration Error occurred ");
throw;
}
}
}
}
My middleware for implementing correlationid:
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace CorrelationProvider.Middleware
{
public class LogHeaderMiddleware
{
private readonly RequestDelegate _next;
public LogHeaderMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var header = context.Request.Headers["X-SessionId"];
if (header.Count > 0)
{
var logger = context.RequestServices.GetRequiredService<ILogger<LogHeaderMiddleware>>();
using (logger.BeginScope("{#SessionId}", header[0]))
{
await _next(context);
}
}
else
{
await _next(context);
}
}
}
}
program.cs:
global using SimpleEmailApp.Services;
using DAL.Model;
using SimpleEmailApp.ConfgureSetting;
using Serilog;
using CorrelationProvider.Middleware;
using CorrelationProvider;
using Microsoft.AspNetCore.Mvc;
//using ImageTracker.Middleware;
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.AddControllers();
//builder.Host.UseSerilog();
var Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.Enrich.WithCorrelationId()
.CreateLogger();
builder.Logging.ClearProviders();
builder.Logging.AddSerilog(Logger);
builder.Services.AddSwaggerGen();
/// <summary>
/// correlation provider
/// </summary>
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ISessionIdAccessor, SessionIdAccessor>();
/// <summary>
/// /below confiure method is add appsettings.development.json values to appsettings.json and
/// configure host(smtp server settings) for us .
/// also congigure method is made by mailsetting c# class properties and fill with mailsetting in
/// appsettings.development.json file
/// we can use appsettings.json instead appsettings.development.json .
/// </summary>
//builder.Services.Configure<AppSetting>(builder.Configuration.GetSection("MailSetting"));
builder.Services.Configure<AppSetting>(builder.Configuration.GetSection("MailSetting"));
builder.Services.ConfigureWritable<AppSetting>(builder.Configuration.GetSection("MailSetting"));
//builder.Services.AddSingleton<FileContentResult>(new FileContentResult(
// Convert.FromBase64String(builder.Configuration.GetValue<String>("Response:PixelContentBase64")),
// builder.Configuration.GetValue<String>("Response:PixelContentType")
// ));
builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("MailSetting",
optional: true,
reloadOnChange: true);
});
builder.Services.AddScoped<IMailService, MailService>();
/// <summary>
/// correlation Id
/// </summary>
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseMiddleware<LogHeaderMiddleware>();
//app.UseMiddleware<EmailTrackMiddleware>();
/// <summary>
/// static files for sending 1x1 pic
/// </summary>
//app.UseDefaultFiles();
//app.UseStaticFiles();
app.UseDeveloperExceptionPage();
app.MapControllers();
app.Run();
My appsettings.json file:
{
"MailSetting": {
"Mail": "michelminestein#gmail.com",
"Password": "quafcvofqjwfvhqu",
"Host": "smtp.gmail.com",
"Port": 587
},
"Serilog": {
"Using": [
"Serilog.Sinks.File",
"Serilog.Sinks.Console"
],
"MinimumLevel": {
"Default": "Information",
"Override": {
"SimpleEmailApp.Controllers": "Information",
"Microsoft": "Information",
"Microsoft.AspNetCore": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:yyyy/MM/dd HH:mm:ss.fff zzz} {Level:u11}] {CorrelationId} {Message:lj}{NewLine}{Exception}",
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"formatter": "Serilog.Formatting.Compact.RenderedCompactJsonFormatter, Serilog.Formatting.Compact"
}
},
{
"Name": "File",
"Args": {
"path": "../logs/EmailApi-.log",
"rollingInterval": "Day",
"outputTemplate": "[{Timestamp:yyyy/MM/dd HH:mm:ss.fff zzz} {Level:u11}] {CorrelationId} {Message:lj}{NewLine}{Exception}"
}
}
]
},
"AllowedHosts": "*"
}
Any solution to solve this error?
One possible I forgot to mention is in message model I can add message ID and GUID in it, with this solution we can track which message is read by client
Following many tutorials, examples, this example below I call on the server side, but the client side does not receive, sometimes it works but sometimes it doesn’t (more doesn’t work than it works)
It was supposed to be very simple, but it's not, any suggest will help me so much!
Server side
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
var connection = #"data source=comandai.database.windows.net;initial catalog=HojeTaPago;persist security info=True;user id=Comandai;password=Ck#21112009;MultipleActiveResultSets=True;";
services.AddDbContext<ComandaiContext>(options => options.UseSqlServer(connection));
services.AddSignalR(options => options.KeepAliveInterval = TimeSpan.FromSeconds(5));
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "HojeTaPago API", Version = "v1" });
c.AddSecurityDefinition("basic", new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.Http,
Scheme = "basic",
In = ParameterLocation.Header,
Description = "Basic Authorization header using the Bearer scheme."
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "basic"
}
},
new string[] {}
}
});
});
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod().AllowAnyHeader()
.AllowCredentials();
}));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "HojeTaPago API V1");
c.RoutePrefix = string.Empty;
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<AuthenticationMiddleware>();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<NovoPedidoHub>("/novopedidohub");
endpoints.MapControllers();
});
}
}
Where im using the signalr
await _novoPedidoContext.Clients.All.SendAsync("NovoPedido", ListaComandaItem);
Client side - Blazor
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddBlazoredLocalStorage();
services.AddBootstrapCss();
services.AddTransient<HubConnectionBuilder>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
Where i call..
protected override async Task OnInitializedAsync()
{
DataService dataService = new DataService();
PedidosParaAceitar = new List<Comanda>(await dataService.BuscarComandasAbertas());
connection = _hubConnectionBuilder.WithUrl(dataService.servidor + "novopedidohub",
opt =>
{
opt.Transports = HttpTransportType.WebSockets;
opt.SkipNegotiation = true;
}).Build();
connection.On<List<ComandaItem>>("NovoPedido", async lista =>
{
var idEstabelecimento = await localStorage.GetItemAsync<int>("IdEstabelecimento");
if (lista.FirstOrDefault().Comanda.IdEstabelecimento == idEstabelecimento)
{
if (PedidosParaAceitar == null)
PedidosParaAceitar = new List<Comanda>();
if (PedidosParaAceitar.Count(x => x.Id == lista.FirstOrDefault().IdComanda) > 0)
foreach (var comandaitem in lista)
{
PedidosParaAceitar.FirstOrDefault(x => x.Id == lista.FirstOrDefault().IdComanda).ComandaItem.Add(comandaitem);
}
else
PedidosParaAceitar.Add(await dataService.BuscarComandaAberta(lista.FirstOrDefault().IdComanda));
StateHasChanged();
}
});
await connection.StartAsync();
}
You didn't specify in the tags if this was client-side (WASM) or server-side Blazor.
Looking at the question I noticed this line in ConfigureServices:
services.AddServerSideBlazor();
So you're attempting to use SignalR, a client-side communication library from the Server. In server-side Blazor all the C# code runs on the server. In this respect, SignalR is redundant since it's already being used by Blazor for communicating between the clients and the server.
By a very fortunate coincidence, I actually wrote an app to test this out recently. I created a server-side Blazor app, and wrote this service:
public class TalkService
{
public TalkService()
{
history = new List<string>();
}
public Action<string> OnChange { get; set; }
// inform all users of new message
public Task SendAsync(string message)
{
// add to history
history.Add(message);
// ensure only last 10 shown
if (history.Count > 10) history.RemoveAt(0);
OnChange.Invoke(message);
return Task.FromResult(0);
}
private readonly List<string> history;
public IReadOnlyList<string> GetHistory() => history;
}
I then registered it as a Singleton on the server (all clients use the same service)
in Startup.cs in the ConfigureServices() method:
services.AddSingleton<TalkService>();
Then rewrote Index.razor as follows:
#page "/"
#inject TalkService service
<p>Talk App started</p>
<p>Send a message: <input type="text"#bind="#message" />
<button class="btn btn-sm btn-primary" #onclick="Send" >Send</button>
</p>
#foreach (var m in messages)
{
<p>#m</p>
}
#code {
string message;
async Task Send()
{
if(!string.IsNullOrWhiteSpace(message))
await service.SendAsync(message);
message = string.Empty;
}
List<string> messages;
protected override void OnParametersSet()
{
// load history
messages = service.GetHistory().ToList();
// register for updates
service.OnChange += ChangeHandler;
}
protected void ChangeHandler(string message)
{
messages.Add(message);
InvokeAsync(StateHasChanged);
}
}
The talk service is a basic "chat" example of course. It's a Singleton so all pages/clients that reference it use the same instance. The service has a simple event OnChange that clients (like the Index page) can listen to for changes elsewhere.
SignalR isn't needed for this app since it's already "there" for server-side.
Demo App
The demo app also has a background service that generates time messages as well. I've pushed this to GitHub to help as a guide:
https://github.com/conficient/BlazorServerWithSignalR
I wanna get value of a key in resource (.resx) file in .net core API. I followed the marked answer and implemented in my code. But When I am fetching value by key then only Key name is coming.
The linked is below which I followed :
How to get the .resx file strings in asp.net core
Please help me for this.
Thanks
Here is a working example , you could refer to :
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
var connection = #"Server=(localdb)\mssqllocaldb;Database=WebAPIDbContext;Trusted_Connection=True;ConnectRetryCount=0";
services.AddDbContext<RestaurantContext>(options => options.UseSqlServer(connection));
services.AddLocalization(o => o.ResourcesPath = "Resources");
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
//Request Localization
//var options = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
//app.UseRequestLocalization(options.Value);
var supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("zh-cn"),
};
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-US"),
// Formatting numbers, dates, etc.
SupportedCultures = supportedCultures,
// UI strings that we have localized.
SupportedUICultures = supportedCultures
});
app.UseHttpsRedirection();
app.UseMvc();
}
Controller
private readonly IStringLocalizer<SharedResource> _sharedLocalizer;
public ValuesController(IStringLocalizer<SharedResource> sharedLocalizer)
{
_sharedLocalizer = sharedLocalizer;
}
// GET api/values/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
var value = _sharedLocalizer["Title"];
return "value";
}
Resource file path
Note : the namespace of SharedResource.cs should be the root of application
namespace RestaurantReviewApi
{
public class SharedResource
{
}
}
Request url : https://localhost:44318/api/values/3?culture=zh-cn
The screenshot of result :
Reference : https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-2.2
For the purpose of downloading files I need to use a GET: /API/File/ID?bearerToken=XYZ... method.
I've created a DelegatingHandler to add my token to the AuthorizationHeader, but it appears the token validation may be done before this point...
All of the tokens at current at added by Angular adding the token to the HTTP header before the request.
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
GlobalFilters.Add(config);
app.UseWebApi(config);
config.MessageHandlers.Insert(0, new QueryStringBearerToken());
}
..
public class QueryStringBearerToken : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var bearerToken = request.GetQueryNameValuePairs().
Where(kvp => kvp.Key == "bearertoken")
.Select(kvp => kvp.Value)
.FirstOrDefault();
//QueryString exists and Header doesn't
if (!string.IsNullOrWhiteSpace(bearerToken) && !request.Headers.Any(x=>x.Key == "Authorization"))
{
request.Headers.Add("Authorization", "Bearer " + bearerToken);
}
return base.SendAsync(request, cancellationToken);
}
}
I presume you are using Katana's Bearer middleware? (judging by your call to ConfigureAuth?)
If so, the Katana middleware will indeed run before the Web API handlers and reject your request before it even gets a chance to be processed by the handler.
Instead of creating a handler you should move your functionality to Katana middleware.
Here's an example:
public class QueryBearerMiddleware : OwinMiddleware
{
public QueryBearerMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
string bearerToken = null;
if (context.Request.QueryString.HasValue)
{
var queryPairs = context.Request.QueryString.ToUriComponent()
.Substring(1)
.Split(new [] {'&'}, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Split('=')).ToDictionary(x => x[0], x => x[1]);
if (queryPairs.ContainsKey("bearertoken"))
{
bearerToken = queryPairs["bearertoken"];
}
}
//QueryString exists and Header doesn't
if (!string.IsNullOrWhiteSpace(bearerToken) && context.Request.Headers.All(x => x.Key != "Authorization"))
{
context.Request.Headers.Add("Authorization", new [] { "Bearer " + bearerToken });
}
await Next.Invoke(context);
}
}
You should register this middleware to run before the Bearer middlware.
Somewhere in your ConfigureAuth you should have a call to app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());. This new middleware we just created, should be registered before, i.e:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use(typeof(QueryBearerMiddleware));
var config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
GlobalFilters.Add(config);
app.UseWebApi(config);
}
}