I'm doing a simple backend with .Net Core that reads data from GET and POST, but I'm not finding how to read GET params neither POST. I have this, a simple Controller:
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
private readonly ILogger<TestController> _logger;
public TestController(ILogger<TestController> logger)
{
_logger = logger;
}
[HttpGet]
public string Get()
{
return "Test GET OK";
}
[HttpPost]
public string Post()
{
return "Test POST OK";
}
}
Client, a simple windows forms with net framework 4.6, is using HttpClient to sent http get request:
public async Task<string> GetAsyncHttpClient(string uri)
{
string responseBody = "";
try
{
UriBuilder builder = new UriBuilder(uri);
builder.Query = "name=testName";
HttpResponseMessage response = await client.GetAsync(builder.Uri);
response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsStringAsync();
// Above three lines can be replaced with new helper method below
// string responseBody = await client.GetStringAsync(uri);
}
catch (Exception e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ", e.Message);
responseBody = "Error with GET operation, exception:\n" + e.ToString();
}
return responseBody;
}
And generated URL is like this:
http://localhost:5915/test?name=testName
Trust me that I've searched a lot and I didn't find how to read and iterate over GET params.
How should I do it?
Thanks!
Normally you would just add a parameter to your method:
[HttpGet]
public string Get(string name)
You can be explicit that it's a query string parameter like this:
[HttpGet]
public string Get([FromQuery]string name)
As for iterating the parameters, you'll have to use Request.Query:
foreach (KeyValuePair<string, StringValues> entry in Request.Query)
{
string key = entry.Key;
foreach (string value in entry.Value)
{
System.Diagnostics.Debug.WriteLine($"{key}={value}");
}
}
You'll need to add a using Microsoft.Extensions.Primitives; for the StringValues. The reason why it's StringValues is because you could have a URL like this: https://www.example.com/test?name=Brian&name=Jennifer, so you would end up with two values in the Query collection entry for "name".
I don't know exactly what you mean but if you just want to make a post or get request then you do it in your client like this:
using (var client = new HttpClient())
{
try
{
HttpResponseMessage response =
await client.PostAsync("https://localhost:YOURPORT/Test?username=test", YOURCONTENT);
var cont = await response.Content.ReadAsStringAsync();
Console.WriteLine(cont);
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadKey();
}
make sure you are using http or https accordingly you have to adjust the url as well
if you mean Query Params you can access them by adding this to the API:
[HttpPost]
public void Post([FromQuery] string username){
//do something
}
I've developed a custom action filter in order to use it for logging response of my web-service in ASP.NET MVC.
However I don't know why when I add this action filter to my method, HTTP status response of my controller changes to 500 and it returns the message: 500 Intenal Server Error. I put all logic inside try catch block but still problem persists.
Here is my custom ActionFilter:
public class LogActionFilter : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
try
{
Log("OnActionExecuting", actionExecutedContext);
}
catch (Exception)
{
}
}
private void Log(string methodName, HttpActionExecutedContext context)
{
try
{
string resopnseBody = getBodyFromResponse(context);
HttpResponseMessage response = context.Response;
var headers = response.Headers;
var content = response.Content;
var actionName = response.ToString();
var message = "";
message = String.Format("response:{0}", resopnseBody);
Debug.WriteLine(message, "");
}
catch (Exception e)
{
}
}
private string getBodyFromResponse(HttpActionExecutedContext context)
{
string data;
using (var stream = context.Response.Content.ReadAsStreamAsync().Result)
{
if (stream.CanSeek)
{
stream.Position = 0;
}
data = context.Response.Content.ReadAsStringAsync().Result;
}
return data;
}
}
Update:
Furthur investigating my code I found that calling getBodyFromResponse leads to this error. I myself suspect to part which I will try to read stream .Result twice however since I copied! this code from elsewhere I don't understand its logic clearly.
Update2:
Here is a sample method in my controller:
[LogActionFilter]
[System.Web.Http.HttpPost]
public async Task<IHttpActionResult> Test()
{
return Ok(new WebServiceResult { responseCode = 0, responseMessage = null });
}
Update 3:
replacing
resopnseBody = getBodyFromResponse(context);
with below line fixed issue but I don't know why!
resopnseBody = context.Response.Content.ReadAsStringAsync().Result;
I got it to run by removing some lines from getBodyFromResponseAsync
private string getBodyFromResponseAsync(HttpActionExecutedContext context)
{
return context.Response.Content.ReadAsStringAsync().Result;
}
I hope the result is what you need.
I'm creating an Instagram API client on ASP MVC using HttpClient, I'm trying to make a get request but it fails without throwing exception or responding and doesn't respond to my timeout. Here is my code:
public class InstagramService
{
private HttpClient Client = new HttpClient {
BaseAddress = new Uri("https://api.instagram.com/v1/"),
Timeout = TimeSpan.FromMilliseconds(500)
};
public async Task<InstagramUser> GetInstagramUser(long? userId = null)
{
InstagramUser User = null;
string Parameter = (userId == null) ? "self" : userId.ToString();
try {
var response = await Client.GetAsync("users/" + Parameter + "/" + GetAccessToken());
if (response.IsSuccessStatusCode)
{
User = await response.Content.ReadAsAsync<InstagramUser>();
}
}catch(Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.InnerException.Message);
}
return User;
}
private string GetAccessToken()
{
return "?access_token=" + DB.config_det_sys.Single(i => i.codigo == "ACCESS_TOKEN_INSTAGRAM" && i.estado == true).Valor;
}
}
EDIT
Here I add how I call my service on the Home Controller, I will still test changing the controller to async Task
public class HomeController : Controller
{
private InstagramService IGService = new InstagramService();
public ActionResult About()
{
var apiCall = IGService.GetInstagramUser();
var model = apiCall.Result;
return View(model);
}
}
I tested on Postman trying to make the API call and it indeed worked, so where I'm failing to catch errors?
Your problem is here:
var model = apiCall.Result;
As I describe on my blog, you shouldn't block on asynchronous code. It can cause a deadlock.
Instead of Result, use await:
var model = await apiCall;
Adding to Stephen's answer, update the controller's action to be async all the way.
public class HomeController : Controller {
private InstagramService IGService = new InstagramService();
public async Task<ActionResult> About() {
var model = await IGService.GetInstagramUser();
return View(model);
}
}
I have an ASP.NET Core 1.0 Web API application and trying to figure out how to pass the exception message to the client if a function that my controller is calling errors out.
I have tried so many things, but nothing implements IActionResult.
I don't understand why this isn't a common thing that people need. If there truthfully is no solution can someone tell me why?
I do see some documentation out there using HttpResponseException(HttpResponseMessage), but in order to use this, I have to install the compat shim. Is there a new way of doing these things in Core 1.0?
Here is something I have been trying with the shim but it isn't working:
// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
Customer c = _customersService.GetCustomerById(id);
if (c == null)
{
var response = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("Customer doesn't exist", System.Text.Encoding.UTF8, "text/plain"),
StatusCode = HttpStatusCode.NotFound
};
throw new HttpResponseException(response);
//return NotFound();
}
return new ObjectResult(c);
}
When the HttpResponseException is thrown, I look on the client and can't find the message I am sending anything in the content.
Here is an simple error DTO class
public class ErrorDto
{
public int Code {get;set;}
public string Message { get; set; }
// other fields
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
And then using the ExceptionHandler middleware:
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500; // or another Status accordingly to Exception Type
context.Response.ContentType = "application/json";
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
var ex = error.Error;
await context.Response.WriteAsync(new ErrorDto()
{
Code = <your custom code based on Exception Type>,
Message = ex.Message // or your custom message
// other custom data
}.ToString(), Encoding.UTF8);
}
});
});
Yes it is possible to change the status code to whatever you need:
In your CustomExceptionFilterAttribute.cs file modify the code as follows:
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.Result = new ContentResult
{
Content = $"Error: {exception.Message}",
ContentType = "text/plain",
// change to whatever status code you want to send out
StatusCode = (int?)HttpStatusCode.BadRequest
};
}
}
That's pretty much it.
If you have custom exceptions, then you can also check for them when grabbing the thrown exception from the context. Following on from that you can then send out different HTTP Status Codes depdending on what has happened in your code.
Hope that helps.
You can create a custom Exception Filter like below
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.Result = new JsonResult(exception.Message);
}
}
Then apply the above attribute to your controller.
[Route("api/[controller]")]
[CustomExceptionFilter]
public class ValuesController : Controller
{
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
throw new Exception("Suckers");
return new string[] { "value1", "value2" };
}
}
Rather than raising and catching an exception, how about you simplify your action to:
// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
var customer = _customersService.GetCustomerById(id);
if (customer == null)
{
return NotFound("Customer doesn't exist");
}
return Ok(customer);
}
I wrote a blog post with some more options such as returning a JSON object instead of text.
Maybe that is helpful. You can return just object and sent for example a BadRequest (HTTP CODE: 400) with your custom object as actual parameter (I just used an interpolated string here) but you can put in anything.
In your client side you can catch that error situation for example with an AJAX error handler.
// GET: api/TruckFahrerGeoData
[HttpGet]
public object GetTruckFahrerGeoData()
{
var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();
var geodataItems = _context.TruckFahrerGeoData;
foreach (var truckFahrerGeoData in geodataItems)
{
GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);
if (geoTelemetryData == null)
{
return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
}
TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
{
Speed = geoTelemetryData.Speed,
Accuracy = geoTelemetryData.Accuracy,
TruckAppId = geoTelemetryData.Activity.TruckAppId,
TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
ClId = geoTelemetryData.Activity.ClId,
TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
TaskId = geoTelemetryData.Activity.TaskId,
TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
};
truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
}
return truckFahrerGeoDataItems;
}
Or an even more cleaner way with IActionResult like that way:
// GET: api/TruckFahrerGeoData
[HttpGet]
public IActionResult GetTruckFahrerGeoData()
{
var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();
var geodataItems = _context.TruckFahrerGeoData;
foreach (var truckFahrerGeoData in geodataItems)
{
GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);
if (geoTelemetryData == null)
{
return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
}
TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
{
Speed = geoTelemetryData.Speed,
Accuracy = geoTelemetryData.Accuracy,
TruckAppId = geoTelemetryData.Activity.TruckAppId,
TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
ClId = geoTelemetryData.Activity.ClId,
TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
TaskId = geoTelemetryData.Activity.TaskId,
TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
};
truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
}
return Ok(truckFahrerGeoDataItems);
}
Late to the party but refining the answer .
Define your error response class with minimum below attributes
using Microsoft.AspNetCore.Http;
public class ErrorResponse
{
private readonly RequestDelegate next;
public ErrorResponse(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context )
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception ex)
{
var code = HttpStatusCode.InternalServerError;
string result = string.Empty;
object data = new object();
if (ex is ForbiddenException)
{
code = HttpStatusCode.Forbidden;
result = JsonConvert.SerializeObject(new Response<object>(Status.Forbidden(ex.Message), data));
}
else if(ex is BadRequestException){
code = HttpStatusCode.BadRequest;
result = JsonConvert.SerializeObject(new Response<object>(Status.BadRequest(ex.Message), data));
}
else if (ex is NotFoundException)
{
code = HttpStatusCode.NotFound;
result = JsonConvert.SerializeObject(new Response<object>(Status.NotFound(ex.Message), data));
}
else if (ex is UnauthorizedException)
{
code = HttpStatusCode.Unauthorized;
result = JsonConvert.SerializeObject(new Response<object>(Status.Unauthorized(ex.Message), data));
}
else
{
result = JsonConvert.SerializeObject(new Response<object>(Status.InternalServerError(ex.Message), data));
}
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
}
Next use this class as middleware in startup.cs class
app.UseHttpsRedirection();
app.UseMiddleware(typeof(ErrorResponse));
Now each request and response will go through this class,if an error occurs then error code will be set to true with error code. A sample response like below
data: {}
status: {
code: 404
error: true
message: "No employee data found"
type: "Not Found"
}
I had the same problem and after some research, I found out I could use HttpClient to call my API and read the response easily. HttpClient does not throw any error when the HTTP response contains an error code, but it sets the IsSuccessStatusCode property to false.
This is my function using the HttpClient. I call this from my controller.
public static async Task<HttpResponseMessage> HttpClientPost(string header, string postdata, string url)
{
string uri = apiUrl + url;
using (var client = new HttpClient())
{
//client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", header);
HttpResponseMessage response = await client.PostAsync(uri, new StringContent(postdata));
return response;
}
}
This is my controller code, where I call the function and read the response and determine whether I have an error or not and respond accordingly. Note that I am checking the IsSuccessStatusCode.
HttpResponseMessage response;
string url = $"Setup/AddDonor";
var postdata = JsonConvert.SerializeObject(donor);
response = await ApiHandler.HttpClientPost(HttpContext.Session.GetString(tokenName), postdata, url);
//var headers = response.Headers.Concat(response.Content.Headers);
var responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
tnxresult = JsonConvert.DeserializeObject<TnxResult>(AppFunctions.CleanResponse(responseBody));
return Json(new
{
ok = true,
message = tnxresult.Message,
statusCode = tnxresult.StatusCode
});
}
else
{
ApiError rs = JsonConvert.DeserializeObject<ApiError>(AppFunctions.CleanResponse(responseBody));
return Json(new
{
ok = false,
message = rs.Message,
statusCode = rs.StatusCode
});
}
My API returns error messages in JSON. If the call is successful, I am packing the response in JSON too.
The crucial line of code is this one...
var responseBody = await response.Content.ReadAsStringAsync();
It serializes the HTTP content to a string as an asynchronous operation.
After that I can convert my JSON string to an object and access the error/success message and the Status Code too.
I used to use ASMX web services, however have since read (and been told) that a better way to request data from a client etc is to use web API's with MVC.
I have created an MVC 4 web api application and getting to grips with how it works.
Currently I have a single public string in my valuesControllers -
public class ValuesController : ApiController
{
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
And I am currently trying to call this in my client like this -
class Product
{
public string value { get; set; }
}
protected void Button2_Click(object sender, EventArgs e)
{
RunAsync().Wait();
}
static async Task RunAsync()
{
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri("http://localhost:12345/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET
HttpResponseMessage response = await client.GetAsync("api/values/5");
if (response.IsSuccessStatusCode)
{
Product product = await response.Content.ReadAsAsync<Product>();
Console.WriteLine("{0}", product.value);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message.ToString());
}
}
}
On debugging I can step through the request and enter the web API code successfully however on the line -
Product product = await response.Content.ReadAsAsync<Product>();
This fails and enters my catch with the exception -
Error converting value "value" to type 'myDemo.Home+Product'. Path '', line 1, position 7.
Why is this?
Why is this?
Because from your controller action you are returning a string, not a Product which are 2 quite different types:
public string Get(int id)
{
return "value";
}
so make sure that you are consistently reading the value on the client:
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsAsync<string>();
Console.WriteLine("{0}", result);
}
Of course if you modified your API controller action to return a Product:
public Product Get(int id)
{
Product product = ... go fetch the product from the identifier
return product;
}
your client code would work as expected.