Web API in MVC 6 is missing $id - asp.net-web-api

I have noticed that compared to Web API in MVC 5, where each object returned includes an $id property, in MVC 6 no such id is returned. Since my client side code makes use of this property, is there any setting to enable it to be returned in MVC 6?

I think that you formulate the question not full correctly, but I suppose that you can have the same result as before if you would modify ConfigureServices method of Startup.cs. Try to set PreserveReferencesHandling used by Newtonsoft.Json to the value PreserveReferencesHandling.Objects:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddJsonOptions(options => {
options.SerializerSettings.PreserveReferencesHandling =
PreserveReferencesHandling.Objects;
});
...
}
Such setting will don't add $id in really all object, but I think it should work in the cases, which you used before.

Related

reading appSettings from asp.net core webapi

I have an asp.net core 2 webapi service. I want to read appsettings.json but I cannot figure it out. As usual, when I search the internet I get a dozen completely different answers based on various versions of the framework that ultimately don't seem to work.
As an example appsettings.json:
"AppSettings": {
"Domain": "http://localhost"
}
Firstly I tried this:
Added via nuget Microsoft.Extensions.Configuration and Microsoft.Extensions.Configuration.Json
In my ConfigureServices method I added:
services.AddSingleton<IConfiguration>(Configuration);
In my Controller I injected the config:
IConfiguration iconfig;
public ValuesController(IConfiguration iConfig)
{
iconfig = iConfig;
}
To retrieve it:
return "value: " + iconfig.GetValue<string>("AppSettings:Domain");
I can see the controller constructor being called and passing in a config, but the value is always null.
I did notice that Startup already passes in an IConfiguration. Is it a case of yet another change in implementation and i need to do something different?
[edit]
I've now read
https://joonasw.net/view/aspnet-core-2-configuration-changes
Which says it's all change again and it's auto injected, but after following the code it doesn't actually say how you get your hands on the data in your controller.
You can take a look at my answer here: https://stackoverflow.com/a/46940811/2410655
Basically you create a class that matches the properties you define in appsettings.json file (In the answer it's the AppSettings class).
And then in ConfigureServices(), you inject that class using services.Configure<>. That class will be available in all Controllers by DI.
To access its data, you can take a look at #aman's original question post from the link above.
public class HomeController : Controller
{
private readonly AppSettings _mySettings;
public HomeController(IOptions<AppSettings> appSettingsAccessor)
{
_mySettings = appSettingsAccessor.Value;
}
}

Passing TempData with RedirectToAction

Intro:
I am a .NET studet trying to learn ASP.NET Core MVC. So please be understanding. I have searched the web for an answer to my problem, but havent found a solution that works for me.
Problem:
I want to pass a validation message from my create post method to the index IActionmethod whenever a post has been created and them show it as an alert message for now. I have read on the web that ViewBag dosent survive a redirect, but a TempData does. This is my code so far.
Create post method:
public IActionResult CreatePost(string textContent, string headline, string type)
{
var catType = new Category() { CategoryType = type.ToUpper() };
if (db.Category.Any(s => s.CategoryType.Trim().ToLower() == type.Trim().ToLower()))
catType = db.Category.FirstOrDefault(s => s.CategoryType.Trim().ToLower() == type.Trim().ToLower());
var newPost = new Post()
{
Content = textContent,
Header = headline,
DateOfPost = DateTime.Now,
category = catType
};
db.Posts.Add(newPost);
db.SaveChanges();
TempData["validation"] = "Your post hase been publsihed";
return RedirectToAction("Index");
}
The index method:
public IActionResult Index()
{
var validation = TempData["validation"];
var posts = (from x in db.Posts
orderby x.DateOfPost descending
orderby x.PostID descending
select x);
return View(posts);
}
I have tried this guide: ClickThis and this one: ClickThis2 but I got this message:
I know this line from gudie number 2 might be important, but didnt now how to apply it. -
var product = TempData["myTempData"] as Product;
The last thing I want to do is pass it to the index view, but dont know how. I am currently passing a model from the index.
Tell me if it is anything more you would like to see. Like dependencies.
All the help I get is gold and will be much appreciate!!!
I landed on this question while googling for "asp.net core redirect to action tempdata". I found the answer and am posting it here for posterity.
Problem
My issue was that, after filling in some TempData values and calling RedirectToAction(), TempData would be empty on the page that I was redirecting to.
Solution
Per HamedH's answer here:
If you are running ASP.NET Core 2.1, open your Startup.cs file and make sure that in your Configure() method app.UseCookiePolicy(); comes after app.UseMVC();.
Example:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
app.UseCookiePolicy();
}
Did you configure Session? TempData is using session behind the scenes.
Project.json
"Microsoft.AspNetCore.Session": "1.1.0"
Here is the Startup.cs file. - ConfigureServices method
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddSession();
services.AddMvc();
}
And Configure method.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseSession();
app.UseMvc(routes => {
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Now try with TempData, it will work.
And you can set the environment with set ASPNETCORE_ENVIRONMENT=Development environment variable.
TempData stores data server-side, under user Session. You need to enable sessions (as exception message says). Check this manual.
If you don't want to use sessions - you need some other way to store data (cookies?)
Providers
The TempData is using various providers for storing the state. By default the cookie based data provider is used.
Session is just an alternative
If your application do not use session I do not see any reason to use it only for TempData store.
Cookie Consent
ASP NET Core 2.1 have some new GDPR features based on cookies. By default, data should be stored in cookies only with the user's consent. If the user does not agree with the storing data in cookies, TempData cannot work. This behavior varies across versions of ASP NET Core.
If you do not want to hold any sensitive data in cookies, you can obviously change the settings.
app.UseCookiePolicy(new CookiePolicyOptions
{
CheckConsentNeeded = context => false
});
You can set the CookiePolicyOptions separatelly in ConfigureServices as well. It is a quite cleaner.
Story continues
We have two kind of data in the cookies. Essential data (needed for running application) and non-essential (some user data). User consent is needed for non-essential data. TempData is non-essential. You can set you TempData as essential and user consent is not needed anymore:
services.Configure<CookieTempDataProviderOptions>(options => {
options.Cookie.IsEssential = true;
});
I highly recommend to think about this before copy / paste.
I'm just posting this for anyone who comes across this problem in an ASP.NET MVC application, #Ahmar's answer made me go look at my logout method, I was using Session.Abandon() before redirecting to the login page.
I just changed it to Session.Clear() to reset the session instead of removing it completely and now the TempData is working in the method I'm redirecting to.

How to override a web api route?

I am trying to standardize an extension model for our REST API development team. We need to provide default implementation of routes, while allowing for custom implementations of routes that replace the default as well.
As a simple example if we have a GET route api/users like this:
public class DefaultUsersController : ApiController
{
[HttpGet]
[Route("api/users", Order = 0)]
public IEnumerable<string> DefaultGetUsers()
{
return new List<string>
{
"DefaultUser1",
"DefaultUser2"
};
}
}
We expect the default work like this:
Now a developer wants to change the behavior of that route, he should be able to simply define the same route with some mechanism to imply their implementation should be the one used, instead of the default. My initial thinking was to use the Order property on the Route attribute since that's what it appears to be there for, as a way to provide a priority (in ascending order) when an ambiguous route is discovered. However it's not working that way, consider this custom implementation that we want to override the default api/users route:
public class CustomUsersController : ApiController
{
[HttpGet]
[Route("api/users", Order = -1)]
public IEnumerable<string> CustomGetUsers()
{
return new List<string>
{
"CustomUser1",
"CustomUser2"
};
}
}
Notice the Order property is set to -1 to give it a lower priority value than the default, which is set to 0. I would have thought this would be used by the DefaultHttpControllerSelector, but it isn't. From the DefaultHttpControllerSelector:
And we end up with this exception being returned in the response:
Is it possible Microsoft just missed the logic/requirement to use Order as a route disambiguator and this is a bug? Or is there another simple way to override a route, hopefully with an attribute?
I have pretty much the same problem. I am creating a starter site, but I want users to be able to redefine to behaviour of a Controller, especially if there is a bug.
I use Autofac to resolve the Controller, but even when I register the new controller as the old one, the original one gets selected.
What I'll do is probably go with URL Rewriting. Especially since this issue is temporary in my case. However, I would be interested if someone has a better option.

ASP.NET WebAPI OData - Inheriting from EntitySetController<> but using Get(ODataQueryOptions options) rather than [Queryable]Get()

I'm using the ASP.Net WebAPI nightly build (2013-01-16) to get the latest OData support possible.
As the Meta-Me blog on MSDN OData 0.2.0-alpha release post says, there's now an EntitySetController<T> from which OData controllers can be derived to take away a lot of the pain and plumbing code.
The EntitySetController<T> class implements Get() as
[Queryable]
public virtual IQueryable<TEntity> Get()
{
throw EntitySetControllerHelpers.GetNotImplementedResponse(Request);
}
I'd like to make use of the more specific Get(ODataQueryOptions options) method offered by the ASP.Net Web API OData support.
I've coded it as
public IEnumerable<Patient> Get(ODataQueryOptions options)
{
IQueryable patients = entities.Patients;
if (options.Filter != null)
{
patients = options.Filter.ApplyTo(patients, new ODataQuerySettings());
}
return (patients as IQueryable<Patient>).AsEnumerable();
}
(I've also had this return IQueryable<> and saw someone else talk about an ODataResult - that's a type I can't discover at the moment).
However, if I try to use the ODataQueryOptions-based Get method in my own controller I get an error message about multiple actions matching the request. Specifically that error is
Multiple actions were found that match the request:
System.Collections.Generic.IEnumerable`1[Dox.Server.Model.Patient] Get(System.Web.Http.OData.Query.ODataQueryOptions) on type Dox.Server.Web.Controllers.PatientController
System.Linq.IQueryable`1[Dox.Server.Model.Patient] Get() on type System.Web.Http.OData.EntitySetController`2[[Dox.Server.Model.Patient, Dox.Server.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
I assume this is due to the route resolver (sorry if that's poor ASP.NET routing terminology) seeing Get() or Get(...) on the controller's base class as well as the controller class itself.
Questions:
a) Is there some way of adjusting routes to fix this?
b) If not, should I make my own version of EntitySetController<T> and swap out it's Get() method?
The configuration being called by Application_Start() is limited to
public static void EnableOData( HttpConfiguration config )
{
var model = BuildModelImplicitly(config);
//As per LinqPad forum: http://forum.linqpad.net/discussion/178/odata-v3-not-working
IEdmEntityContainer container = model.EntityContainers().First();
model.SetIsDefaultEntityContainer(container, true);
//config.EnableOData(model, "api");
config.Routes.MapODataRoute("OData", "api", model);
//config.EnableSystemDiagnosticsTracing();
}
There's no other configuration being called to do with routes or handlers, etc. Note that the EnableOData() method on HttpConfiguration no longer exists in the latest nightly builds as per this CodePlex discussion.
Thanks very much!
It's very cool to see that you're using our nightly builds :)
The reason you're getting a multiple matching actions error is because EntitySetController already defines a Get method. The good news is that EntitySetController also defines a QueryOptions property that you can use to retrieve the query options. So you should be able to override the EntitySetController's Get method and use the query options property instead of the parameter. It should behave exactly the same way as if you had bound the query options to an action parameter.

ASP.NET MVC3 Fluent Validation Constructor hit multiple times per request

I have an ASP.NET MVC3 website setup using fluent validation and ninject. The validation code is working. However, I set a break point in the validation class constructor and I noticed that when I request my view that uses the validation the constructor gets hit multiple times. Based on very basic testing it seems that the number of times the constructor is hit is equal to the number of properties that exist on the object. Has anyone else come across something similar? Or can someone shed more insight on how this type of validation works behind the scenes? -Thanks
Here is the constructor...
public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
RuleFor(x => x.Id).NotNull();
RuleFor(x => x.Name).Length(0, 10);
RuleFor(x => x.Email).EmailAddress();
RuleFor(x => x.Age).InclusiveBetween(18, 60);
}
}
Here are the libraries/resources that I am using (I just got the NuGet packages and configured everything based on the info from the two links below):
http://fluentvalidation.codeplex.com/wikipage?title=mvc
https://github.com/ninject/ninject.web.mvc.fluentvalidation
I figured out how to prevent this issue. Even though this solves my issue, I would like input from others on whether there are any consequences in doing this?
So on the second link you will see instructions on how to set up Ninject.
On the second step you need to apply the "InRequestScope()" extension method. Then the constructor will only be hit once per http request that uses your validator. That obviously means that only one instance of the validator object is created per http request, which makes sense to me. I don't know if there are any consequences to using this solution?
Bind(match.InterfaceType).To(match.ValidatorType).InRequestScope();
Resurrecting this thread.
I had the same problem with SimpleInjector. My solution was include LifeTime.Scoped on the collection register.
private static void WarmUpMediatrAndFluentValidation(this Container container)
{
var allAssemblies = GetAssemblies();
container.RegisterSingleton<IMediator, Mediator>();
container.Register(typeof(IRequestHandler<,>), allAssemblies);
RegisterHandlers(container, typeof(INotificationHandler<>), allAssemblies);
RegisterHandlers(container, typeof(IRequestExceptionAction<,>), allAssemblies);
RegisterHandlers(container, typeof(IRequestExceptionHandler<,,>), allAssemblies);
//Pipeline
container.Collection.Register(typeof(IPipelineBehavior<,>), new[]
{
typeof(RequestExceptionProcessorBehavior<,>),
typeof(RequestExceptionActionProcessorBehavior<,>),
typeof(RequestPreProcessorBehavior<,>),
typeof(RequestPostProcessorBehavior<,>),
typeof(PipelineBehavior<,>)
});
container.Collection.Register(typeof(IRequestPreProcessor<>), new[] {typeof(EmptyRequestPreProcessor<>)});
container.Collection.Register(typeof(IRequestPostProcessor<,>), new[] {typeof(EmptyRequestPostProcessor<,>)});
container.Register(() => new ServiceFactory(container.GetInstance), Lifestyle.Singleton);
container.Collection.Register(typeof(IValidator<>), allAssemblies, Lifestyle.Scoped);
}
container.Collection.Register(typeof(IValidator<>), allAssemblies, Lifestyle.Scoped); <- Workers fine for me, calling only once per request.

Resources