Ignore object attribute when using signalR - c#

i have a problem with ignoring an attribute so it doesn't get sent down to the client. I have this object
public class UserEntry : IComparable<UserEntry>
{
[JsonIgnore]
public string UserId { get; set; }
public string AnonymousUserId { get; set; }
}
And i am having a problem for when for example i send a message to my signalR application the UserId is still in the data but i only want the AnonymousUserId to get sent down because the UserId is only used in the backend and shouldn't ever be in client.
so when i get here
public Task TopUserBroadcast(TopUsersBroadcastNotificationModel model)
{
SignalRClient.SendAsync(SignalRAppMethods.Broadcast, Constants.GetInfoGroup(model.InfoId), ResponseMessageIDs.TopUsers,model.Entries);
return Task.CompletedTask;
}
The userId is still in the model.Entries. Anyway i can make it ignore that attribute?
and the TopUsersBroadcastNotificationModel looks like this
public class TopUsersBroadcastNotificationModel
{
public List<UserEntry> Entries { get; set; }
public long InfoId { get; set; }
public TopUsersBroadcastNotificationModel(List<UserEntry> entries, long InfoId)
{
Entries = entries;
InfoId = infoId;
}
}
So what i really want and hoped the [JsonIgnore] would solve is that when its sent down to the client the UserId should be ignored so if i log the entries i get down to client there should be no UserId there.

you need to add a function as following in your entity :
public bool ShouldSerializeXXXX() { return false; }
change the XXXX to your attribute name .
in your case UserId:
public bool ShouldSerializeUserId() { return false; }
If you need to make it dynamic you can input a static boolean value in the return field and change it when you need
EDIT
about your comment , this is what working in my case :
i have a person class with fingerprint as a string field , and i didn't want to send it to by a SignalR hub or even a webservices , but by using an specific action to get the fingerprint , so what considere a entity person with attributs fingerprint as following
public string fingerprint { get; set; }
i added this method :
public bool ShouldSerializefingerprint() { return ShouldSerializefingerprintTest; }
the ShouldSerializefingerprintTest field is a static boolean attributs who can be edited to true or false
by default is false so the fingerprint will not be showen on any webservice/signalR
and when i need to get the fingerprint ShouldSerializefingerprintTest to true
but public bool ShouldSerializefingerprint() { return false; } can do what you need.
you can find more on microsoft doc.

Related

ASP.NET Core can't get object from body

Code:
[HttpPost("user/register")]
public IActionResult Register([FromBody] User user)
{
if (user?.Name is null || user?.Password is null)
{
return BadRequest(new {message = "User is null and/or name and password are not provided"});
}
else
{
// Add to db
return Json(user);
}
}
Also the User class:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public string Role { get; set; }
}
It should basically get a user and add it to the database. I tried sending this json:
{ "Name": "Batman", "Password": "IronmanSucks"}
The app caught the request, but the user object was null. I also tried with:
{ "user": { "Name": "Batman", "Password": "IronmanSucks"} }
But according to this documentation, the first json should have worked fine.
Here is a link to an example http request in postman
Does this have to do with the headers or is it a bug in .NET Core 2.0?
This can only happen if the type does not have a parameterless constructor, thus this can be simply fixed by adding such.
I believe that the Model is coming up as invalid hence why it is null.
You should try adding a [BindNever] attribute into the User class for the Role and Guid properties, seeing as you aren't using them.
If that doesn't work you may try using extended classes like so:
public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
public class DataUser : User
{
public Guid Id { get; set }
public string Role { get; set; }
}
If you're using MVC Core instead of MVC, make sure you add Json Formaters (from Microsoft.AspNetCore.Mvc.Formatters.Json). In your Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvcCore()
.AddJsonFormatters();
}
This should help the [FromBody] to de-serialize your Post content

Disallow user to perform an update on a field or member of a class and return 405 Method Not Allowed : HTTP

I am working on an api which serves creating,updating,deleting of user settings for an application. My users are of two types
admin user
common user
I have a field public bool ReadOnly { get; set; } which says whether a common user is allowed to change the setting or not.
Now the question is in which layer i need to validate this and throw the 405 response to the client. Please suggest.
private readonly SettingsRepository _SettingsRepository;
[HttpPut("{userid}/settings/{settingName}")]
public IActionResult Put(string userid, [FromBody]Setting setting)
{
var result = _SettingsRepository.Update(userid, setting);
if (result == true)
{
return Ok(201);
}
else
{
return BadRequest();
}
}
//Updates the existing setting for a user having userid
public bool Update(string userid, Setting setting)
{
bool flag = false;
if (userid == null || setting == null)
{
return flag;
}
var existing = Profiles.profiles.Where(p => p.UserID.ToLower() == userid.ToLower() && p.Settings.Any(s => s.Name.ToLower() == setting.Name.ToLower())).SelectMany(res => res.Settings).ToList();
if (existing.Count() > 0)
{
existing.ForEach(e =>
{
e.Name = setting.Name;
e.Value = setting.Value;
e.Type = setting.Type;
e.Valid = setting.Valid;
e.ReadOnly = setting.ReadOnly;
e.ModifiedOn = DateTime.UtcNow;
e.Encrypted = setting.Encrypted;
e.Enabled = setting.Enabled;
e.CreatedOn = setting.CreatedOn;
e.Description = setting.Description;
});
FileSerDe.SerializeSettings<IList<Profile>>(Profiles.profiles, System.IO.Directory.GetCurrentDirectory() + "\\" + "seed.txt");
flag = true;
}
return flag;
}
//Profile Entity
public class Profile
{
public string UserID { get; set; }
public string UserName { get; set; }
public List<Setting> Settings { get; set; }
}
//Setting Entity
public class Setting
{
public string Name { get; set; }
public object Value { get; set; }
public string Type { get; set; }
public bool Encrypted { get; set; }
public bool ReadOnly { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
public bool Valid { get; set; }
public bool Enabled { get; set; }
public string Description { get; set; }
}
It looks business logic is in repository. so you can put security measure in repository. do that first thing in repository & throw exception on failed.
this will centralize your business logic to single place.
405 Method not allowed would be used when the HTTP method (e.g. GET or PUT) is specifically not allowed to be used with a given URL, and would apply to all users. For something which is permissions-within your application and related to a specific user it would be more accurate to a send a 403 Forbidden response.
As for the layers, clearly the API Action method is the only one which can return the actual HTTP error code, but since the information that tells you whether the user has permission is held in the database, you could arrange it so that the data layer tells the API layer what the appropriate response should be, perhaps by throwing an exception or by setting a flag on an output parameter to the database method. This would require you to pass information about the current user to the database layer, though, some people think that's unnecessary overhead, unless it's required anyway to record audit data etc.
The alternative is that you could get the API layer to retrieve the relevant data from the database before attempting to run the Update, and make a decision based on that retrieved data, entirely within the API action method. It's really a design decision that is up to you and what suits your application structure. Either way is possible, and, arguably, valid.

Only update parameters where the value is specified by client

I have a servicestack service which accepts a DTO that looks like this:
[Route("/appointment/{id}", Verbs = "POST")]
public class UpdateAppointment
{
public Guid Id { get; set; }
public DateTime StartTime { get; set; }
public int Duration { get; set; }
public string Description { get; set; }
public Guid? MemberId { get; set; }
}
How can I check whether the MemberId value was set by the client since "null" is a valid value. Normally if NULL is not a valid value, I could use the PopulateWithNonDefaultValues() method.
So the result should be that if I don't specify MemberId in my HTTP POST payload, I want the server to not update the value.
I hope that makes sense..
This is not normally an issue if you consider that the client always provides all values when calling the UpdateAppointment Service. So I'd highly recommend that you consider every property is a "valid" value provided by the client and update all fields.
Create a separate Service if only want to update a partial property list.
If I really needed to check whether the client provided a value you can specify a different value in the Request DTO constructor, e.g:
public class UpdateAppointment
{
public UpdateAppointment()
{
MemberId = Guid.Empty;
}
//...
}
where a non Guid.Empty value means it was populated by the client.
Or you could also use a calculated Property:
public class UpdateAppointment
{
[IgnoreDataMember]
public bool HasMemberId { get; set; }
Guid? memberId;
public Guid? MemberId
{
get { return memberId; }
set
{
memberId = value;
HasMemberId = true;
}
}
}
A more fragile alternative is to buffer the Request with the global Request Filter:
appHost.PreRequestFilters.Add((httpReq, httpRes) => {
httpReq.UseBufferedStream = true;
});
Which will retain a copy of the Request Stream which you can get a copy of in your Service with:
var jsonBody = base.Request.GetRawBody();
var hasMemberId = jsonBody.ToLower().Contains("memberid");
Although note this is serializer dependent, i.e. wont work with Binary Serializers like ProtoBuf or MsgPack.
Why is MemberId nullable if null value is not allowed?
Just change its definition to:
public Guid MemberId { get; set; }
have you tried load the values first then editing it before update?
I mean this:
[HttpPost]
public ActionResult Update(UpdateAppointment UAForm){
UpdateAppointment ua = new UpdateAppointmentBll().Find(UAForm.Id);
ua.StartTime = UAForm.StartTime;
//so no...
if(UAForm.MemberId != null)
ua.MemberId = UAForm.MemberId;
new UpdateAppointmentBll().Save(ua);
return View();
}

Model validation, database constraints

I'm trying to add some architecture to my projects and enrich my models.
I started with CQS (implementation similar to that one: CQS-Sample) and here's my first problem.
Let's say I have two classes like these below:
public class Network
{
public int Id { get; set; }
public User User { get; set; }
private IQueryFactory _queryFactory { get; set; }
public Network(IQueryFactory queryFactory)
{
_queryFactory = queryFactory;
}
public void AddUser(User user)
{
if(this.User == null && user != null)
{
userHasUniqueEmail(user);
this.User = user;
}
}
private void userHasUniqueEmail(User user)
{
bool isUnique = _queryFactory.ResolveQuery<INewUserUniqueQuery>().Execute(user.Email);
if (!isUnique)
{
throw new ArgumentException("E-mail is not unique");
}
}
}
public class User
{
public int Id { get; set; }
public string Email { get; set; }
}
Network object can have User, but I need to check first in database that given e-mail doesn't already exists or do some other checkings, so my commands will be executed successfully.
By adding user I mean adding completely new User to database.
Would it be correct way to do this?
You can do it the way you do it now and it's ok.
Another option is to make this Validation in Contoller. Then you should use Remote attribute. And Move your IsEmailUnique(string mail) method to Controller.
If you want to know how you can do it with email check - this question will help you.

C#.NET WebService returning object

I am creating a Web Service using ASP.NET C#. I am sending various data types from the webservice so I use the following structure.
public enum WS_ServiceResponseResult
{
Success,
Failure,
}
public class WS_ServiceResponse
{
public WS_ServiceResponseResult result { get; set; }
public object data { get; set; }
}
public class WS_User
{
public long id{ get; set; }
public string name{ get; set; }
}
Webservice Sample Method
[WebMethod(EnableSession = true)]
public WS_ServiceResponse LogIn(string username, string pasword)
{
WS_ServiceResponse osr = new WS_ServiceResponse();
long userID = UserController.checkLogin(username, pasword);
if (userID != 0)
{
osr.result = WS_ServiceResponseResult.Success;
osr.data = new WS_User() { id = userID, name = username };
}
else
{
osr.result = WS_ServiceResponseResult.Failure;
osr.data = "Invalid username/password!";
}
return osr;
}
I am using two client types, javascript and C#.NET Windows Form. When I call from javascript I get no problem and the osr.data is filled with WS_User. So i can use osr.data.id easily. But when I use from C#.NET (proxy is generated using "Add Web Reference") I can successfully call but when the result arrives I get a Soap Exception
{System.Web.Services.Protocols.SoapException:
System.Web.Services.Protocols.SoapException:
Server was unable to process request.
---> System.InvalidOperationException: There was an error generating the XML
document. ... ...
What am I missing? I Guess object is not well defined and causing the problems. What are the workarounds?
Thanks
Maksud
Addition:
If add the following dummy method, then it works nicely. Hope it helps, to get the solution.
[WebMethod]
public WS_User Dummy()
{
return new WS_User();
}
I had a similar Problem returning an "object" (multiple classes possible)
Here is a sample code:
[Serializable()]
[XmlRoot(ElementName="Object")]
public sealed class XMLObject
{
private Object _Object;
[XmlElement(Type=typeof(App.Projekte.Projekt), ElementName="Projekt")]
[XmlElement(Type=typeof(App.Projekte.Task), ElementName="Task")]
[XmlElement(Type=typeof(App.Projekte.Mitarbeiter), ElementName="Mitarbeiter")]
public Object Object
{
get
{
return _Object;
}
set
{
_Object = value;
}
}
}
I think you should change your code this way:
[XmlRoot(ElementName="ServiceResponse")]
public class WS_ServiceResponse
{
public WS_ServiceResponseResult result { get; set; }
[XmlElement(Type=typeof(WS_User), ElementName="WS_User")]
[XmlElement(Type=typeof(string), ElementName="Text")]
public object data { get; set; }
}

Categories

Resources