Serialization empty - restsharp

I try to serialize a custom object, but it ends up being empty.
The result I want to have is :
{
"accessRules": [
{
"method": "GET",
"path": "/*"
}
],
"redirection":"https://www.mywebsite.com/"
}'
Here are my classes:
[Serializable]
public class AccessRule
{
[SerializeAs(Name = "method")]
string Method { get; set; }
[SerializeAs(Name = "path")]
string Path { get; set; }
public AccessRule(string method, string path)
{
this.Method = method;
this.Path = path;
}
}
[Serializable]
public class RequestBody
{
[SerializeAs(Name = "accessRules")]
AccessRule[] AccessRules
{
get { return arList.ToArray(); }
}
private List<AccessRule> arList;
[SerializeAs(Name = "redirection")]
string Redirection { get; set; }
public RequestBody(string method, string path, string redirection)
{
this.arList = new List<AccessRule>();
this.arList.Add(new AccessRule(method, path));
this.Redirection = redirection;
}
}
var RequestBody = new RequestBody("GET", "/*", "https://www.mywebsite.com");
And it leads to an empty json string.
If i do :
var RequestBody = new { accessRules = new[] { new { method = "GET", path = "/*" } }, redirection = "https://www.mywebsite.com" };
It works.
I use RestSharp 104.4.0.0, also tried with Newtonsoft.Json v6.0.0.0
What am I missing here ?
Thanks.

Related

.net core 2.2 webapi and handling multypart request

I have .net core 2.2 webapi as backend.
My backend have to handling the requests (photo and text) from mobile application as multypart-form request.
Plz hint me how it to prpvide on backend?
this problem has solved
public class SwaggerFileOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
if (operation.OperationId == "MailWithPhotos")
{
operation.Parameters = new List<IParameter>
{
new NonBodyParameter
{
Name = "awr_file",
Required = false,
Type = "file",
In = "formData"
},
new NonBodyParameter
{
Name = "awr_message",
Required = true,
Type = "string",
In = "formData"
},
new NonBodyParameter
{
Type = "string",
In = "header",
Name = "Authorization",
Description = "token",
Required = true
}
};
}
}
}
[HttpPost]
[ProducesResponseType(typeof(ResponseMail), 200)]
public async Task<PipeResponse> MailWithPhotos([FromForm] MailwithPhoto fIleUploadAPI)
{
var file = fIleUploadAPI.awr_file; // OK!!!
var message = fIleUploadAPI.awr_message; // OK!!!
var tokenA = Request.Headers["Authorization"]; // OK!!!
var fileContentStream11 = new MemoryStream();
await fIleUploadAPI.awr_file.CopyToAsync(fileContentStream11);
await System.IO.File.WriteAllBytesAsync(Path.Combine(folderPath,
fIleUploadAPI.awr_file.FileName),
fileContentStream11.ToArray());
}
public class MailwithPhoto
{
public string awr_message { get; set; }
public string Authorization { get; set; }
public IFormFile awr_file { get; set; }
}

XML Serialization - Required attribute

I have a class that should represent a controller's action parameter and I'd like its properties to be "required" (meaning, you get a status code 400 or something in case it's passed as null). I managed to get it done using System.ComponentModel.DataAnnotations, but the ErrorMessage that I pass to the constructor of the Required attribute is never shown.
[XmlRoot(ElementName = "root")]
public class Request
{
[XmlElement(ElementName = "prop")]
[Required(ErrorMessage = "The property is required.")]
public string Property { get; set; }
[XmlElement(ElementName = "another")]
[Required(ErrorMessage = "The property is required.")]
public string Another { get; set; }
}
Action:
[HttpPost]
public IActionResult Post([FromBody] Request value)
{
return Ok(value); //ignore this, it's just for testing purposes...
}
However, if I don't pass the Property value, I get a 400 that doesn't contain the ErrorMessage I passed earlier. Am I missing something here?
<ValidationProblemDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Title>One or more validation errors occurred.</Title>
<Status>400</Status>
</ValidationProblemDetails>
My Startup has Xml formatters added to it:
services.AddMvc(options =>
{
options.RespectBrowserAcceptHeader = true;
options.InputFormatters.Insert(0, new XmlSerializerInputFormatter(options));
options.OutputFormatters.Insert(0, new XmlSerializerOutputFormatter());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
The body of the request looks like this, and it doesn't have "Property":
<root>
<another>Test</another>
<!-- Property "Property" is missing here -->
</root>
Kudos to Code Rethinked for the huge help - Customizing automatic HTTP 400 error response in ASP.NET Core Web APIs.
An approach that I managed to figure out eventually includes the use of services.Configure in my Startup.ConfigureServices method.
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
return new OkObjectResult(new CustomResponse(someStatusCode, context))
{
ContentTypes = { "application/xml" }
};
};
});
So, I made a class named CustomResponse that holds the status code I want to retrieve and all the validation errors (including the ones where my Required property was not passed to the API).
[XmlRoot(ElementName = "rcemsTrxSubReqAck")]
public class CustomResponse
{
[XmlElement(ElementName = "Status")]
public string Status { get; set; }
[XmlArray(ElementName = "Errors"), XmlArrayItem(ElementName = "Error")]
public string[] Errors { get; set; }
public CustomResponse(int status, ActionContext context)
{
Status = status;
Errors = ConstructErrorMessages(context);
}
private string[] ConstructErrorMessages(ActionContext context)
{
if (context == null)
{
return null;
}
string[] arr = new string[context.ModelState.ErrorCount];
int i = 0;
foreach (var keyModelStatePair in context.ModelState)
{
var key = keyModelStatePair.Key;
var errors = keyModelStatePair.Value.Errors;
if (errors != null && errors.Count > 0)
{
if (errors.Count == 1)
{
var errorMessage = GetErrorMessage(errors[0]);
arr[i] = $"{key}: {errorMessage}";
}
else
{
var errorMessages = new string[errors.Count];
for (var j = 0; j < errors.Count; j++)
{
errorMessages[j] = GetErrorMessage(errors[j]);
}
arr[i] = $"{key}: {errorMessages.ToString()}";
}
i++;
}
}
return arr;
}
private string GetErrorMessage(ModelError error)
{
return string.IsNullOrEmpty(error.ErrorMessage) ? "The input was not valid." : error.ErrorMessage;
}
}

Swashbuckle.AspNetCore 5.0.0-rc4 UploadFileFilter doesn't work

I need to add upload file for Swashbuckle.AspNetCore 5.0.0-rc4. In earlier version it works like:
public class SwaggerUploadFileParametersFilter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters != null)
{
var attribute =
apiDescription.ActionDescriptor.GetCustomAttributes<UploadFileParametersAttribute>()
.FirstOrDefault();
if (attribute != null)
{
operation.consumes.Add("multipart/form-data");
operation.parameters.Add(new Parameter
{
name = "file",
required = true,
type = "file",
#in = "formData"
}
);
}
}
}
}
[UploadFileParameters]
public async Task<IHttpActionResult> MyMethod([FromUri]MyMethodParams parameters)
I try to implement it using Microsoft.OpenApi.Models objects:
public class SwaggerUploadFileParametersFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var actionAttributes = context.MethodInfo.GetCustomAttributes<UploadFileParametersAttribute>().FirstOrDefault();
if (actionAttributes != null)
{
operation.RequestBody = new OpenApiRequestBody()
{
Content =
{
["multipart/form-data"] = new OpenApiMediaType()
{
Schema = new OpenApiSchema()
{
Properties =
{
["file"] = new OpenApiSchema()
{
Description = "Select file",
Type = "file"
}
}
}
}
}
};
}
}
}
But it doesn't work. I don't see file component in swagger
I took your code and some documentation from Swagger File Upload
I modified your code and added small fix
public class SwaggerUploadFileParametersFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var parameters = operation.Parameters;
if (parameters == null || parameters.Count == 0)
{
return;
}
var isUploadFile = context.ApiDescription.ActionDescriptor.Parameters.Any(x => x.ParameterType == typeof(IFormFile));
if (isUploadFile)
{
operation.RequestBody = new OpenApiRequestBody()
{
Content =
{
["multipart/form-data"] = new OpenApiMediaType()
{
Schema = new OpenApiSchema()
{
Type = "object",
Properties =
{
["file"] = new OpenApiSchema()
{
Description = "Select file", Type = "string", Format = "binary"
}
}
}
}
}
};
}
}
}
And controller:
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IActionResult> UploadFileAsync([FromForm] IFormFile file)

Elastisearch.net property opt in?

I'm using Elastisearch.NET with NEST 2.3. I want to use attribute mapping but I only want to index certain properties. As I understand it all properties are indexed unless you ignore them using for example [String(Ignore = true)] Is it possible to ignore all properties by default and only index the ones that have a nest attribute attached to them? Like JSON.NETs MemberSerialization.OptIn
You could do this using a custom serializer to ignore any properties not marked with a NEST ElasticsearchPropertyAttributeBase derived attribute.
void Main()
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(
pool,
new HttpConnection(),
new SerializerFactory(s => new CustomSerializer(s)));
var client = new ElasticClient(connectionSettings);
client.CreateIndex("demo", c => c
.Mappings(m => m
.Map<Document>(mm => mm
.AutoMap()
)
)
);
}
public class Document
{
[String]
public string Field1 { get; set;}
public string Field2 { get; set; }
[Number(NumberType.Integer)]
public int Field3 { get; set; }
public int Field4 { get; set; }
}
public class CustomSerializer : JsonNetSerializer
{
public CustomSerializer(IConnectionSettingsValues settings, Action<JsonSerializerSettings, IConnectionSettingsValues> settingsModifier) : base(settings, settingsModifier) { }
public CustomSerializer(IConnectionSettingsValues settings) : base(settings) { }
public override IPropertyMapping CreatePropertyMapping(MemberInfo memberInfo)
{
// if cached before, return it
IPropertyMapping mapping;
if (Properties.TryGetValue(memberInfo.GetHashCode(), out mapping))
return mapping;
// let the base method handle any types from NEST
// or Elasticsearch.Net
if (memberInfo.DeclaringType.FullName.StartsWith("Nest.") ||
memberInfo.DeclaringType.FullName.StartsWith("Elasticsearch.Net."))
return base.CreatePropertyMapping(memberInfo);
// Determine if the member has an attribute
var attributes = memberInfo.GetCustomAttributes(true);
if (attributes == null || !attributes.Any(a => typeof(ElasticsearchPropertyAttributeBase).IsAssignableFrom(a.GetType())))
{
// set an ignore mapping
mapping = new PropertyMapping { Ignore = true };
Properties.TryAdd(memberInfo.GetHashCode(), mapping);
return mapping;
}
// Let base method handle remaining
return base.CreatePropertyMapping(memberInfo);
}
}
which produces the following request
PUT http://localhost:9200/demo?pretty=true
{
"mappings": {
"document": {
"properties": {
"field1": {
"type": "string"
},
"field3": {
"type": "integer"
}
}
}
}
}

RavenDB static index on dictionary

I have an application that uses documents, that contain list of attributes in a dictionary, for some reason we need to use a static index and query/filter over these attributes.
A prototype looks like this:
class Program
{
static void Main(string[] args)
{
IDocumentStore store = new DocumentStore() { DefaultDatabase = "Test", Url = "http://localhost:8081" };
store.Initialize();
IndexCreation.CreateIndexes(typeof(Program).Assembly, store);
using (var session = store.OpenSession())
{
session.Store(new Document { Id = "1", Name = "doc_name", Attributes = new Dictionary<string, object> { { "Type", "1" }, { "Status", "Active" } } });
session.SaveChanges();
}
using (var session = store.OpenSession())
{
// works
var l1 = session.Query<Document, Documents_Index>().Where(a => a.Attributes["Type"] == "1").ToList();
// not working
var l2 = session.Query<Document, Documents_Index>().Where(a => a.Attributes["Status"] == "Active").ToList();
}
}
}
public class Documents_Index : AbstractIndexCreationTask<Document>
{
public Documents_Index()
{
Map = docs => docs.Select(a =>
new
{
a.Name,
a.Attributes,
Attributes_Type = a.Attributes["Type"]
});
}
}
[Serializable]
public class Document
{
public string Id { get; set; }
public string Name { get; set; }
public Dictionary<string, object> Attributes { get; set; }
}
But since I need to query using any arbitrary Attribute name/value this index does solve our problem. Actually the list of attributes is known at run-time (so we tried modifying the Map expression to inject any number of attribute names, but so far we weren't successful). Is there a way how to define the index in some dynamic fashion?
You need to write it like:
public class Documents_Index : AbstractIndexCreationTask<Document>
{
public Documents_Index()
{
Map = docs => docs.Select(a =>
new
{
a.Name,
_ = a.Attributes.Select(x=>CreateField("Attributes_"+x.Key, x.Value),
});
}
}