I have an asp-hosted webasenbly on blazor using SignalR to send data from the server to the client. Here's the server sending a card to the client:
FounderCard cardToSend = LinkedInCards.GetOne();
int ReaminigCards = LinkedInCards.Count;
cardToSend.IsLeaderOfBand= true;
cardToSend.IsSelected = true;
await Hub.Clients.All.SendAsync(MessageOrders.RevealLinkdinCard.ToString(), cardToSend, ReaminigCards);
here's the debug when sending the object, clearly properly filled in:
Here's the receiving client:
hub.On<FounderCard, int>(MessageOrders.RevealLinkdinCard.ToString(), (card, remainig) => {
Game.RevealLinkedinCard(card, remainig);
});
But the object is anew. The values on the enums are the 0 value..
PS: the method work for other complex structures as player data, all fields arrive properly formatted to the destination ...
Just in case it helps, this is the definition of the FounderCard being sent:
public class FounderCard : Card
{
//base clase Card just implements Serialize and INotifyPropertyChanged
public Sections Section;
public Races Race;
public string Color => FounderCommons.FromSectionToColor(this.Section);
public string ID = String.Empty;
public bool IsLeaderOfBand;
public bool IsSelected;
public void SetID(int counter)
{
this.ID = FounderCommons.GetSectionID(this.Section) + "_" + FounderCommons.GetRaceID(this.Race) + "_" + counter.ToString();
}
}
System.Text.Json (the Json library being used by default in SignalR), doesn't serialize fields by default. You either need to change the fields to properties by adding { get; set; }, or you can set the attribute [JsonInclude] on the fields. More info can be found at https://learn.microsoft.com/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-6-0#include-fields
Related
I am trying to post objects to my server but the received objects have the value null.
Backend Code:
// Signature
public IActionResult Save(string num, string pat, string token, [FromBody]DataCl data, [FromBody]List<items> listItems)
// EDIT: Added class
public class Object
{
public List<items> listItems { get; set; }
public DataCl data { get; set; }
}
// So new signature
public IActionResult Save(string num, string pat, string token, [FromBody]Test test)
// The value is still null
Frontend Code:
post(num, data, list)
return this.http.post<any>(url,{data, list}, httpOptions).subscribe()
So the parameter num, pat and token are receiving the correct data but the data representing the body are not reciving any data - they are null.
With only one object it is working fine - the correct object was received but with two it does not work anymore but why? Is it something in the frontend code? Or backend?
Check the following article here
Don't apply [FromBody] to more than one parameter per action method.
The ASP.NET Core runtime delegates the responsibility of reading the
request stream to the input formatter. Once the request stream is
read, it's no longer available to be read again for binding other
[FromBody] parameters.
You cannot have two FromBody attributes. The from body is only read once.
{data, list} is one object anyway in javascript. There is no way to Post multiple objects in body, unless they are embedded.
{
object1: {}
object2: {}
}
And in you backend code:
class WrapperObjectResponse {
public Object1 = ...
public Object2 = ...
}
In your new signature, try this:
[Route("save/{num}/{pat}/{token}")]
public IActionResult Save(string num, string pat, string token, [FromBody]Test test)
And call like this:
return this.http.post<any>(url + '/' + num + '/' + pat + '/' + token + '/',{data: {}, list = []}, httpOptions).subscribe()
this opportunity Ii'd like to thank everyone who has an answer to this question, I'm trying to get a json from my web api service and I can't this is my code at the web api...
[ResponseType(typeof(List<CompanyType>))]
[Route("GetList")]
[DeflateCompression]
public async Task<IHttpActionResult> Get()
{
List<CompanyType> companyTypes = (List<CompanyType>)MemoryCacheManager.GetValue(#"CompanyTypes");
if (companyTypes != null) return Ok(companyTypes);
companyTypes = await _CompanyType.Queryable().ToListAsync();
if (companyTypes == null) return Ok(HttpStatusCode.NoContent);
MemoryCacheManager.Add(#"CompanyTypes", companyTypes);
return Ok(companyTypes);
}
and at the site of my client I got this
public async Task<T> GetAsync<T>(string action, string authToken = null)
{
using (var client = new HttpClient())
{
if (!authToken.IsNullOrWhiteSpace())
client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse(#"Bearer " + authToken);
var result = await client.GetAsync(BuildActionUri(action));
string json = await result.Content.ReadAsStringAsync();
if (result.IsSuccessStatusCode)
return JsonConvert.DeserializeObject<T>(json); //This line fails because the characters in the value
throw new ApiException(result.StatusCode, json);
}
}
As you can see there nothing than weird here it is a simple code that try to parse a json value to a Generic class but this fails bacause when i call my webapi Url it gives me this value
json = ��VR�LQ�R2T�Q�2u�B*R�"E�e�)�#q�������̔Ԣb%��Ҝ�Z�>#�>#�>�̊B������J�r2�q�
I don't know why my webapi give me tha value, when I try to debug just my service it give me this value
<ArrayOfCompanyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" ><CompanyType z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"><Id>1</Id><Type>Privada</Type><JobProviders i:nil="true" /></CompanyType><CompanyType z:Id="i2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"><Id>2</Id><Type>Mixta</Type><JobProviders i:nil="true" /></CompanyType><CompanyType z:Id="i3" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"><Id>3</Id><Type>Publica</Type><JobProviders i:nil="true" /></CompanyType></ArrayOfCompanyType>
as you can see everything looks fine but the problem start when I try to parse to get this value from my client.
this is my class
[DataContract(IsReference = true, Name = #"CompanyType", )]
public class CompanyType : Entity
{
[DataMember(Order = 0)]
public int Id { get; set; }
[DataMember(Order = 1)]
public string Type { get; set; }
[DataMember(Order = 2)]
public virtual List<JobProvider> JobProviders { get; set; }
}
I tried it without de DataContracts and still the same error.
best regards!.
Well I figured out, I was trying to use this sample http://blog.developers.ba/asp-net-web-api-gzip-compression-actionfilter/ but the thing was that the serializer wasn´t registered, so at my startup project I did this
GlobalConfiguration.Configuration.Formatters.Add(new ProtoBufFormatter());
And I changed my Actions deleting the attribute [DeflateCompression]
[ResponseType(typeof(List<CompanyType>))]
[Route("GetList")]
//[DeflateCompression]
public async Task<IHttpActionResult> Get()
{
Logic goes here....
}
And It's working now, but now I have another Issue and its when I try to make a call to my webapi action where I have to response an IHttpActionResult there is an exception of
no serializer defined for type: System.Object
We are integrating with mobile software company to do our application in a mobile device.
Our controller has ( simplification) methods like :
api/users/1
–GetUserById(...)
api/users/changePassword
–ChangePassword(Person p)
Ok.
The ChangePassword can return several applicative error codes ( password has already used , password too short , password bla bla...)
So if ,for example, password has already been used , then the HttpCode should be 200 returned with additional info
We agreed on this convention for every response :( additional to the response data)
{
"Success":0,
"ErrorCode": 6,
"ErrorMessage":"already used"
}
But this structure , as I said - should be in every response.
So till now - for example : api/users/1 returned :
{
"userId":1,
"name":"John"
}
But now - the response should be :
{
"data":
{
"userId":1,
"name":"John"
}
,
"result": //no errors
{
"Success":0,
"ErrorCode": 0,
"ErrorMessage":""
}
}
They always looking for the "result" object to see the applicative response.
Question
I assume that the place which I should do it is in message handler after base.SendAsync ( response part)
But how should I wrap the regular response which I send via Request.CreateResponseMessage with the format + values of :
NB , of course at the Request.CreateResponseMessage phase I already have result object with the appropriate result codes.
By the time message handlers run in Web API pipeline, the result your action method has produced would have been serialized. An action filter would be a better option, since you can deal with objects, and you can do something like this.
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext context)
{
var content = context.Response.Content as ObjectContent;
if (content != null)
{
content.Value =
new MyResponse()
{
Result = new Result() { Success = 0, ErrorCode = 6 },
Data = content.Value
};
}
}
}
public class Result
{
public int Success { get; set; }
public int ErrorCode { get; set; }
}
public class MyResponse
{
public Result Result { get; set; }
public object Data { get; set; }
}
Note: The above code will work only for JSON and not XML.
You can create an ActionFilterAttribute, and on the OnActionExecuted Method you will get HttpActionExecutedContext where you can check the response message.
you can decorate your controlleror action by this attribute and return and create you own ResponseMessage.
Hope that helps.
i'm making a request do a asp.net webapi Post Method, and i'm not beeing able to get a request variable.
Request
jQuery.ajax({ url: sURL, type: 'POST', data: {var1:"mytext"}, async: false, dataType: 'json', contentType: 'application/x-www-form-urlencoded; charset=UTF-8' })
.done(function (data) {
...
});
WEB API Fnx
[AcceptVerbs("POST")]
[ActionName("myActionName")]
public void DoSomeStuff([FromBody]dynamic value)
{
//first way
var x = value.var1;
//Second way
var y = Request("var1");
}
i Cannot obtain the var1 content in both ways... (unless i create a class for that)
how should i do that?
First way:
public void Post([FromBody]dynamic value)
{
var x = value.var1.Value; // JToken
}
Note that value.Property actually returns a JToken instance so to get it's value you need to call value.Property.Value.
Second way:
public async Task Post()
{
dynamic obj = await Request.Content.ReadAsAsync<JObject>();
var y = obj.var1;
}
Both of the above work using Fiddler. If the first option isn't working for you, try setting the content type to application/json to ensure that the JsonMediaTypeFormatter is used to deserialize the content.
After banging my head around for a while on this and trying many different things I ended up putting some breakpoints on the API server and found the key value pairs stuffed down in the request. After I knew where they were, it was easy to access them. However, I have only found this method to work with WebClient.UploadString. However, it does work easily enough and allows you to load up as many parameters as you like and very easily access them server side. Note that I am targeting .net 4.5.
CLIENT SIDE
// Client request to POST the parameters and capture the response
public string webClientPostQuery(string user, string pass, string controller)
{
string response = "";
string parameters = "u=" + user + "&p=" + pass; // Add all parameters here.
// POST parameters could also easily be passed as a string through the method.
Uri uri = new Uri("http://localhost:50000/api/" + controller);
// This was written to work for many authorized controllers.
using (WebClient wc = new WebClient())
{
try
{
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
response = wc.UploadString(uri, login);
}
catch (WebException myexp)
{
// Do something with this exception.
// I wrote a specific error handler that runs on the response elsewhere so,
// I just swallow it, not best practice, but I didn't think of a better way
}
}
return response;
}
SERVER SIDE
// In the Controller method which handles the POST request, call this helper:
string someKeyValue = getFormKeyValue("someKey");
// This value can now be used anywhere in the Controller.
// Do note that it could be blank or whitespace.
// This method just gets the first value that matches the key.
// Most key's you are sending only have one value. This checks that assumption.
// More logic could be added to deal with multiple values easily enough.
public string getFormKeyValue(string key)
{
string[] values;
string value = "";
try
{
values = HttpContext.Current.Request.Form.GetValues(key);
if (values.Length >= 1)
value = values[0];
}
catch (Exception exp) { /* do something with this */ }
return value;
}
For more info on how to handle multi-value Request.Form Key/Value pairs, see:
http://msdn.microsoft.com/en-us/library/6c3yckfw(v=vs.110).aspx
I searched all morning to find an answer that depicted both client and server code, then finally figured it out.
Brief intro - The UI is an MVC 4.5 project that implements a standard view. The server side is an MVC 4.5 WebApi. The objective was to POST the model as JSON and subsequently update a database. It was my responsibility to code both the UI and backend. Below is the code. This worked for me.
Model
public class Team
{
public int Ident { get; set; }
public string Tricode { get; set; }
public string TeamName { get; set; }
public string DisplayName { get; set; }
public string Division { get; set; }
public string LogoPath { get; set; }
}
Client Side (UI Controller)
private string UpdateTeam(Team team)
{
dynamic json = JsonConvert.SerializeObject(team);
string uri = #"http://localhost/MyWebApi/api/PlayerChart/PostUpdateTeam";
try
{
WebRequest request = WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "application/json; charset=utf-8";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(json);
streamWriter.Flush();
streamWriter.Close();
}
WebResponse response = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
}
}
catch (Exception e)
{
msg = e.Message;
}
}
Server Side (WebApi Controller)
[Route("api/PlayerChart/PostUpdateTeam")]
[HttpPost]
public string PostUpdateTeam(HttpRequestMessage context)
{
var contentResult = context.Content.ReadAsStringAsync();
string result = contentResult.Result;
Team team = JsonConvert.DeserializeObject<Team>(result);
//(proceed and update database)
}
WebApiConfig (route)
config.Routes.MapHttpRoute(
name: "PostUpdateTeam",
routeTemplate: "api/PlayerChart/PostUpdateTeam/{context}",
defaults: new { context = RouteParameter.Optional }
);
Try this.
public string Post(FormDataCollection form) {
string par1 = form.Get("par1");
// ...
}
try using following way
[AcceptVerbs("POST")]
[ActionName("myActionName")]
public static void DoSomeStuff(var value)
{
//first way
var x = value;
}
I'm currently developing my own AuthorizationManager, it looks something like that:
public class MyAuthorizationManager : ServiceAuthorizationManager
{
static bool initialize = false;
public override bool CheckAccess(OperationContext operationContext)
{
ServiceSecurityContext context = ServiceSecurityContext.Current;
string[] roles = Roles.GetRolesForUser(operationContext.ServiceSecurityContext.PrimaryIdentity.Name);
return roles.Count() > 0;
}
public override bool CheckAccess(OperationContext operationContext, ref System.ServiceModel.Channels.Message message)
{
MessageBuffer buffer = operationContext.RequestContext.RequestMessage.CreateBufferedCopy(int.MaxValue);
message = buffer.CreateMessage();
Console.WriteLine(message);
return base.CheckAccess(operationContext, ref message);
}
}
I would like to perform authorization check based on a service contract parameter, in example, if contract looks like:
[ServiceContract]
public interface IServerContract
{
[OperationContract]
[ServiceKnownType(typeof(ChildTypeOne))]
[ServiceKnownType(typeof(ChildTypeTwo))]
string SecuredMessage(ParentType incoming);
}
My goal is authorizing depending on type, in example, authorizing if incoming date is ChildTypeOne and deniying in case it was ChildTypeTwo.
I've checked "Message" and it looks like:
It must be decrypted
Seems to be highly dependent on binding
Is there any easy way to simply get parameter type?
Ok, i've figured out how to perform that. Anyway, if you know any better way to do so, let me know:
Here is the AuthorizationManager i'm using:
public class MyAuthorizationManager : ServiceAuthorizationManager
{
static bool initialize = false;
public override bool CheckAccess(OperationContext operationContext, ref System.ServiceModel.Channels.Message message)
{
bool returnedValue = base.CheckAccess(operationContext, ref message);
// messags in WCF are always read-once
// we create one copy to work with, and one copy to return back to the plumbing
MessageBuffer buffer = operationContext.RequestContext.RequestMessage.CreateBufferedCopy(int.MaxValue);
message = buffer.CreateMessage();
// get the username vale using XPath
XPathNavigator nav = buffer.CreateNavigator();
StandardNamespaceManager nsm = new StandardNamespaceManager(nav.NameTable);
nav = nav.SelectSingleNode("//#i:type",nsm);
returnedValue &= (nav.ToString() == "a:"+typeof(ChildTypeOne).Name);
return returnedValue;
}
public class StandardNamespaceManager : XmlNamespaceManager
{
public StandardNamespaceManager(XmlNameTable nameTable)
: base(nameTable)
{
this.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
this.AddNamespace("s11", "http://schemas.xmlsoap.org/soap/envelope/");
this.AddNamespace("s12", "http://www.w3.org/2003/05/soap-envelope");
this.AddNamespace("wsaAugust2004", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
this.AddNamespace("wsa10", "http://www.w3.org/2005/08/addressing");
this.AddNamespace("i", "http://www.w3.org/2001/XMLSchema-instance");
}
}
}
Previous AuthorizationManager will work rejecting "ChildTypeTwo". You can use a RoleProvider in order to get role based on type.