I am consuming a web api wrote in c#, MVC, that looks like:
public IEnumerable<Day> Post([FromBody] string postObject)
Using fiddler or poster I can post to that REST service since my body has a = before my sending JSON, something like
={"BillTypeId":"4","RestId":"1"}
Using retrofit to make this call I can not send that initial = and my request looks like
{"BillTypeId":"4","RestId":"1"}
That makes the rest service receive always a null parameter.
Do you know a way to send it before my json? Since I am calling that rest like this
void postToGetDayList(#Body ResquestListenerBillType request, Callback<List<Day>> callback);
Thank you!!!
If you haven't fixed your issue, I think you can refer to my following code:
WebAPI:
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// POST api/values
public string Post([FromBody]string value)
{
return value;
}
}
Retrofit WebAPIService:
public interface WebAPIService {
#GET("/api/values")
List<String> listValues();
#POST("/api/values")
String postValues(#Body String value);
}
Call webAPIService.postValues("BNK");
You will find the result as the following image. Hope this helps!
before send class convert to string and add one " fist string and add one " on last string then send body
my client API is :
[Put("/WebPage/{key}/")]
Task<string> PutWebPage([AliasAs("key")]string id, [Body]string value);
sample code is :
WebPageModel webPageModelSave = new WebPageModel();
webPageModelSave = ObjectCopier.CloneClass(Public.CashEntity.webPageModel);
webPageModelSave.Address = uxLabel_AddressTitle.Text;
string codeingData = JsonConvert.SerializeObject(webPageModelSave);
codeingData = ArmanSerialize.CryptoString.Encrypt(codeingData, "5552552");
string resutlt = await Public.Helper.ApiServer.PutWebPage("123", "\""+codeingData+"\"");
and on server remove the " fist and last string like this:
// PUT api/<controller>/5
[HttpPut("{id}/")]
public string Put(string id, [FromBody]string value)
{
string removeCotation = value.Remove(value.Length - 1, 1).Remove(0, 1);
string valueItem = ArmanSerialize.CryptoString.Decrypt(value, "5552552");
string baseUrl = Request.Host.Host;
baseUrl = baseUrl.ToLower().Replace("http://", "").Replace("https://", "");
var serverID = 123;
if (id.Replace("\"", "") == serverID.Replace("\"","") && !string.IsNullOrEmpty(valueItem))
{
WebPageModel webPageModel = new WebPageModel();
webPageModel = JsonConvert.DeserializeObject<WebPageModel>(valueItem);
EntityFrameworkCore.LogicLayer logicLayer = new EntityFrameworkCore.LogicLayer();
logicLayer.UpdateWebPageModel(webPageModel);
return JsonConvert.SerializeObject("OK");
}
else
{
//error
return JsonConvert.SerializeObject("Error");
}
}
I test it on xamarin form and asp.net core 3 its work it.
Related
I need to Post two string values to add a company in DB. Here I have written PUT as
await Http.PutAsJsonAsync($"api/B2CUsers/company/invitation/add/{UtpID}/{User.email}",UtpID,User.email);
I get an error at User.email which says cannot convert string to System.Text.Json.JsonSerializerOptions.
controller function:
[HttpPut("company/invitation/add/{utpid}/{emailAddressInvitedUser}")]
public async Task<int> AddUserCompanyInvitationAsync(string utpid, string emailAddressInvitedUser)
{
string userid = GetCurrentUser();
return await _iB2CUserServiceRepository.AddUserCompanyInvitation(userid, utpid, emailAddressInvitedUser);
}
Note: It is customary to pass a single value to a Web Api end point, so you'd better define a single DTO object which will contain the values of both strings, something like this:
var dtoObj = new DtoObject { UtpID = UtpID, Email = User.email };
using var response =
await htpClient.PutAsJsonAsync("api/B2CUsers/company/invitation/add",
dtoObj );
response.EnsureSuccessStatusCode();
Note: Your code should define an object named DtoObject that is scoped to your Web Api and Blazor (Perhaps you can put it in the Shared project ?)
In the AddUserCompanyInvitationAsync Web Api method, you'll get a single parameter object of type DtoObject, which you can use it in your code like this:
return await
_iB2CUserServiceRepository.AddUserCompanyInvitation(userid, dtoObj.UtpID , dtoObj.Email);
Correct function signature is
[HttpGet("company/invitation/add2/{utpid}/{emailAddressInvitedUser}")]
public Task<int> AddUserCompanyInvitationAsync([FromRoute] string utpid, [FromRoute] string emailAddressInvitedUser)
If you don't add [FromRoute] attributes .NET will not map route values to your variables.
Keep in mind this kind of request will be logged in the web serveur log's. This mean email addresses will be visible in the logs. You also need to escape the email string with HttpUtility.UrlEncode
curl -X PUT "http://localhost:14786/company/invitation/add2/45/test%40test.com" -H "accept: text/plain"
The prefered way to do that is to pass Json request in the body.
public class AddUserRequest
{
public int utpid { get; set; }
public string emailAddressInvitedUser { get; set; }
}
public class AddUserResponse
{
public int uid { get; set; }
}
[HttpPut("company/invitation/add1")]
public Task<AddUserResponse> AddUserCompanyInvitationAsync([FromBody]AddUserRequest addUserRequest)
{
Console.WriteLine(addUserRequest.utpid);
Console.WriteLine(addUserRequest.emailAddressInvitedUser);
return Task.FromResult(new AddUserResponse() { uid = 1 });
}
and for the client
var client = new HttpClient();
int id = 45;
string email = "test#test.com";
await client.GetAsync($"http://localhost:14786/company/invitation/add2/{id}/{System.Web.HttpUtility.UrlEncode(email)}");
var response = await client.PutAsJsonAsync("http://localhost:14786/company/invitation/add1", new AddUserRequest() { utpid = id, emailAddressInvitedUser = email });
Instead of PUT use POST
I'm running a REST API service that has this action :
[HttpPost]
public FooResponse DoFoo(FooRequest request)
//public FooResponse DoFoo([FromBody] FooRequest request)
{
return null;
}
My request:
public class FooRequest
{
public string FooId;
}
I have an Angular client, that's making this call :
startFoo(fooId: string)
{
const url = `${this.baseUrl}StartFoo`;
const params = new HttpParams()
.set('FooId', fooId);
console.log(`params : ${params}`);
const result = this.httpClient.post<fooItem>(url, {params}).toPromise();
return result;
}
When I make the call from PostMan, the FooId is populated, when I call it from Angular, the endpoint is hit, but the param is always null. When I look in the console log, the parameters is there.
I've tried this solution, but it did not resolve my issue.
What am I missing?
You should add [FromBody] attribute in method .
[HttpPost]
public FooResponse DoFoo([FromBody] FooRequest request)
{
return null;
}
While you send the request to api, your request body must be in json format.
var fooRequest = { FooId : 1};
const result = this.httpClient.post<fooItem>(url, JSON.stringify(fooRequest) ).toPromise();
I did not try, I guess that It will work.
I'm trying to pass a big string in my controller using json. Also i need Controller to send me an answer.
Here is my controller in web api:
public class CustomersController : ApiController
{
// GET: api/Customers
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET: api/Customers/5
public string Get(int id)
{
return "value";
}
// POST: api/Customers
public void Post([FromBody]string value)
{
}
// PUT: api/Customers/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE: api/Customers/5
public void Delete(int id)
{
}
}
First of all where i should read my string and where should i send an answer?
And here is my client which try to send a string
using (var client = new HttpClient())
{
var response = await client.PostAsync("http://192.168.1.15:8282/",new StringContent("Mystring", Encoding.UTF8, "application/json"));
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
}
I need my web api to read my string, and then to send me an answer
Instead of having the methods as void you need to return the string value from the controller methods.
Also, don't forget to decorate the methods with respective http verb attribubte (HttpGet, HttpPost, HttpPut etc.) which the method is responsible to serve.
Here's an example where the method returns an Ok result, this generate an http status code 200 with the string in the response body
[HttpPost]
public IHttpActionResult Post([FromBody]string value)
{
return Ok(value);
}
Then for the client call.
First of, you need to specify the route to the controller correctly
192.168.1.15:8282/api/Customers
Then, sending a single string as content when using the content-type of application/json is not suitable as json always start parsing from an object {} or array [].
The easiest way of sending a single string is therefore to just change the content type to application/x-www-form-urlencoded and adding a = sign infront of the string
using (var client = new HttpClient())
{
var response = await client.PostAsync("http://192.168.1.15:8282/api/Customers",new StringContent("=Mystring", Encoding.UTF8, "application/x-www-form-urlencoded"));
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
}
}
In my WebApi I have a HttpGet and HttpPost method, the get method is working fine and the post method is called but the body content is always null, unless used in a HttpRequestMessage. I tried providing the body content in a string format(preferred datatype) aswell as in a model but neither one of those methods worked. I also tried switching the content type without success. Does anyone know if I'm doing something wrong or how I can easily get the variable data from the HttpRequestMessage, which in the example below is "test".
Method 1:
[System.Web.Http.HttpPost]
[Route("api/v1/AddItem")]
public IHttpActionResult AddItem([FromBody]string filecontent, string companycode)
{
MessageBox.Show(filecontent);
Return Ok("");
}
Method 2 (with model):
[System.Web.Http.HttpPost]
[Route("api/v1/AddItem")]
public IHttpActionResult AddItem([FromBody]ItemXML filecontent, string companycode)
{
MessageBox.Show(filecontent.XMLContent);
Return Ok("");
}
Model:
public class ItemXML
{
public ItemXML(string content)
{
XMLContent = content;
}
public string XMLContent { get; set; }
}
Method 3:
[System.Web.Http.HttpPost]
[Route("api/v1/AddItem")]
public IHttpActionResult AddItem(HttpRequestMessage filecontent, string companycode)
{
var content = filecontent.Content.ReadAsStringAsync().Result;
MessageBox.Show(content);
Return Ok("");
}
Method 3 content string ("test" is the provided value): " content "------WebKitFormBoundarydu7BJizb50runvq0\r\nContent-Disposition: form-data; name=\"filecontent\"\r\n\r\n\"test\"\r\n------WebKitFormBoundarydu7BJizb50runvq0--\r\n" string"
Create a model store data to be sent to server
public class Model {
public string filecontent { get; set;}
public string companycode { get; set;}
}
Update Action
[HttpPost]
[Route("api/v1/AddItem")]
public IHttpActionResult AddItem([FromBody]Model model) {
if(ModelStat.IsValid) {
return Ok(model); //...just for testing
}
return BadRequest();
}
On the client make sure the request is being sent properly. In this case going to use JSON.
public client = new HttpClient();
var model = new {
filecontent = "Hello World",
companycode = "test"
};
var response = await client.PostAsJsonAsync(url, model);
If using another type of client ensure that the data being sent is formatted correctly for the Web API action to accept the request.
Reference Parameter Binding in ASP.NET Web API
I'm looking at the documentation of WebAPI 2, and i'm severely disappointed with the way the action results are architected. I really hope there is a better way.
So documentation says I can return these:
**void** Return empty 204 (No Content)
**HttpResponseMessage** Convert directly to an HTTP response message.
**IHttpActionResult** Call ExecuteAsync to create an HttpResponseMessage, then convert to an HTTP response message.
**Other type** Write the serialized return value into the response body; return 200 (OK).
I don't see a clean way to return an array of items with custom HTTP status code, custom headers and with auto negotiated content though.
What I would like to see is something like
public HttpResult<Item> Post()
{
var item = new Item();
var result = new HttpResult<Item>(item, HttpStatusCode.Created);
result.Headers.Add("header", "header value");
return result;
}
This way I can glance over a method and immediately see whats being returned, and modify status code and headers.
The closest thing I found is NegotiatedContentResult<T>, with weird signature (why does it need an instance of controller?), but there's no way to set custom headers?
Is there a better way ?
The following code should give you everything you want:
[ResponseType(typeof(Item))]
public IHttpActionResult Post()
{
var item = new Item();
HttpContext.Current.Response.AddHeader("Header-Name", "Header Value");
return Content(HttpStatusCode.Created, item);
}
... if you really need to return an array of items ...
[ResponseType(typeof(List<Item>))]
public IHttpActionResult Post()
{
var items = new List<Item>();
// Do something to fill items here...
HttpContext.Current.Response.AddHeader("Item-Count", items.Count.ToString());
return Content(HttpStatusCode.Created, items);
}
I don't think the designers of the web-api intended for controller methods to be fiddling with the headers.
The design pattern seems to be to use DelegatingHandler, ActionFilterAttribute and the ExecuteAsync overridable method of ApiController to handle authentication and response formatting.
So perhaps your logic for message content negotiation should be handled there ?
However if you definitely need to control headers from within your controller method you can do a little set-up to make it work.
To do so you can create your own DelegationHandler that forwards selected headers from your "Inner" response headers:
public class MessageHandlerBranding : DelegatingHandler {
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
//If we want to forward headers from inner content we can do this:
if (response.Content != null && response.Content.Headers.Any())
{
foreach (var hdr in response.Content.Headers)
{
var keyUpr = hdr.Key.ToUpper(); //Response will not tolerate setting of some header values
if ( keyUpr != "CONTENT-TYPE" && keyUpr != "CONTENT-LENGTH")
{
string val = hdr.Value.Any() ? hdr.Value.FirstOrDefault() : "";
response.Headers.Add(hdr.Key, val);
}
}
}
//Add our branding header to each response
response.Headers.Add("X-Powered-By", "My product");
return response;
}
}
Then you register this handler in your web-api configuration, this is usually in the GlobalConfig.cs file.
config.MessageHandlers.Add(new MessageHandlerBranding());
You could also write your own custom class for the response object like this:
public class ApiQueryResult<T> : IHttpActionResult where T : class
{
public ApiQueryResult(HttpRequestMessage request)
{
this.StatusCode = HttpStatusCode.OK; ;
this.HeadersToAdd = new List<MyStringPair>();
this.Request = request;
}
public HttpStatusCode StatusCode { get; set; }
private List<MyStringPair> HeadersToAdd { get; set; }
public T Content { get; set; }
private HttpRequestMessage Request { get; set; }
public void AddHeaders(string headerKey, string headerValue)
{
this.HeadersToAdd.Add(new MyStringPair(headerKey, headerValue));
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = this.Request.CreateResponse<T>(this.StatusCode, this.Content);
foreach (var hdr in this.HeadersToAdd)
{
response.Content.Headers.Add(hdr.key, hdr.value);
}
return Task.FromResult(response);
}
private class MyStringPair
{
public MyStringPair(string key, string value)
{
this.key = key;
this.value = value;
}
public string key;
public string value;
}
}
And use it like this in your controller:
[HttpGet]
public ApiQueryResult<CustomersView> CustomersViewsRow(int id)
{
var ret = new ApiQueryResult<CustomersView>(this.Request);
ret.Content = this.BLL.GetOneCustomer(id);
ret.AddHeaders("myCustomHkey","myCustomValue");
return ret;
}