I am trying to receive the below key value pair as the input parameter to my Web API
json=%7B%0A%22MouseSampleBarcode%22%20%3A%20%22MOS81%22%0A%7D%0A
where the right of the string is the URL encoded JSON which looks like
{
"MouseSampleBarcode" : "MOS81"
}
How can I parse this and store them in to the Model class
[HttpPost]
public async Task<IHttpActionResult> Get([FromBody] CoreBarCodeDTO.RootObject coreBarCode)
{
string Bar_Code = coreBarCode.MouseSampleBarcode.ToString();
where the CoreBarCodeDTO looks like below
public class CoreBarCodeDTO
{
public class RootObject
{
public string MouseSampleBarcode { get; set; }
}
}
You could do it this way. Change your class to this definition. In your controller coreBarCode.json will have the the json which you can then work with as needed:
public class CoreBarCodeDTO
{
private string _json;
public string json { get { return _json; }
set {
string decoded = HttpUtility.UrlDecode(value);
_json = decoded;
}
}
}
Update
[HttpPost]
public async Task<IHttpActionResult> Get([FromBody] CoreBarCodeDTOcoreBarCode coreBarCode)
{
string Bar_Code = coreBarCode.json;
//work with the JSON here, with Newtonsoft for example
var obj = JObject.Parse(Bar_Code);
// obj["MouseSampleBarcode"] now = "MOS81"
}
As #Lokki mentioned in his comment. The GET verb does not have a body, you need to change that to POST or PUT (depending if you are creating/searching or updating), so your code would look like this:
[HttpPost("/")]
public async Task<IHttpActionResult> Get([FromBody] CoreBarCodeDTO.RootObject coreBarCode)
{
string Bar_Code = coreBarCode.MouseSampleBarcode.ToString();
So, as I said: Get doesn't have body.
Follow #KinSlayerUY answer.
[HttpPost("/")]
public async Task<IHttpActionResult> Post([FromBody] CoreBarCodeDTO.RootObject coreBarCode)
{
string Bar_Code = coreBarCode.MouseSampleBarcode.ToString();
...
}
If you need use GET remove [FromBody] attribute and send data as single parameters
[HttpGet("/")]
public async Task<IHttpActionResult> Get(string mouseSampleBarcode)
{
var rootObject = new CoreBarCodeDTO.RootObject
{
MouseSampleBarcode = mouseSampleBarcode
}
...
}
Related
I want to display a response in Swagger UI in XML format instead of JSON. How I can achieve this? This is the code I want to adapt:
[SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(FeedModel))]
public async Task<IActionResult> GetCompanyPostFeed(Guid companyId)
{
var feed = new FeedModel();
// Some database requests
return Content(feed, "text/xml", Encoding.UTF8);
}
You could try decorating the method with an attribute SwaggerProducesAttribute as described here:
[SwaggerProduces("text/xml")]
[SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(FeedModel))]
public async Task<IActionResult> GetCompanyPostFeed(Guid companyId)
In keeping with the dislike of link-only answers, I'll reproduce some of the relevant bits of that article here:
[AttributeUsage(AttributeTargets.Method)]
public class SwaggerProducesAttribute : Attribute
{
public SwaggerProducesAttribute(params string[] contentTypes)
{
this.ContentTypes = contentTypes;
}
public IEnumerable<string> ContentTypes { get; }
}
public class ProducesOperationFilter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var attribute = apiDescription.GetControllerAndActionAttributes<SwaggerProducesAttribute>().SingleOrDefault();
if (attribute == null)
{
return;
}
operation.produces.Clear();
operation.produces = attribute.ContentTypes.ToList();
}
}
Then in SwaggerConfig.cs, you'll need something like:
config
.EnableSwagger(c =>
{
...
c.OperationFilter<ProducesOperationFilter>();
...
}
I have a asp.net core (v2.1) webapi project which expose this function:
[HttpPost]
[Route("v1/do-something")]
public async Task<IActionResult> PostDoSomething(ModelData model)
{
//...
}
and this model:
public class ModelData
{
[Required]
public string Email { get; set; }
}
I want to make this endpoint flexible, in content type perspective. Therefore, it should be OK to send this endpoint diffrent content type in body.
For example, those "BODY" argument will be allowed:
// application/x-www-form-urlencoded
email="abc123#gmail.com"
// application/json
{
"email": "abc123#gmail.com"
}
In contrast to the old .net framework, in dotnet core this is not allowed out of the box. I found that I need to add Consume attribute with [FormForm] attribute. But, If I add the [FormForm] attribute the the model argument, it wont work anymore on JSON (for example) - because then it should be [FromBody].
I though that it will be ok to use code like this:
[HttpPost]
[Route("v1/do-something")]
public async Task<IActionResult> PostDoSomething([FromBody] [FromForm] ModelData model)
{
//...
}
But as you can expect, this code not working.
So, in order to achieve this flexibility I have to duplicate all my endpoint - which sound like a very very bad idea.
[HttpPost]
[Route("v1/do-something")]
[Consume ("application/json")]
public async Task<IActionResult> PostDoSomething([FromBody] ModelData model)
{
//...
}
[HttpPost]
[Route("v1/do-something")]
[Consume ("application/x-www-form-urlencoded")]
public async Task<IActionResult> PostDoSomething([FromForm] ModelData model)
{
//...
}
// ... Other content types here ...
It's sound an easy task. But seems like more complicated.
I missed something? How to make an endpoint work in any content type?
Here is a custom model binder that binds based on the content type.
public class BodyOrForm : IModelBinder
{
private readonly IModelBinderFactory factory;
public BodyOrForm(IModelBinderFactory factory) => this.factory = factory;
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var contentType =
bindingContext.ActionContext.HttpContext.Request.ContentType;
BindingInfo bindingInfo = new BindingInfo();
if (contentType == "application/json")
{
bindingInfo.BindingSource = BindingSource.Body;
}
else if (contentType == "application/x-www-form-urlencoded")
{
bindingInfo.BindingSource = BindingSource.Form;
}
else
{
bindingContext.Result = ModelBindingResult.Failed();
}
var binder = factory.CreateBinder(new ModelBinderFactoryContext
{
Metadata = bindingContext.ModelMetadata,
BindingInfo = bindingInfo,
});
await binder.BindModelAsync(bindingContext);
}
}
Tested with the following action.
[HttpPost]
[Route("api/body-or-form")]
public IActionResult PostDoSomething([ModelBinder(typeof(BodyOrForm))] ModelData model)
{
return new OkObjectResult(model);
}
Here is a demo is on GitHub.
I have an MVC application with two micro services. In the first I call PostAsync() method to send my custom object
public class Logo {
public Guid Id { get; set; }
public IEnumerable<byte> Content { get; set; }
}
to another service
public async Task PostLogo(Logo logo)
{
using (var client = new HttpClient())
{
await client.PostAsync(_url, new StringContent(JsonConvert.SerializeObject(logo), Encoding.UTF8, "application/json"));
}
}
In the second micro service I try to deserialize using
[HttpPost, Route("logo")]
public Task<FileUploadResultModel> SaveAsync([FromBody]Logo model)
{
return _fileService.SaveAsync(null);
}
but it gets null instead of input object
.
Can anyone explain how can I send/process custom object using Post request, please?
Update action to follow preferred syntax structure.
[HttpPost, Route("logo")]
public async Task<IHttpActionResult> SaveAsync([FromBody]Logo model) {
if(ModelState.IsValid) {
FileUploadResultModel result = await _fileService.SaveAsync(model);
return Ok(result);
}
return BadRequest();
}
Based on comments it is possible that the byte array is too large so the model binder is unable to properly bind the model. Check the web.config and see what is the max size of data that can be sent. You may need to increase it. The max is 2GB but I would advise against such a high value.
Check this answer: Maximum request length exceeded
If you are able to get the image smaller you should base64 Url encode that image to a string and sent that as the content.
public class Logo {
public Guid Id { get; set; }
public string Content { get; set; }
}
And when you get the model convert the content back to a byte array
[HttpPost, Route("logo")]
public async Task<IHttpActionResult> SaveAsync([FromBody]Logo model) {
if(ModelState.IsValid) {
byte[] content = Convert.FromBase64String(model.Content);
var id = model.Id;
//...
return Ok(result);
}
return BadRequest();
}
I am using the .NET pattern where my controller action has a signature as such
public async Task<IHttpActionResult> GetTimeSeries(string deviceId, string tag) { ... }
and inside the controller I want to send a custom error message adhering to the JSON API Spec
My goal is when encountering an error to use
return BadRequest(someJSONErrorObject);
or
return NotFound(someJSONErrorObject);
rather than throw an exception.
Currently if I do
return BadRequest(JsonConvert.SerializeObject(someJSONErrorObject));
the JSON I get back looks like
{
"Message": "{\"data\":null,\"errors\":[{\"detail\":\"The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.\"}],\"meta\":null}"
}
Create a custom IHttpActionResult
public class CustomResponseResult<T> : IHttpActionResult {
public CustomResponseResult(HttpStatusCode statusCode, T content, HttpRequestMessage request) {
Content = content;
Request = request;
StatusCode = statusCode;
}
public T Content { get; private set; }
public HttpRequestMessage Request { get; private set; }
public HttpStatusCode StatusCode { get; private set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) {
return Task.FromResult(Execute());
}
private HttpResponseMessage Execute() {
var response = Request.CreateResponse(StatusCode, Content);
response.RequestMessage = Request;
response.ReasonPhrase = StatusCode.ToString();
return response;
}
}
and then in order to maintain usability create extension methods
public static class ApiControllerExtensions {
public static IHttpActionResult BadRequest<T>(this ApiController controller, T content) {
return controller.CustomResponse(HttpStatusCode.BadRequest, content);
}
public static IHttpActionResult NotFound<T>(this ApiController controller, T content) {
return controller.CustomResponse(HttpStatusCode.NotFound, content);
}
public static IHttpActionResult CustomResponse<T>(this ApiController controller, HttpStatusCode statusCode, T content) {
var request = controller.Request;
var result = new CustomResponseResult<T>(statusCode, content, request);
return result;
}
}
that allowed the calls wanted
return this.BadRequest(someJSONErrorObject);
or
return this.NotFound(someJSONErrorObject);
Create custom error class where you can set the http status and error message. Create a custom exception class to throw your custome error messag. Create a Global Exception filter which will send you custom exception in json format.
I have a web api, where the global configuration is configured to use:
XmlMediaTypeFormatter
My problem is I wont to extend this web api with a new controller, that uses the JsonMediaTypeFormatter instead.
Is it possible to change the MediaTypeFormatter to JSON for only one API Controller class?
My problem is not returning JSON, I have accumplished this with returning HttpResponseMessage:
return new HttpResponseMessage
{
Content = new ObjectContent<string>("Hello world", new JsonMediaTypeFormatter()),
StatusCode = HttpStatusCode.OK
};
It's on the request I get the problem. If I have an object with two properties:
public class VMRegistrant
{
public int MerchantId { get; set; }
public string Email { get; set; }
}
And my controller action takes the VMRegistrant as argument:
public HttpResponseMessage CreateRegistrant(VMRegistrant registrant)
{
// Save registrant in db...
}
But the problem is when I call the action with JSON it fails.
You can have your controller return an IHttpActionResult and use the extension method HttpRequestMessageExtensions.CreateResponse<T> and specify the formatter you want to use:
public IHttpActionResult Foo()
{
var bar = new Bar { Message = "Hello" };
return Request.CreateResponse(HttpStatusCode.OK, bar, new MediaTypeHeaderValue("application/json"));
}
Another possibility is to use the ApiController.Content method:
public IHttpActionResult Foo()
{
var bar = new Bar { Message = "Hello" };
return Content(HttpStatusCode.OK, bar, new JsonMediaTypeFormatter(), new MediaTypeHeaderValue("application/json"));
}
Edit:
One possibility is to read and deserialize the content yourself from the Request object via reading from the stream and using a JSON parser such as Json.NET to create the object from JSON:
public async Task<IHttpActionResult> FooAsync()
{
var json = await Request.Content.ReadAsStringAsync();
var content = JsonConvert.DeserializeObject<VMRegistrant>(json);
}
Yes, it's possible to change the MediaTypeFormatters for only one class/controller. If you want to save and restore the default formatters you can follow these steps:
In the beginning of the request save old formatters
Clear the formatters collection
Add the desired formatter
At the end of the request copy back the old formatters
I think this is easily done by an ActionFilterAttribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class ChangeFormatterAttribute : ActionFilterAttribute
{
private IEnumerable<MediaTypeFormatter> oldFormatters;
private MediaTypeFormatter desiredFormatter;
public ChangeFormatterAttribute(Type formatterType)
{
this.desiredFormatter = Activator.CreateInstance(formatterType) as MediaTypeFormatter;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
var formatters = actionContext.ControllerContext.Configuration.Formatters;
oldFormatters = formatters.ToList();
formatters.Clear();
formatters.Add(desiredFormatter);
base.OnActionExecuting(actionContext);
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var formatters = actionExecutedContext.ActionContext.ControllerContext.Configuration.Formatters;
formatters.Clear();
formatters.AddRange(oldFormatters);
base.OnActionExecuted(actionExecutedContext);
}
}
And the usage:
[ChangeFormatterAttribute(typeof(JsonMediaTypeFormatter))]
public class HomeController : ApiController
{
public string Get()
{
return "ok";
}
}
// ...
[ChangeFormatterAttribute(typeof(XmlMediaTypeFormatter))]
public class ValuesController : ApiController
{
public string Get()
{
return "ok";
}
}
Maybe you could have your media type formatter only accept the type that is handled by your controller:
public class Dog
{
public string Name { get; set; }
}
public class DogMediaTypeFormatter : JsonMediaTypeFormatter
{
public override bool CanReadType(Type type)
{
return type == typeof (Dog);
}
}
Probably not the best solution though :I