ASP.NET GET request always returns blank - asp.net

I'm trying to make an endpoint to return a JSON response. I've tried narrowing it down to just the object and all I receive is {} or [{}] as a response. After debugging I confirmed that the object was being created correctly but when returning the response it was always blank. Below is simplified code but still has the same issue. Any ideas what I'm doing wrong?
[Route("{application}")]
[HttpGet]
public IActionResult Get(string application)
{
List<RequestedSetting> requestedSettings = new List<RequestedSetting>();
RequestedSetting rs = new RequestedSetting("foo", "bar");
requestedSettings.Add(rs);
return Json(requestedSettings);
}
public class RequestedSetting
{
public string Name;
public string Value;
public RequestedSetting(string name, string value)
{
Name = name;
Value = value;
}
}
I've also tried this:
[Route("{application}")]
[HttpGet]
public List<RequestedSetting> Get(string application)
{
List<RequestedSetting> requestedSettings = new List<RequestedSetting>();
RequestedSetting rs = new RequestedSetting("foo", "bar");
requestedSettings.Add(rs);
return requestedSettings;
}

Fields are not supported for serialisation in System.Text.Json. see the docs for more info.
Instead, change your class to use properties:
public class RequestedSetting
{
public string Name { get; set; }
public string Value { get; set; }
public RequestedSetting(string name, string value)
{
Name = name;
Value = value;
}
}

Related

Auto Mapper Constructor initialization Mapping Issue

I have the following Mapping configurations:-
Initialized Data:-
private static IEnumerable<Source> InitializeData()
{
var source= new[]
{
new Source("John", "Doe", "1111111111"),
new Source("Jack", "Handsome", "2222222222"),
new Source("Joe", "Mackenze", "3333333333")
};
return source;
}
Source Model:
public class Source
{
private string First { get; set; }
private string Last { get; set; }
private string Phone { get; set; }
public Source(string first, string last, string phone)
{
First = first;
Last = last;
Phone = phone;
}
}
Destination Model
public class Destination
{
public string First { get; set; }
public string Last { get; set; }
public string Phone { get; set; }
}
Main
static void Main(string[] args)
{
var config = new MapperConfiguration(cfg =>
{
cfg.AllowNullCollections = true;
cfg.CreateMap<Source, Destination>().ReverseMap();
});
var mapper = new Mapper(config);
var source= InitializeData();
var people = mapper.Map<IEnumerable<Destination>>(source);
foreach (var p in people)
{
Console.WriteLine("Name: {0}-{1} Phone: {2}", p.First, p.Last, p.Phone);
}
Console.ReadLine();
}
Problem descriptions:
I have been struggled to understand the AutoMapper mapping between source and destination models.
My source model has a constructor to initialize or accept data from outside. It works fine when I removed the source constructor from the model that's mean flat mapping works fine but constructor initialization has the issue. When I debug in VS2019, it shows the number of records but all fields are empty/null.
What is wrong with the above mapping. I have gone through the AutoMapper reference docs but do not get a hold on this issue.
I highly appreciate your help!
Try calling AssertConfigurationIsValid. Check http://docs.automapper.org/en/latest/Configuration-validation.html.
Your Source properties are private. I assume you meant public.

System.Text.Json Deserialize Fails

With this DTO:
public class QuestionDTO {
public Guid Id { get; set; }
public string Prompt { get; set; }
public List<Answer> Choices { get; set; }
public QuestionDTO() {
}
public QuestionDTO(Question question) {
this.Id = question.Id;
this.Prompt = question.Prompt;
this.Choices = question.Choices;
}
}
I was getting an error about Unable to Parse without a parameterless constructor. I have since fixed that, but now my objects are de-serialized empty:
using System.Text.Json;
var results = JsonSerializer.Deserialize<List<QuestionDTO>>(jsonString);
The jsonString contains 3 items with the correct data, and the deserialized list contains 3 items, but all the properties are empty.
The new json library is case sensitive by default. You can change this by providing a settings option. Here is a sample:
private JsonSerializerOptions _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
private async Task SampleRequest()
{
var result = await HttpClient.GetStreamAsync(QueryHelpers.AddQueryString(queryString, queryParams));
_expenses = await JsonSerializer.DeserializeAsync<List<Common.Dtos.Expenses.Models.Querys.ExpensesItem>>(result, _options);
}

Data Annotations to sanitize request and response before logging

I'm looking for a reliable solution to log details of requests and responses made to and from our controllers. However, some of the data passing through contains sensitive information that should not be written to a log.
In the controller, the inbound request is bound to a single model from the request body, and as the request is answered, a single model is passed to the Ok() result like this (very simplified):
[HttpGet]
[Route("Some/Route")]
public IHttpActionResult SomeController([FromBody] RequestType requestObj)
{
ResponseType responseObj = GetResponse(requestObj)
return this.Ok(responseObj);
}
Now my goal is to somehow log the contents of the request and response object at the beginning and end of the controller, respectively. What I would like to do is bind the models first, then log out their attributes. An example of the RequestType is something like:
public class RequestType
{
public string SomeAttribute { get; set; }
public string AnotherAttribute { get; set; }
public string Password{ get; set; }
}
And the log would look something like:
[date-time] Request to SomeController:
SomeAttribute: "value_from_request"
AnotherAttribute: "another_value"
Password: "supersecret123"
Now clearly we don't want the password to be logged. So I would like to create a custom data annotation that would not log certain fields. Its use would look like this (updated RequestType):
public class RequestType
{
public string SomeAttribute { get; set; }
public string AnotherAttribute { get; set; }
[SensitiveData]
public string Password{ get; set; }
}
Where would I start with this? I'm not incredibly familliar with .NET, but know that there are many sort of magic classes that can be subclassed to override some of their functionality. Is there any such class that can help here? Even better, is there any way to do this during the model binding? So we could catch errors that occur during model binding as well?
We should be able to achieve what you're looking for with an ActionFilterAttribute.
Capture Requests Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class CaptureRequestsAttribute : ActionFilterAttribute // *IMPORTANT* This is in the System.Web.Http.Filters namespace, not System.Web.Mvc
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var messages = actionContext.ActionArguments.Select(arg => GetLogMessage(arg.Value));
var logMessage = $"[{DateTime.Now}] Request to " +
$"{actionContext.ControllerContext.Controller}]:\n{string.Join("\n", messages)}";
WriteToLog(logMessage);
base.OnActionExecuting(actionContext);
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var result = actionExecutedContext.Response.Content as ObjectContent;
var message = GetLogMessage(result?.Value);
var logMessage = $"[{DateTime.Now}] Response from " +
$"{actionExecutedContext.ActionContext.ControllerContext.Controller}:\n{message}";
WriteToLog(logMessage);
base.OnActionExecuted(actionExecutedContext);
}
private static void WriteToLog(string message)
{
// todo: write you logging stuff here
}
private static string GetLogMessage(object objectToLog)
{
if (objectToLog == null)
{
return string.Empty;
}
var type = objectToLog.GetType();
var properties = type.GetProperties();
if (properties.Length == 0)
{
return $"{type}: {objectToLog}";
}
else
{
var nonSensitiveProperties = type
.GetProperties()
.Where(IsNotSensitiveData)
.Select(property => $"{property.Name}: {property.GetValue(objectToLog)}");
return string.Join("\n", nonSensitiveProperties);
}
}
private static bool IsNotSensitiveData(PropertyInfo property) =>
property.GetCustomAttributes<SensitiveDataAttribute>().Count() == 0;
}
Sensitive Data Attribute
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class SensitiveDataAttribute : Attribute
{
}
Then, you can just add it to your WebApi controller (or a specific method in it):
[CaptureRequests]
public class ValuesController : ApiController
{
// .. methods
}
And finally your models can just add the SensitiveDataAttribute:
public class TestModel
{
public string Username { get; set; }
[SensitiveData]
public string Password { get; set; }
}
This does not make use of DataAnnotations,however, One way that comes to mind would be to use the serialization. If your payload is within a reasonable size you could serialize and deserialize your RequestType class when reading and writing to/from a log. This would require a custom serialization format or making use of the default, xml.
[Seriliazeble()]
public class RequestType
{
public string SomeAttribute { get; set; }
public string AnotherAttribute { get; set; }
[NonSerialized()]
public string Password{ get; set; }
}
Using the above attribute will omit Password from serialization. Then you copuld proceed to Logger.Log(MySerializer.Serialize(MyRequest)); and your sensitive data will be omitted.
This link describes the approach in detail.
For xml serialization, simply use the XmlSerializer class.
public class MySerializationService
{
public string SerializeObject(object item)
{
XmlSerializer serializer = new XmlSerializer(item.GetType());
System.IO.MemoryStream aMemStr = new System.IO.MemoryStream();
System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(aMemStr, null);
serializer.Serialize(writer, item);
string strXml = System.Text.Encoding.UTF8.GetString(aMemStr.ToArray());
return strXml;
}
public object DeSerializeObject(Type objectType, string objectString)
{
object obj = null;
XmlSerializer xs = new XmlSerializer(objectType);
obj = xs.Deserialize(new StringReader(objectString));
return obj;
}
}
Then using the above or similar methods you can read and write in a custom format.
Write :
string logData=new MySerializationService().SerializeObject(myRequest);
Read :
RequestType loggedRequest= (RequestType)new MySerializationService().DeSerializeObject(new RequestType().GetType(), logData);

Unit Test a method that searches element in db

I wanted to do a Unit Test for a method in the following Class:
public class SearchInfo : IDisposable
{
public List<SearchResult> SerialResults { get; set; }
public List<SearchResult> TagResults { get; set; }
/// <summary>
/// Uses the Search string to filter a resultset by Serial and Tag
/// </summary>
/// <param name="search"></param>
public SearchInfo(string search)
{
SerialResults = new List<SearchResult>();
TagResults = new List<SearchResult>();
SerialResults.AddRange(FindSerial(search));
TagResults.AddRange(FindTags(search));
}
private static IEnumerable<SearchResult> FindTags(string search)
{
List<SearchResult> result;
using (var db = new TIPWebITDataContext())
{
result = (from i in db.tblTechInventories
where i.Tag.Equals(search)
select new SearchResult()
{
SearchType = "Tag",
Key = i.Tag,
KeyCaption = i.Tag,
Name = i.tblTechItem.ItemName
}).ToList();
}
return result;
}
private static IEnumerable<SearchResult> FindSerial(string search)
{
List<SearchResult> result;
using (var db = new TIPWebITDataContext())
{
result = (from i in db.tblTechInventories
where i.Serial.Contains(search)
select new SearchResult()
{
SearchType = "Searial",
Key = i.Tag,
KeyCaption = i.Serial,
Name = i.tblTechItem.ItemName
}).ToList();
}
return result;
}
#region Implementation of IDisposable
public void Dispose()
{
SerialResults = null;
TagResults = null;
}
#endregion
}
public class SearchResult
{
public string SearchType { get; set; }
public string Key { get; set; }
public string KeyCaption { get; set; }
public string Name { get; set; }
}
}
Note:
Method Name = FindTags() returns return result.
The method checks the entered tag is present in database or not. If it is available returns the result.
How will we unit test this method?
Have mocked the Repo
Have covered scenario of "THROWS EXCEPTION"
Question is how to unit test the scenario of ALL WENT WELL ie user;s makeModel matched with repository's makeModel
Strictly speaking, this is an integration test and not a unit test.
With a unit test, you would mock the DB interaction and simple test the logic in your code.
However, I appreciate that it is easier to test in this manner and do so myself. The way to deal with this would be to inject test data into your database that gives a consistent result and assert against that. This test data can be injected in TestSetup and removed if required in TestCleanup.

Web API error failed to serialize the response body

Im fairly new to ASP.NET MCV 4 as well as Mongo DB and trying to build web API.
I thought I had finally got it right but when I start the app and enter: http://localhost:50491/api/document into my browser I get this error message
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
Here is my code
This is the Document Class
public class Document
{
[BsonId]
public ObjectId DocumentID { get; set; }
public IList<string> allDocs { get; set; }
}
This is where the Connection to the DB is made:
public class MongoConnectionHelper
{
public MongoCollection<BsonDocument> collection { get; private set; }
public MongoConnectionHelper()
{
string connectionString = "mongodb://127.0.0.1";
var server = MongoServer.Create(connectionString);
if (server.State == MongoServerState.Disconnected)
{
server.Connect();
}
var conn = server.GetDatabase("cord");
collection = conn.GetCollection("Mappings");
}
Here is the ApiController Class:
public class DocumentController : ApiController
{
public readonly MongoConnectionHelper docs;
public DocumentController()
{
docs = new MongoConnectionHelper();
}
public IList<BsonDocument> getAllDocs()
{
var alldocs = (docs.collection.FindAll().ToList());
return alldocs;
}
}
I read futher on and the error message suggested:
Type 'MongoDB.Bson.BsonObjectId' with data contract name 'BsonObjectId:http://schemas.datacontract.org/2004/07/MongoDB.Bson' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
That is all good and well but how do I do that?
Either a) don't serialize your document classes over Web API, and create some DTOs meant to be serialized, or b) use something else as ID.
If you want an easy auto-generated ID, and you're OK with the fact that it will consume slightly more space, you can resort to the following "hack":
public class Document
{
public Document()
{
Id = ObjectId.GenerateNewId().ToString();
}
public string Id { get; set; }
}
This way, you'll get MongoIDs, but they'll be stored as a string.
If you need Web API2 responce in XML format , you need to handle the default Id like below
eg: ObjectId("507f191e810c19729de860ea")
Either you need to remove the Id from serialization.
[DataContract]
public class Document
{
[BsonId]
public string Id { get; set; }
[DataMember]
public string Title { get; set; } //other properties you use
}
Or You can change the Type of ID with custom logic
public class GuidIdGenerator : IIdGenerator
{
public object GenerateId(object container, object document)
{
return Guid.NewGuid();
}
public bool IsEmpty(object id)
{
return string.IsNullOrEmpty(id.ToString());
}
}
public class Document
{
[BsonId(IdGenerator = typeof(GuidIdGenerator))]
public string Id { get; set; }
public string Title { get; set; } //other properties you use
}

Resources