Posting XML to WebAPI - parameter is aways null - asp.net-web-api

I'm trying to post XML to an action method. The parameter is always null. Does anyone know what I'm doing wrong?
The controller class:
namespace TestNetCore.Controllers
{
[Route("api/[controller]")]
public class BookController : Controller
{
[HttpPost]
public Book AddBook([FromBody]Book b) <-- b is always null
{
return new Book
{
Id = b.Id,
Title = b.Title
};
}
}
}
Startup.cs:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddXmlDataContractSerializerFormatters()
.AddMvcOptions(opts =>
{
opts.RespectBrowserAcceptHeader = true;
opts.ReturnHttpNotAcceptable = true;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseStaticFiles();
app.UseStatusCodePages();
app.UseDeveloperExceptionPage();
app.UseMvcWithDefaultRoute();
}
}
In project.json I have added this:
"Microsoft.AspNetCore.Mvc.Formatters.Xml": "1.0.1"
The model class:
namespace TestNetCore.Models
{
[DataContract]
public class Book
{
[DataMember(Order = 1)]
public string Id { get; set; }
[DataMember(Order = 2)]
public string Title { get; set; }
}
}
The script that sends XML:
$(document).ready(function () {
$("form").submit(function (e) {
e.preventDefault();
$.ajax({
url: "api/book",
dataType: "xml",
contentType: "application/xml; charset=utf-8",
method: "POST",
data: { b: "<Book xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.datacontract.org/2004/07/TestNetCore.Models\"><Id>1</Id><Title>Programming Book</Title></Book>" },
success: function (book) {
addBook(book);
},
error: function (jqXHR) {
alert(jqXHR.responseText);
}
})
});
});
function addBook(book) {
$("table tbody").append("<tr><td>" + book.id + "</td><td>" + book.title + "</td></tr>");
}

Your javascript is the problem, you are sending an object with a property named b that contains a string (the xml content) and this doesn't match the action contract with expects and object Book and it's properties:
data: { b: "<Book xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.datacontract.org/2004/07/TestNetCore.Models\"><Id>1</Id><Title>Programming Book</Title></Book>" },

Related

Using .NET Core MVC response displays NULL value of one of property

Using .Net Core 3.1 MVC pattern, I have been developing a back-end system. There are two tables, one is "TranslationInfo", another one is "TranslationContent". TranslationInfo has many TranslationContent, so I configured the Dbcontext like this.
TranslationInfo
public class TranslationInfo
{
[Key]
public int Index { get; set; }
public DateTime InsertedDate { get; set; }
public DateTime UpdatedDate { get; set; }
}
TranslationContent
public class TranslationContent
{
[Key]
public int Code { get; set; }
public string English { get; set; }
public string French { get; set; }
public string Status { get; set; }
public string Message { get; set; }
[ForeignKey("Index")]
public TranslationInfo TranslationInfo { get; set; }
}
Everything is alright except for getting a list of TranslationContent based on TranslationInfo.Index or not. The result displays the list of TranslationContent w/o TranslationInfo even though it enables to retrieve the result based on TranslationInfo.Index.
Result of 'https://localhost:44311/contentsList/3'
[
{
"code": 1,
"english": "string",
"french": "string",
"status": "",
"message": "",
"translationInfo": null
},
{
"code": 2,
"english": "string",
"french": "string",
"status": "string",
"message": "string",
"translationInfo": null
},
{
"code": 4,
"english": "cvzxvc",
"french": "asdfasdfas",
"status": "Passed",
"message": null,
"translationInfo": null
},
{
"code": 10,
"english": "string",
"french": "string",
"status": "string",
"message": "string",
"translationInfo": null
}
]
Here is Application area.
ITranslationService.cs
public interface ITranslationServices
{
List<TranslationInfo> GetTranslationInfos();
TranslationInfo GetTranslationInfo(int index);
TranslationInfo CreateTranslationInfo(TranslationInfo translationInfo);
TranslationInfo EditTranslationInfo(TranslationInfo translationInfo);
List<TranslationContent> GetTranslationContents();
List<TranslationContent> GetTranslationContentsByIndex(int index);
TranslationContent GetTranslationContent(int code);
TranslationContent CreateTranslationContent(TranslationContent translationContent);
TranslationContent EditTranslationContent(TranslationContent translationContent);
}
TranslationService.cs
using Excel.DB;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Excel.Core
{
public class TranslationService : ITranslationServices
{
private readonly AppDbContext _context;
public TranslationService(AppDbContext context)
{
_context = context;
}
/* Translation Info Area */
public List<TranslationInfo> GetTranslationInfos()
{
return _context.TranslationInfos.ToList();
}
public TranslationInfo GetTranslationInfo(int index)
{
return _context.TranslationInfos.First(t => t.Index == index);
}
public TranslationInfo CreateTranslationInfo(TranslationInfo translationInfo)
{
translationInfo.InsertedDate = DateTime.UtcNow;
translationInfo.UpdatedDate = DateTime.UtcNow;
_context.Add(translationInfo);
_context.SaveChanges();
return translationInfo;
}
public TranslationInfo EditTranslationInfo(TranslationInfo translationInfo)
{
var dbTranslationInfo = _context.TranslationInfos.First(t => t.Index == translationInfo.Index);
// update process will be added.
dbTranslationInfo.UpdatedDate = DateTime.UtcNow;
_context.SaveChanges();
return dbTranslationInfo;
}
/* Translation Content Area */
public List<TranslationContent> GetTranslationContents()
{
return _context.TranslationContents.ToList();
}
public List<TranslationContent> GetTranslationContentsByIndex(int index)
{
return _context.TranslationContents.Where(t => t.TranslationInfo.Index == index).ToList();
}
public TranslationContent GetTranslationContent(int code)
{
return (TranslationContent)_context.TranslationContents.First(t => t.Code == code);
}
public TranslationContent CreateTranslationContent(TranslationContent translationContent)
{
var dbTranslationInfo = _context.TranslationInfos.First(t => t.Index == translationContent.TranslationInfo.Index);
if (dbTranslationInfo == null)
{
throw new Exception("Cannot find translation info");
}
translationContent.TranslationInfo = dbTranslationInfo;
_context.Add(translationContent);
_context.SaveChanges();
return (TranslationContent)translationContent;
}
public TranslationContent EditTranslationContent(TranslationContent translationContent)
{
var dbTranslationContent = _context.TranslationContents.First(t => t.Code == translationContent.Code);
// update process will be added.
dbTranslationContent.English = translationContent.English;
dbTranslationContent.French = translationContent.French;
dbTranslationContent.Message = translationContent.Message;
dbTranslationContent.Status = translationContent.Status;
dbTranslationContent.TranslationInfo = translationContent.TranslationInfo;
_context.SaveChanges();
return dbTranslationContent;
}
}
}
Here is API(Controller) area
TranslationController
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Excel.Core;
using Excel.DB;
namespace Excel.API.Controllers
{
[ApiController]
[Route("[controller]")]
public class TranslationController : ControllerBase
{
private readonly ITranslationServices _translationServices;
public TranslationController(ITranslationServices translationServices)
{
_translationServices = translationServices;
}
[HttpGet("/infos")]
public IActionResult GetTranslationInfos()
{
return Ok(_translationServices.GetTranslationInfos());
}
[HttpGet("/info/{index}", Name = "GetTranslationInfo")]
public IActionResult GetTranslationInfo(int index)
{
return Ok(_translationServices.GetTranslationInfo(index));
}
[HttpPost("/info")]
public IActionResult CreateTranslationInfo(TranslationInfo translationInfo)
{
var newTranslationInfo = _translationServices.CreateTranslationInfo(translationInfo);
return CreatedAtRoute("GetTranslationInfo", new { newTranslationInfo.Index }, newTranslationInfo);
}
[HttpPut("/info")]
public IActionResult EditTranslationInfo(TranslationInfo translationInfo)
{
return Ok(_translationServices.EditTranslationInfo(translationInfo));
}
[HttpGet("/contents",Name = "GetTranslationContents")]
public IActionResult GetTranslationContents()
{
return Ok(_translationServices.GetTranslationContents());
}
[HttpGet("/contentsList/{index}", Name = "GetTranslationContentsByIndex")]
public IActionResult GetTranslationContentsByIndex(int index)
{
return Ok(_translationServices.GetTranslationContentsByIndex(index));
}
[HttpGet("/content/{code}", Name = "GetTranslationContent")]
public IActionResult GetTranslationContent(int code)
{
return Ok(_translationServices.GetTranslationContent(code));
}
[HttpPost("/content")]
public IActionResult CreateTranslationContent(TranslationContent translationContent)
{
var newTranslationContent = _translationServices.CreateTranslationContent(translationContent);
return CreatedAtRoute("GetTranslationContent", new { newTranslationContent.Code }, newTranslationContent);
}
[HttpPut("/content")]
public IActionResult EditTranslationContent(TranslationContent translationContent)
{
return Ok(_translationServices.EditTranslationContent(translationContent));
}
}
}
Can you let me know how I can get the TranslationInfo when getting the list of TranslationContent?
Oh, that seems to be an easy one:
You have to have the TranslationInfo included in your request. The TranslationContext is the table you are asking for, so EFCore, which you are using, is cutting all other tables out.
Change this:
public List<TranslationContent> GetTranslationContentsByIndex(int index)
{
return _context.TranslationContents.Where(t => t.TranslationInfo.Index == index).ToList();
}
to this:
public List<TranslationContent> GetTranslationContentsByIndex(int index)
{
return _context.TranslationContents.Where(t => t.TranslationInfo.Index == index).Include(x => x.TranslationInfo).ToList();
}
Or you could do it the other way around:
public List<TranslationInfo> GetTranslationContentsByIndex(int index)
{
return _context.TranslationInfo.Where(t => t.Index == index).Include(x => x.TranslationContent).ToList();
}

Http headers passed from Angular 8 are not appearing in HttpContext in ASP.NET Core API 3.1

I am passing some header values to my API call in an Angular application:
httpOptions = {
headers: new HttpHeaders({
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"xibm-clientid": "Test"
})
};
submitSomething(myData: IMyData): Observable<any> {
return this.httpClient
.post<any>(apiURL, JSON.stringify(myData), this.httpOptions)
.pipe(catchError(this.errorHandler));
}
The request is getting redirected to my backend API (ASP.NET 3.1 Core API) and I am checking the request header as follows:
string apicClientId = context.Request.Headers["xibm-clientid"].ToString();
The header "xibm-clientid" is not present in context.Request.Headers list.
Note:
I am checking this header in a custom middleware rather than in Controller level:
public class CheckHeadersMiddleware
{
private readonly RequestDelegate _next;
public CheckHeadersMiddleware(RequestDelegate next)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
}
public async Task Invoke(HttpContext context)
{
string apicClientId = context.Request.Headers["xibm-clientid"].ToString();
}
}
I can read this header when it hits the controller but I don't want to do that because I want to check the header before the controller is called. So why the header is not appearing in the middleware level?
Is something I am missing here?
You can try to use Request.Headers["xibm-clientid"].ToString();.Here is a demo:
angular:
httpOptions = {
headers: new HttpHeaders({
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"xibm-clientid": "Test"
})
};
return this.httpClient
.post<any>("https://localhost:xxx/Api", 1, this.httpOptions);
}
Api Controller:
[ApiController]
[Route("[controller]")]
public class ApiController : ControllerBase
{
public IActionResult Index()
{
var s = Request.Headers["xibm-clientid"].ToString();
return Ok();
}
}
result:
Update:
Where do you use the middleware?Here is a demo with middleware,and I can get the header(I check the method is post and I check the RequestPath):
middleware:
public class CheckHeadersMiddleware
{
private readonly RequestDelegate _next;
public CheckHeadersMiddleware(RequestDelegate next)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Path == "/Api" && context.Request.Method == "POST")
{
string apicClientId = context.Request.Headers["xibm-clientid"].ToString();
}
await _next(context);
}
}
result:
angular send two request,one of the method is option,another is post,only the post request have the header xibm-clientid

aspnet web api how to validate using modelstate in multipart form data?

I am new for using asp net web api.
I want to create UploadDocument feature in my web api.
so, I create this way.
From Client
api.post('vendor/UploadDocument', formData, { headers: { 'Content-Type': 'multipart/form-data' } })
Controller
public class VendorController : ApiController {
[HttpPost]
public HttpResponseMessage UploadDocument()
{
try
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var request = HttpContext.Current.Request;
var model = new UploadDocumentViewModel();
model.field1 = request.Form["field1"];
model.field2 = request.Form["field2"];
model.Document = request.Files["Document"];
if (ModelState.IsValid)
{
return Request.CreateResponse(HttpStatusCode.OK);
}
else //ModelState is not valid
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
catch (Exception exception)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
}
}
Model
public class UploadDocumentViewModel
{
[Required]
public string field1 { get; set; }
[Required]
public int field2 { get; set; }
[Required]
public HttpPostedFile Document { get; set; }
}
My problem is, in controller ModelState always empty.
I have tried to add code
Validate(model);
if (ModelState.IsValid)...
but it didn't work too.
can someone have any idea for validating model data annotation in multipart form data ?
try clear model state before validate
ModelState.Clear();
this.Validate(model);
if (ModelState.IsValid) {
}
Check this answer : Custom DataAnnotation
Your method should look like this:
public class VendorController : ApiController {
[HttpPost]
public IHttpActionResult UploadDocument(UploadDocumentViewModel viewModel)
{
try
{
if (!Request.Content.IsMimeMultipartContent())
return StatusCode(HttpStatusCode.UnsupportedMediaType);
if (viewNodel == null)
return BadRequest("Model is empty");
var field1 = viewModel.field1;
var field2 = viewModel.field2;
var documents = viewModel.document;
if (ModelState.IsValid)
{
return Ok();
}
else
{
return BadRequest(ModelState);
}
}
catch (Exception exception)
{
return InternalServerError(exception);
}
}
}
I prefer to passing some of those validations in action filters, to make your methods cleaner (try/catch, modelstate).
If you will have some problems with model binding, you can implement your custom model binder.

Undefined - ajax method error

I looked most of questions like this and i tried a lot of methods to solve the problem but i couldn't.
var kullaniciAdi = '<%= Session["kullanici"] %>';
alert(kullaniciAdi);
$.ajax({
url: "http://localhost:63000/GetInfoService.asmx/GetInfos",
type: "POST",
dataType:'json',
data: JSON.stringify({ kullaniciAdi: kullaniciAdi }),
contentType: "application/json; charset=windows-1254",
success: function (infos) {
var items = infos.d;
$jQuery.each(items, function (index, val) {
$("div#adSoyad").html('<h1>' + val[index].ad + " " + val[index].soyad + '</h1>');
$("div#fotograf").html('<img src="' + val[index].fotograf + '" class="img-responsive">');
});
},
error: function (e) {
alert(e.leader);
}
});
it's not working no matter what i did. i tried to turn index to 0 or remove them nothing worked.
here is my service that i have been calling:
public class GetInfoService : System.Web.Services.WebService
{
[WebMethod]
public List<kullaniciBilgileri> GetInfos(string kullaniciAd)
{
List<kullaniciBilgileri> infos = new List<kullaniciBilgileri>();
try
{
SqlConnection conn = new SqlConnection("Data Source=.\\SQLExpress; Initial Catalog=twitter;Integrated Security=SSPI");
if (conn.State == ConnectionState.Closed)
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT ad,soyad,fotograf FROM tblKullanici WHERE kullaniciAdi = #kullaniciAd");
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
cmd.Parameters.AddWithValue("#kullaniciAd", kullaniciAd);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
var kullanici = new kullaniciBilgileri
{
ad = dr["ad"].ToString(),
soyad = dr["soyad"].ToString(),
fotograf = dr["fotograf"].ToString()
};
infos.Add(kullanici);
}
return infos;
}
catch (SoapException se)
{
throw se;
}
}
public class kullaniciBilgileri
{
public string ad { get; set; }
public string soyad { get; set; }
public string fotograf { get; set; }
}
}
and for more information this method my login and home page. maybe there are mistakes that i've made.
public partial class Login : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
[WebMethod]
public static string GetSessionValue(string kullaniciAd)
{
HttpContext.Current.Session["kullanici"] = kullaniciAd;
return kullaniciAd;
}
}
public partial class Home : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (HttpContext.Current.Session["kullanici"] == null)
{
Response.Redirect("Login.aspx");
}
}
}
sorry for my English :)

Pass data serialized with Json.net library to the View and bind it with knockout

I am trying to send some serialized data to the view and bind it to the knockout code. I am using json.net library for serialization because I want to pass the constants of an enum to the view ( and not the underlying integers.) I am not sure how my controller returning Json data should look like. Here is the sample code:
My view model that will be serialized:
public class FranchiseInfoViewModel
{
public string FolderName { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public LobbyTemplateOptions LobbyTemplate { get; set; }
public List<LobbyTemplateOptions> LobbyTemplates { get; set; }
public void Initialize()
{
FolderName = "Test";
LobbyTemplate = LobbyTemplateOptions.G;
LobbyTemplates = new List<LobbyTemplateOptions>
{
LobbyTemplateOptions.G,
LobbyTemplateOptions.H,
LobbyTemplateOptions.I
};
Enum:
[JsonConverter(typeof(StringEnumConverter))]
public enum LobbyTemplateOptions
{
G = 7,
H = 8,
I = 9
}
My knockout code:
$(function () {
omega.FranchiseInfo = (function () {
var FolderName = ko.observable();
var LobbyTemplates = ko.observableArray([]);
$.getJSON("FranchiseData", function (data) {
FolderName(data.FolderName);
for (var i = 0; i < data.LobbyTemplate.length; i++) {
LobbyTemplates.push(data.LobbyTemplate[i]);
}
});
return {
folderName: FolderName,
lobbyTemplates: LobbyTemplates
}
} ());
ko.applyBindings(omega.FranchiseInfo);
})
}
I am wondering how my controller that passes serialized Json data to the view should look like as I have not used json.net and I am relatively new to programming:
Controller passing the Json data to the view:
public JsonResult FranchiseData()
{
FranchiseInfoViewModel franchiseInfoViewModel = new FranchiseInfoViewModel();
franchiseInfoViewModel.MapFranchiseInfoToFranchiseInfoViewModel();
string json = JsonConvert.SerializeObject(franchiseInfoViewModel);
// this is how I do it with the default Json serializer
// return Json(franchiseInfoViewModel, JsonRequestBehavior.AllowGet);
}
I would be very gratefull if somebody can post a working example of my controller. Thank You!
You need to implement JsonNetResult.
public class JsonNetResult : ActionResult
{
public Encoding ContentEncoding { get; set; }
public string ContentType { get; set; }
public object Data { get; set; }
public JsonSerializerSettings SerializerSettings { get; set; }
public Formatting Formatting { get; set; }
public JsonNetResult()
{
SerializerSettings = new JsonSerializerSettings();
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(ContentType)
? ContentType
: "application/json";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
if (Data != null)
{
JsonTextWriter writer = new JsonTextWriter(response.Output) { Formatting = Formatting };
JsonSerializer serializer = JsonSerializer.Create(SerializerSettings);
serializer.Serialize(writer, Data);
writer.Flush();
}
}
}
To use it, in your case you need to rewrite controller method in this way:
public ActionResult FranchiseData()
{
FranchiseInfoViewModel franchiseInfoViewModel = new FranchiseInfoViewModel();
franchiseInfoViewModel.MapFranchiseInfoToFranchiseInfoViewModel();
JsonNetResult jsonNetResult = new JsonNetResult();
jsonNetResult.Formatting = Formatting.Indented;
jsonNetResult.Data = franchiseInfoViewModel;
return jsonNetResult;
}
(implementation of JsonNetResult above was taken this blog post
http://james.newtonking.com/archive/2008/10/16/asp-net-mvc-and-json-net.aspx )

Resources