Query parameter routing with asp .net core - asp.net-core

I'd like to use url query parameter instead of path parameter using .net core API.
controller
[Route("api/[controller]/[action]")]
public class TranslateController : Controller
{
[HttpGet("{languageCode}")]
public IActionResult GetAllTranslations(string languageCode)
{
return languageCode;
}
}
startup.cs is using only default settings
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc()
.AddJsonOptions(jsonOptions =>
{
jsonOptions.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
jsonOptions.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
jsonOptions.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
services.AddLogging();
services.AddSingleton<IConfiguration>(Configuration);
services.AddSwaggerGen(c =>
{
c.SingleApiVersion(new Info
{
Version = "v1",
Title = "Translate API",
Description = "bla bla bla description",
TermsOfService = "bla bla bla terms of service"
});
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUi();
}
my swagger request looks like this
my postman request is here
I would like to change my GetAllTranslations to accept query parameter instead of path parameter but when I change my postman query to
http://localhost:42677/api/Translate/GetAllTranslations?languageCode=en
I will get error 404 Not found so obviously my controller path is not set correctly, but I cannot find out how to do this... Any Ideas?
I have tried removing the [HttpGet("{languageCode}")] attribute, but I keep getting null parameter instead of the value.

This is what you're looking for
public IActionResult GetAllTranslations([FromQuery]string languageCode)

The answer from #jcmontx worked, but it doesn't explain why parameter bindind needs to be explicitly set. I am still not sure if and why is this enforced,
but one reason would be that if binding parameters are not set explicitly, it opens up the API to be used the way it was not intended, which is not very secure neither a good practice.

Related

Problem in enabling CORS in asp net core web api v3.0

I am using asp net core 3.0 in my web API project. I have created various API's and all are accessible via Swagger or Postman. But when trying to access the same via any other client like React, Method not allowed (405 error code) is received. On investing further, I find out that at first, OPTION request is received from the React application and the net core web API application is giving the 405 status code. Further, I find out that I need to enable all the methods as well as origins from the net core application to accept all types of requests otherwise it will not accept OPTION request. To achieve this, I enabled CORS policy in startup.cs file but still had no luck. Following is my startup.cs file:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
var elasticUri = Configuration["ElasticConfiguration:Uri"];
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.WithExceptionDetails()
.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri(elasticUri))
{
MinimumLogEventLevel = LogEventLevel.Verbose,
AutoRegisterTemplate = true,
})
.CreateLogger();
}
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.Configure<IISServerOptions>(options =>
{
options.AutomaticAuthentication = false;
});
services.Configure<ApiBehaviorOptions>(options =>
{
//To handle ModelState Errors manually as ApiController attribute handles those automatically
//and return its own response.
options.SuppressModelStateInvalidFilter = true;
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
services.AddControllers(options =>
{
//To accept browser headers.
options.RespectBrowserAcceptHeader = true;
}).
AddNewtonsoftJson(options =>
{
// Use the default property (Pascal) casing
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
}).
AddJsonOptions(options =>
{
//Not applying any property naming policy
options.JsonSerializerOptions.PropertyNamingPolicy = null;
options.JsonSerializerOptions.IgnoreNullValues = true;
}).
AddXmlSerializerFormatters().
AddXmlDataContractSerializerFormatters();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors("CorsPolicy");
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", "My API V1");
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
//Configuring serilog
loggerFactory.AddSerilog();
}
}
I tried testing the same API with the OPTIONS method from POSTMAN. It is also giving the Http Status Code as 405. But when trying to access the same request using the POST method, I received the response successfully.
Is there anything wrong with the above code or something wrong with the order of middlewares being called in Configure().
Try to add extension method and modifying your startup class:
Extension method:
public static void AddApplicationError(this HttpResponse response, string
message)
{
response.Headers.Add("Application-Error", message);
response.Headers.Add("Access-Control-Expose-Headers", "Application-Error");
response.Headers.Add("Access-Control-Allow-Origin", "*");
}
Startup.cs :
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode = (int)
HttpStatusCode.InternalServerError;
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
context.Response.AddApplicationError(error.Error.Message);
await context.Response.WriteAsync(error.Error.Message);
}
});
});
}
P.S. in my case I had scenario also returning 405 status error, cause was, similar action methods I used and there are conflicted
For ex:
[HttpGet]
public ActionResult GetAllEmployees()
[HttpGet]
public ActionResult GetCustomers()
Hope this will help at least to show exact error message
You need to add Cors in Startup.cs file under your web api project
add this variable in Startup.cs
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
add services.AddCors before services.AddControllers() in the method ConfigureServices in file Startup.cs:
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://localhost:4000",
"http://www.yourdomain.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddControllers();
*** You can pass only * to allow all instead of passing http://localhost:4000","http://www.yourdomain.com in the WithOrigins method
add app.UseCors before app.UseAuthentication() in the method Configure in file Startup.cs:
app.UseCors(MyAllowSpecificOrigins);
Check this Microsoft help
Try this:
app.UseCors(policy =>
policy.WithOrigins("https://localhost:PORT", "https://localhost:PORT")
.AllowAnyMethod()
.WithHeaders(HeaderNames.ContentType)
);

Why am I looking at JSON, not my nice Swagger UI?

I've installed swashbuckle on on a clean asp.net core web api project following these instructions. My startup class is below. You can see I've added AddSwaggerGen(), UseSwagger() and UseSwaggerUI().
When I visit https://localhost:44334/swagger/v1/swagger.json, instead of seeing the swagger UI I expect, I've got a pile of JSON, starting {"swagger":"2.0","info":{"version":"v1","title":"MoqOcr"}...
What am I missing ?
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.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// sby
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "MoqOcr", Version = "v1" });
});
}
// 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
{
app.UseHsts();
}
// sby
// 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", "My API V1");
c.RoutePrefix = string.Empty;
});
//app.UseHttpsRedirection();
app.UseMvc();
}
}
I have checked your configuration for Swagger in Startup.cs and there seemed no unexpected thing to setup swagger to me. The only thing that pops in my mind is that you are mistaken the SwaggerEndpoint setting which indicates to you (I suppose) that you can access your Swagger UI from that url but it holds a json to build and configure that UI page. Fair enough but you should try https://localhost:44334/swagger
or https://localhost:44334/swagger/index.html to see your Swagger UI page. Hope this solves your problem.

Get request outside a controller in .netcore

Is there a way I can get the response from a method without using a controller. I mean, in order to get the tenants from the database I use attribute binding and I get it from: "http://localhost:5000/api/tenants". Is there a way I can retrieve values without using a controller, like a service? For example in angular I use httpclient to get the response. Is there anything similar in .netcore 2 webapi? Thank, you!
For Controller, it uses UseMvc middleware to route the request to controller.
If you would not use controller, you could try custom middleware to return the data directly based on the request path.
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)
{
//your config
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//your config
app.Map("/tenants", map => {
map.Run(async context => {
var dbContext = context.RequestServices.GetRequiredService<MVCProContext>();
var tenants = await dbContext.Users.ToListAsync();
await context.Response.WriteAsync(JsonConvert.SerializeObject(tenants));
});
});
app.Run(async context => {
await context.Response.WriteAsync($"Default response");
});
}
}

Redirect to HTTPS in Blazor

I have a blazor app.
I hosted it on server and have access with https.
But when i do redirect (in one controller), happens exception.
Startap.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseMvc(routes =>
{
routes.MapRoute(name: "default", template: "{controller}/{action}/{id?}");
});
app.Map("/schedule", subdirApp =>
{
subdirApp.UseBlazor<Client.Startup>();
});
}
And method in controller
[HttpGet]
[Route("***")]
public IActionResult Return()
{
FileStream fs = new FileStream(_filePath, FileMode.Open);
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
List<ScheduleEntity> _list = (List<ScheduleEntity>)formatter.Deserialize(fs);
foreach (var x in _list)
Schedules.Add(x);
fs.Close();
return Redirect("~//schedule");
}
Exception
Please, help me
These API responses can be a bit misleading. Without seeing the rest of your code around the configuration of endpoints, I suspect this might be a CORS issue with the API.
Try adding the following code to the public void Configure(IApplicationBuilder app, IHostingEnvironment env) method in your API's Startup.cs class:
app.UseCors(opts => opts
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
);
The fetch response may be due to the request preflight being rejected.
Having said that, the first exception message is saying you're trying to load insecure content, so I'd also check your Blazor front-end app's configuration to see what the API client is requesting and ensure the API endpoint certificate is valid?

I am using NSwag with an ASP.Net Core API and the Swagger UI client is displaying

I am using NSwag with an ASP.Net Core API, when I execute the web API and navigates to the Swagger UI it displays the following error:
Fetching resource list: undefined. Please wait. It gives an 404 and tells me that Cannot read property 'substring' of undefined, that when I tried to trace the error is pointing to the Swagger client in self.url.substring. Although the json displayed in the swagger.json is totally correct.
This is my Startup.cs class with the Explorer Solution at the right showing my nuget dependencies:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// 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();
}
app.UseStaticFiles();
// Enable the Swagger UI middleware and the Swagger generator
app.UseSwaggerUi(typeof(Startup).GetTypeInfo().Assembly, settings =>
{
settings.SwaggerUiRoute = "/swagger";
settings.PostProcess = document =>
{
document.Info.Version = "v1";
document.Info.Title = "Analisis API";
document.Info.Description = "A simple ASP.NET Core web API";
document.Info.TermsOfService = "None";
document.Info.Contact = new NSwag.SwaggerContact
{
Name = "Example",
Email = "example#gmail.com",
Url = "http://google.es"
};
document.Info.License = new NSwag.SwaggerLicense
{
Name = "Use under LICX",
Url = "https://example.com/license"
};
};
app.UseMvc();
});
}
And this is my controller:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : Controller
{
public IDatosAnalisis datosManager = new DatosAnalisis();
public IResultado resultadoManager = new Resultado();
[HttpGet]
public ActionResult<String> GetDefault()
{
return "Bienvenido a AnalisisApi";
}
[HttpGet("getResultado/{patologiaId}")]
[ProducesResponseType(typeof(ResultadoDTO), 200)]
public ActionResult<ResultadoDTO> GetResultadoByPatologiaId(int patologiaId)
{
ResultadoDTO result = resultadoManager.getResultadoByPatologia(patologiaId);
return result;
}
/// <summary>
/// Receives the analisis data and evaluates them.
/// </summary>
[HttpPost]
public ActionResult<List<ShortResultDTO>> TestValoresAnalisis(DatosSujetoDTO datosSujeto)
{
List<ShortResultDTO> results = datosManager.postDatosAnalisisAndGetPatologias(datosSujeto);
return results;
}
}
Thanks in advance for any help given!
Same problem here, my workaround: using a custom url to visit Swagger
https://localhost:44336/swagger/index.html?url=/swagger/v1/swagger.json