Is it possible to config multiple routes with ODATA - odata

I've created a project containing both MVC and WebAPI.
There's a MVC controller named HomeController which containing only one action named Index.
Also, there's a API controller named ValuesController.
Everything works fine without OData, I can access to both http://localhost/Home/Index and http://localhost/api/Values successfully.
However, after I changed some code to support OData, I'm failed to access to http://localhost/Home/Index. Below is the related code:
//startup.cs
public void ConfigureServices(IServiceCollection services) {
services.AddDbContext<ProductsContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDb");
});
//Adding OData middleware.
services.AddOData();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider provider){
if (env.IsDevelopment()){
app.UseDeveloperExceptionPage();
}
//Adding Model class to OData
var builder = new ODataConventionModelBuilder(provider);
builder.EntitySet<ValuesEntity>("Values");
builder.EntityType<ValuesEntity>().Filter(QueryOptionSetting.Allowed).OrderBy(QueryOptionSetting.Allowed);
//Enabling OData routing.
app.UseMvc(routebuilder =>
{
routebuilder.MapODataServiceRoute("odata", "api", builder.GetEdmModel());
routebuilder.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
As you can see, there're multiple routes, but the default route seems never get used.
I can still access to the http://localhost/api/Values, but If I access to the http://localhost/Home/Index, the page will show me the error:
An unhandled exception occurred while processing the request.
ArgumentNullException: Value cannot be null.
Parameter name: routeName
Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.CreateRequestScope(HttpRequest request, string routeName)

Related

.net attribute routes not matched in production

I have a asp.net v6 mvc project with an spa front end that works fine in development. (Following the new pattern where my spa dev server has a proxy configuration to forward api requests to the .net app).
When I deploy this to IIS, my api routes (all beginning "api/") are no longer recognised, and all api requests are redirected to the fallback spa home page. What's going on ?
This is my Configure method, in Startup.cs
public void Configure(IApplicationBuilder builder, IWebHostEnvironment env)
{
...
builder.UseStaticFiles();
builder.UseRouting();
builder.UseEndpoints(endpoints =>
{
// I added this line to try and fix my problem. It's not necessary in dev, but still broken in prod.
endpoints.MapControllers();
// This was all I had in dev, but all my Controller actions have AspNetCore.Mvc Route attributes.
endpoints.MapControllerRoute(
name: "default",
pattern: "api/{controller}/{action=Index}/{id?}");
// I know this method is called and used, because if I change "index.html" everything breaks and I'm no longer redirected to my spa
endpoints.MapFallbackToFile("index.html");
});
}
Here is a controller...
using Microsoft.AspNetCore.Mvc;
[ApiController]
public class AllItemsController : ControllerBase
{
[Route("api/all-items")]
public ApiResponse<List<Item>, string> Get()
{

Include a file extension in ASP.NET Core MVC routes

I'm porting a legacy application to ASP.NET Core and I'm trying to keep the URLs consistent. Ideally I need to include a "file extension" in the URL, e.g. /Home/Index.ext should route to the Index action on HomeController.
I've started with the standard web application template in Visual Studio and tried modifying the default route:
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}.ext/{id?}");
A request for /Home/Index.ext then gives the error:
An unhandled exception occurred while processing the request.
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebApplication1.Controllers.HomeController.Index (WebApplication1)
WebApplication1.Controllers.HomeController.Privacy (WebApplication1)
WebApplication1.Controllers.HomeController.Error (WebApplication1)
Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(CandidateState[] candidateState)
I can't see why it's not taking the action name from this URL to select the correct action. Is there some way I can change this route pattern to get it to match this correctly?
You can do this with the IOutboundParameterTransformer that's inside Microsoft.AspNetCore.Routing.
Simply create a transformer as follows (if you want the .ext extension):
public class AddExtensionTransformer : IOutboundParameterTransformer
{
public string TransformOutbound(object value)
=> value + ".ext";
}
Then you register it as a constraint (I used the name addextension):
services.Configure<RouteOptions>(options =>
{
options.ConstraintMap["addextension"] = typeof(AddExtensionTransformer);
});
And then in your route, you simply use addextension as a route constraint (using the : delimiter).
For the default route from your post, it will look like this:
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action:addextension=Index}/{id?}");
ASP.NET Core will now also automatically generate URLs like /Home/Index.ext whenever you tell it to generate an action URL.
So it not only parses them but also generates them (works both ways).
You can then use an Attribute Route like this:-
[Route("Home/Index.ext")]
public ActionResult Index() {... }
For more information read this Documentation
You can use URL Rewriting Middleware like below(html extension as a sample):
1.Custom a class containing ReWriteRequests
public class RewriteRules
{
public static void ReWriteRequests(RewriteContext context)
{
var request = context.HttpContext.Request;
if (request.Path.Value.EndsWith(".html", StringComparison.OrdinalIgnoreCase))
{
context.HttpContext.Request.Path = context.HttpContext.Request.Path.Value.Replace(".html", "");
}
}
}
2.Startup.cs:
Be sure use URL Rewriting Middleware before app.UseStaticFiles();
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRewriter(new RewriteOptions()
.Add(RewriteRules.ReWriteRequests)
);
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
Reference:
URL Rewriting Middleware in ASP.NET Core

Swagger breaks when adding an API Controller to my Host Project in aspnetboilerplate

I downloaded a new .Net Core MVC project template from https://aspnetboilerplate.com/Templates, setup everything (database and restored nuget packages) and ran the Host application. All good as I get the Swagger UI going and can see all the standard services.
I then proceeded to create a simple API controller in the Host application:
[Route("api/[controller]")]
[ApiController]
public class FooBarController : MyAppControllerBase
{
public string HelloWorld()
{
return"Hello, World!";
}
}
And then Swagger fails to load the API definition:
Fetch error
Internal Server Error http://localhost:21021/swagger/v1/swagger.json
If I remove the Route and ApiController attributes, Swagger works again, but my new controller is not displayed. I can access it by going to http://localhost:21021/foobar/helloworld which is probably fine, but I'd like it to show up in Swagger UI.
Am I missing something?
This is how you should configure your Swagger in your "Configure(IApplicationBuilder app, IHostingEnvironment env)" method.
#region Swagger COnfiguration
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", "Your class name");
c.RoutePrefix = string.Empty;
});
#endregion
And here will be your configureServices settings for swagger.
services.AddSwaggerGen(config =>
{
config.SwaggerDoc("v1", new Info
{
Title = "Title Here",
Version = "v1"
});
});

How to get session value asp.net core inside a view

I am trying to get session like this
#HttpContext.Session.GetString("some");
But I am getting
*
An object reference is required for the non-static field ......
*
Any one with ideas?
You have to inject the IHttpContextAccessor implementation to the views and use it.
#using Microsoft.AspNetCore.Http
#inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
Now you can access the HttpContext property and then Session
<p>
#HttpContextAccessor.HttpContext.Session.GetInt32("MySessionKey")
</p>
Assuming you have all the necessary setup done to enable session in the startup class.
In your ConfigureServices method,
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
and IApplicationBuilder.UseSession method call in the Configure method.
app.UseSession();
First enable session by adding the following 2 lines of code inside the ConfigureServices method of startup class:
services.AddMemoryCache();
services.AddSession();
In the same method add the code for creating singleton object for IHttpContextAccessor to access the session in the view.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Now inside the Configure method of startup class you will have to add .UseSession() before the .UseMvc():
app.UseSession();
The full code of Startup class is given below:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddMemoryCache();
services.AddSession();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseStaticFiles();
app.UseSession();
app.UseMvc(routes =>
{
// Default Route
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Then go to the view and add the following 2 lines at the top:
#using Microsoft.AspNetCore.Http
#inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
And then you can get the value from session variable in the view like:
#HttpContextAccessor.HttpContext.Session.GetString("some")
I have only modified the answer from #Shyju by including the complete code.
I know there has already been answers, but for ASP.NET Core 6 things little changed.
Again you have to add Session in services, and use it in middleware pipeline.
In Program.cs add the following
builder.Services.AddSession();
app.UseSession();
But instead of injecting HttpContextAccesor and so on, you can have direct access to Context class in view like this`
Context.Session.SetInt32("sessionInt",1);

MVC and Sitefinity api

I am working on a project that needs to update data in a Sitefinity database. I added the Telerik.Sitefinity_All dll's to the project. I've added the connection string to the database that contains all the sitefinity data. I'm trying to use the API for sitefinity to connect to the database and pull the data but I'm having troubles. How do you configure the App.WorksWith() to use the connection for the sitefinity database, or is there any good documentation showing how to fully set this up? Thanks in advance for any help, I'm extremely new to Sitefinity. FYI, this is using Sitefinity v 9.2
Presuming the site builds and runs fine here is what you need to do in order to be able to create a custom Web Api service which will use the Sitefinity API and will allow the web api to be called from external applications:
Register a custom route - this is done in Global asax file. See example below:
protected void Application_Start(object sender, EventArgs e)
{
SystemManager.ApplicationStart += SystemManager_ApplicationStart;
}
private void SystemManager_ApplicationStart(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
private void RegisterRoutes(RouteCollection routes)
{
routes.Ignore("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "ajax/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });
}
Here I am using the /ajax/ route because /api/ is already taken by Sitefinity in 9.2.
Create your web api controller and use the Sitefinity API inside:
public class CourseController : ApiController
{
[HttpPost]
public HttpResponseMessage CreateOrUpdateCourse([FromBody] Course item)
{
// use Sitefinity API here
// if you need to make modifications to the data then you need to use the ElevatedModeRegion }}

Resources