Cannot post object from .net Client web-api web service - asp.net

I have one .net client which tries to make http request to web api service
here is my Request:
public List<Category> GetCategories()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:54558/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Task<string> response = client.GetStringAsync("api/CategoryApi/");
List<Category> lstCategory = JsonConvert.DeserializeObjectAsync<List<Category>>(response.Result).Result;
return lstCategory;
}
public void Create(Category category)
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var stringContent = new StringContent(category.ToString());
HttpResponseMessage responseMessage = client.PostAsync("api/CategoryApi/", stringContent).Result;
}
and in my webapi
public IEnumerable<Category> GetCategories()
{
return categoryRepository.data;
}
public string PostCategory(Category category)
{
categoryRepository.add(category);
return "MessageOk";
}
SO when I make request to my GetCategories action of the web-api everything is OK.
and no matter what I do it seems that .net application cannot find the Post action of the web-api and I never actually see entering in Postcategory method
as I have also put breakpoints here.
I only get the error
atusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Pragma: no-cache
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNccG9zdGdyZXNcRGVza3RvcFxXZWJBcGlTZXJ2aWNlXFdlYkFwaVNlcnZpY2VcYXBpXENhdGVnb3J5QXBpXA==?=
Cache-Control: no-cache
Date: Sat, 13 Jun 2015 17:55:16 GMT
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Content-Length: 1022
Content-Type: application/json; charset=utf-8
Expires: -1
}}
What may be the issue. Thak you in advance

You are posting just a Category.ToString which if you didn't override ToString to be a Json or XML string, it will fail on the server side because there is no way to deserialize the content into a Category object. You should serialize the Category on the client before posting it. Also make sure your request headers include the proper Content-Type of application/json. By posting StringContent, the Content-Type won't be application/json. You are setting the Accept header, but that only describes the data coming back to your client, not the data you are posting. One last thing, I would not use the same HttpClient for both the get and the post request. Each method should use it's own HttpClient so you don't have any extra headers depending on the call.

Related

Microsoft Cognitive Speech Speaker Identification Can't create enrollment

Please help to advise for my issue below.
I try to create an enrollment using sample code from here
private async Task<HttpResponseMessage> MakeRequest()
{
string path = #"<path_to_wav_file>";
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "<my_key>");
// Request parameters
queryString["shortAudio"] = "true";
queryString["identificationProfileId"] = "<my_profile_id>";
var uri = "https://westus.api.cognitive.microsoft.com/spid/v1.0/identificationProfiles/<my_profile_id>/enroll?" + queryString;
HttpResponseMessage response;
// Request body
byte[] byteData = File.ReadAllBytes(path);
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response = await client.PostAsync(uri, content);
}
return response;
}
and got the response
{StatusCode: 202, ReasonPhrase: 'Accepted', Version: 1.1, Content:
System.Net.Http.StreamContent, Headers: { Pragma: no-cache
Operation-Location:
https://westus.api.cognitive.microsoft.com/spid/v1.0/operations/af54c843-8df9-4511-8d65-4825ebec024d
apim-request-id: 37567cff-d259-4a1d-82fc-9fc884edcfe3
Strict-Transport-Security: max-age=31536000; includeSubDomains;
preload x-content-type-options: nosniff Cache-Control: no-cache
Date: Tue, 08 Jan 2019 07:12:05 GMT X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET Content-Length: 0 Expires: -1 }}
said that
{"error":{"code":"Unspecified","message":"Access denied due to invalid
subscription key. Make sure you are subscribed to an API you are
trying to call and provide the right key."}}
The error message is strange because I used the same Subscription Key that created profile successfully.
I think you should use ("Ocp-Apim-Subscription-Key", "") when make request to
https://api.projectoxford.ai/spid/v1.0/operations/af54c843-8df9-4511-8d65-4825ebec024d

Display content string from HttpResponseMessage

I have a post controller in an MVC app returning this response:
return new HttpResponseMessage(HttpStatusCode.Accepted)
{
Content = new StringContent("test")
};
When I hit the post URL with this code:
using (WebClient client = new WebClient())
{
string result = client.UploadString(url, content);
}
result contains this response:
StatusCode: 202, ReasonPhrase: 'Accepted', Version: 1.1, Content: System.Net.Http.StringContent, Headers: { Content-Type: text/plain; charset=utf-8 }
Why isn't "test" appearing after Content:?
Thanks!
You should not return HttpResponseMessage from ASP.NET MVC action. In this case you'll get messy response like this:
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
Server: Microsoft-IIS/10.0
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcRHJvcGJveFxwcm9nXFN0YWNrT3ZlcmZsb3dcZG90TmV0XE12Y0FwcGxpY2F0aW9u?=
X-Powered-By: ASP.NET
Date: Sun, 04 Feb 2018 10:18:38 GMT
Content-Length: 154
StatusCode: 202, ReasonPhrase: 'Accepted', Version: 1.1, Content: System.Net.Http.StringContent, Headers:
{
Content-Type: text/plain; charset=utf-8
}
As you see, you actually get 200 HTTP response with HttpResponseMessage details in response body. This messy body content is what you deserialize into result variable.
ASP.NET MVC actions should return an instance of the class derived from System.Web.Mvc.ActionResult. Unfortunately, there is no built-in action result that allows setting both return status code and body content.
There is ContentResult class that allows to set return string content with status code of 200. There is also HttpStatusCodeResult that allows setting arbitrary status code but the response body will be empty.
But you could implement your custom action result with settable status code and response body. For simplicity, you could base it on ContentResult class. Here is a sample:
public class ContentResultEx : ContentResult
{
private readonly HttpStatusCode statusCode;
public ContentResultEx(HttpStatusCode statusCode, string message)
{
this.statusCode = statusCode;
Content = message;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
base.ExecuteResult(context);
HttpResponseBase response = context.HttpContext.Response;
response.StatusCode = (int)statusCode;
}
}
The action would look like:
public ActionResult SomeAction()
{
return new ContentResultEx(HttpStatusCode.Accepted, "test");
}
Another possible fix is to change your controller from MVC to WEB API controller. To make this - just change base class of controller from System.Web.Mvc.Controller to System.Web.Http.ApiController. In this case you could return HttpResponseMessage as in your answer.
In both cases you will get correct HTTP response with 202 status code and string in the body:
HTTP/1.1 202 Accepted
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/10.0
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcRHJvcGJveFxwcm9nXFN0YWNrT3ZlcmZsb3dcZG90TmV0XE12Y0FwcGxpY2F0aW9u?=
X-Powered-By: ASP.NET
Date: Sun, 04 Feb 2018 10:35:24 GMT
Content-Length: 4
test

ASP.NET Web API OAuth2 customize 401 unauthorized response

I am using Microsoft.Owin.Security.Jwt. My resource server is configured as follows:
// Resource server configuration
var audience = "hello";
var secret = TextEncodings.Base64Url.Decode("world);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
}
});
Currently, when a token is expired, the Reponse is as follows:
401 Unauthorized
**Headers:**
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/10.0
Www-Authenticate: Bearer
X-Sourcefiles: =?UTF-8?B?Yzpcc3JjXFVTQi5FbnRlcnByaXNlQXV0b21hdGlvbi5BdXRoQXBpXFVTQi5FbnRlcnByaXNlQXV0b21hdGlvbi5BdXRoQXBpXGFwaVx1c2VyXGxvb2t1cFxsaWtvc3Rv?=
X-Powered-By: ASP.NET
Date: Fri, 30 Dec 2016 13:54:26 GMT
Content-Length: 61
Body
{
"message": "Authorization has been denied for this request."
}
Is there a way to set a custom Www-Authenticate header, and/or add to the body if the token is expired?
I'd like to return something like:
WWW-Authenticate: Bearer realm="example",
error="invalid_token",
error_description="The access token expired"
One way to do this is to create a custom AuthorizeAttribute and then decorate the method or class in question. Make sure to override HandleUnauthorizedRequest and then call its base method to carry on as normal and return 401.
public class CustomAuthorize : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
HttpContext.Current.Response.AppendHeader("WWW-Authenticate", #"Bearer realm=""example"" ... ");
base.HandleUnauthorizedRequest(actionContext);
}
}
Usage:
[CustomAuthorize]
public IHttpActionResult Get()
{
...
}
May need some further logic around headers but should be enough to get started with.

Using POST for .NET ApiController action with name starting with "Delete"

I'd like to have an ApiController with a controller action that accepts POST requests and has a name that starts with Delete. I'm using the following jQuery:
function DeleteMyDomainObjectButton_OnClick() {
var $div = $(this).closest("div.domain-object");
var url = baseUrl + "Api/MyBusiness/DeleteMyDomainObject";
var dto = {
"MyDomainObjectId": $div.find("input[name=MyDomainObjectId]").val()
};
$.ajax({
complete: DeleteMyDomainObjectAjax_OnComplete
, dataType: "json"
, contentType: "application/json"
, accept: "applcation/json"
, data: JSON.stringify(dto)
, error: DeleteMyDomainObjectAjax_OnError
, method: "POST"
, success: DeleteMyDomainObjectAjax_OnSuccess
, url: url
});
}
I keep getting a 405: {"Message":"The requested resource does not support http method 'POST'."}. The response headers are:
HTTP/1.1 405 Method Not Allowed
Cache-Control: no-cache
Pragma: no-cache
Allow: DELETE
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 07 Jul 2015 20:45:35 GMT
Content-Length: 73
I did check the request headers, and it actually is sending a POST. Here's my controller:
public class BaseApiController : ApiController
{
/* Properties for All API Controllers set by DI */
}
public class MyBusinessController : BaseApiController
{
public virtual MyDomainObject FooBar(DeleteMyDomainObjectDto dto)
{
return this.DeleteMyDomainObject(p_input);
}
[HttpPost] // Doesn't work.
// [AcceptVerbs(HttpVerbs.Post)] // Also doesn't work.
public virtual MyDomainObject DeleteMyDomainObject(DeleteMyDomainObjectDto dto)
{
return BusinessLogic.DeleteMyDomainObject(dto)
}
}
Now, if I change the controller action that I reference from jQuery to FooBar, it works.
Question: How can I turn off the magic sauce that makes the ApiController only allow DELETE requests for controller actions with names starting with the string Delete? Or am I doing something else wrong?

Can't set ISO-8859-1 as charset of web-api response

I need to set charset ISO-8859-1 for the responses of my web api controllers, and not UTF-8.
The controller for testing returns a POCO object like this:
public class StudyCaseController : ApiController
{
...
// GET: api/StudyCase/5
public Study Get(int id)
{
...
}
}
I've tried to set <globalization requestEncoding="iso-8859-1" responseEncoding="iso-8859-1"/> in the Web.config, but testing with a fiddler request like this:
GET http://localhost:45988/api/StudyCase/1 HTTP/1.1
User-Agent: Fiddler
Host: localhost:45988
Accept: text/xml
I've got a response like this:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B? QzpcTUVESE9NRVxEZXNhcnJvbGxvQ1xQcm95ZWN0b3NcVmlld0NhcE1hblxWaWV3Q2FwTWFuXGFwaVxT dHVkeUNhc2VcMQ==?=
X-Powered-By: ASP.NET
Date: Tue, 24 Mar 2015 11:36:13 GMT
Content-Length: 1072
<?xml version="1.0"?>
... etc...
I've also tried to specify the charset at the request with Accept-Charset: iso-8859-1 but the same result.
For more info, i've tested it with IIS Express and IIS Server.
Thanks.
You can set supported encodings for formatters in HttpConfiguration class. 28591 is codepage for ISO-8859-1 (Latin 1).
config.Formatters.Add(new XmlMediaTypeFormatter());
config.Formatters[0].SupportedEncodings.Clear();
config.Formatters[0].SupportedEncodings.Add(Encoding.GetEncoding(28591));
Dealing with the problem my self, I found out that the problem was indeed the XML MediaTypeFormatter.
It does not support ISO-8859-1 and without the ability to change the server-side code I was forced to use another MediaTypeFormatter
https://www.nuget.org/packages/NetBike.Xml.Formatting/
or in nuget console
Install-Package NetBike.Xml.Formatting
This solved my problem (in a project using Web API 2).
Here is a demonstration of one way to set this using the HttpClient
private static HttpClient httpClient;
private static MediaTypeFormatter formatter;
private static List<MediaTypeFormatter> formatters;
public static void loadSettings()
{
//httpClient settings are set here
formatters = new List<MediaTypeFormatter>();
formatter = new NetBike.Xml.Formatting.NetBikeXmlMediaTypeFormatter();
formatters.Add(formatter);
}
public static async Task<HttpResponseMessage> GetContent(string requestMethod)
{
try
{
var returnValue = await httpClient.GetAsync(requestMethod);
return returnValue;
}
catch (Exception ex)
{
Debug.WriteLine("Communicator.GetXml: " + ex.Message);
}
return null;
}
public static async void testStuff()
{
var httpResponse = await GetContent("http://someDomain.com/someMethod");
MyModelObject myModel = await httpResponse.Content.ReadAsAsync<MyModelObject>(formatters);
}
What you want to do, is to set this as a default formatter for the whole project.
-EDIT-
Microsoft argues that this behavior is intended.
https://connect.microsoft.com/VisualStudio/feedback/details/958121
I have experienced issues with the NetBike Formatter, when formatting advanced XML objects, and even have a case where it writes BOM to the output.
Therefore I do not recommend it above the default MediaTypeSerializer, instead the solution falls back to the old "it depends"
That looks like this in code
List<MediaTypeFormatter> formatters = new List<MediaTypeFormatter>();
MediaTypeFormatter badencodingFormatter = new NetBike.Xml.Formatting.NetBikeXmlMediaTypeFormatter();
badencodingFormatter.SupportedEncodings.Clear();
badencodingFormatter.SupportedEncodings.Add(Encoding.GetEncoding("ISO-8859-1"));
MediaTypeFormatter defaultFormatter = new CustomXmlMediaTypeFormatter();
formatters.Add(badencodingFormatter);
formatters.Add(defaultFormatter);
This makes sure that anoying encoding is handled by NetBike, but only in those (hopefully rare) cases

Resources