How do I make a http request from an asp.net middleware? - asp.net

I have a middleware which needs to call off to an external service to check for some state and then act on it. I'm wondering how I go about making a request from middleware?
I've seen some docs about having a HttpClientFactory service, but I'm not really sure how I make that available to my middleware?

You can use the default HttpClient
This allows you to create a client in your middleware and send any request you need.
Example:
using(var client = new HttpClient()){
try
{
HttpResponseMessage response = await client.GetAsync("http://www.contoso.com/");
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
// Above three lines can be replaced with new helper method below
// string responseBody = await client.GetStringAsync(uri);
Console.WriteLine(responseBody);
}
catch(HttpRequestException e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ",e.Message);
}
}

Related

Pass FromHeader the httpClient to webapi 6.0

Below is the code, I have used to call the api. However, May I know how to pass http header
for example
Get customer has a header [FromHeader] field.
string uri = "https://localhost:7290/customers";
var response = await _httpClient.GetAsync(uri);
HttpClient GetAsync() is a shortcut for generating an instance of a HttpRequestMessage set to perform a GET for the specified URI and passing it to the SendAsync() method.
You can create your own request message instance and append additional details such as headers, then use the SendAsync() method yourself.
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("header", "value");
var response = await client.SendAsync(request);
https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httprequestmessage?view=net-6.0

POSTing data while redirecting to a third-party URL using Response.Redirect()

In ASP.Net Core 2.0, how can I POST data while redirecting to a third-party URL using Response.Redirect()?
Note: I have to POST this data without using the query-string.
Response.Redirect triggers a GET request which means that the only option is using a query string.
Can you trigger the redirection from the client (if any) in order to make a POST request?
You must use object if you want post data without using query string.
[HttpPost]
public IActionResult Search([FromBody] CustomerSearchRequestApiModel request)
{
if (request == null)
{
return BadRequest();
}
return Ok(request);
}
It is impossible to use Response.Redirect() to send Post request.
For a workaround, you could try HttpClient to send Post request and then return the reponse to the web browser with ContentResult as text/html.
Here is a demo code:
public async Task<ContentResult> HtmlView()
{
using (var formDataContent = new MultipartFormDataContent())
{
HttpClient client = new HttpClient();
Article article = new Article { ArticleName = "AN" };
formDataContent.Add(new StringContent("AN", Encoding.UTF8, "application/json"), "ArticleName");
using (HttpClient httpClient = new HttpClient())
{
HttpResponseMessage response = await httpClient.PostAsync(#"https://localhost:44393/Articles/Create", formDataContent);
return new ContentResult
{
ContentType = "text/html",
StatusCode = (int)response.StatusCode,
Content = await response.Content.ReadAsStringAsync()
};
}
}
}
Note
Change the HttpClient part to send the right request to your own third party url with validate parameters.

dotnet core webapi calling .net webapi2

I am calling a .NET WebApi2 endpoint from a dotnet core webapi. When I debug into the .NET WebApi2 POST endpoint, my value is always null. Is this not possible to do?
When I call the GET endpoint with an ID, the ID is passed with no issues.
I have used both Postman and Fiddler to debug. Whenever I pass my JSON object from Postman to the .NET WebApi2 POST endpoint, my value is populated.
Beyond frustrated as this seems pretty simple. :,(
Updated to include code
dotnet core web api (calling from Postman)
[HttpPost]
public async Task PostAsync([FromBody] string value)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var jsonObject = new JObject();
jsonObject.Add("text", "Rich");
var response = await client.PostAsJsonAsync("http://localhost:54732/api/Rich", jsonObject);
var responseResult = response.Content.ReadAsStringAsync().Result;
}
.NET WebApi2 (JObject is always null)
// POST: api/Rich
public void Post(JObject value)
{
}
This boils down to using JObject basically. For your older Web Api action, JObject works merely because you're posting JSON, and JObject is a dynamic. However, that is an entirely incorrect approach. You should be binding to a concrete class that represents the JSON being posted. That said, you may or may not be able to change anything there, and its not technically the source of your current issue.
The actual source is that you're attempting to send a JObject, which is not doing what you think it is. Again, JObject is a dynamic. It has accessors to parse and access the underlying JSON, but it does not actually expose the members of that JSON object directly. As a result, if you attempt to serialize it, you won't get anything usable from it. Passing it to PostAsJsonAsync causes it to be serialized.
What you actually need is something like:
var jsonObject = new { text = "Rich" };
Then, what you're passing to PostAsJsonAsync will be an anonymous object with actual members that can be serialized.
My "REAL" issue turned out to be Transfer-Encoding: chunked was being sent in the request header.
Here is my corrected code (dotnet core web api):
public async Task PostAsync([FromBody] JObject value)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
var jsonObject = new { variable1 = "Rich" };
var json = JsonConvert.SerializeObject(jsonObject);
var content = new StringContent(json, Encoding.UTF8, "application/json");
content.Headers.ContentLength = json.Length;
var response = await client.PostAsync("http://localhost:54732/api/Rich", content);
var responseResult = response.Content.ReadAsStringAsync().Result;
}
Here is my .NET WebApi2 code:
public IHttpActionResult Post([FromBody]RichTest value)
{
return Ok(value.variable1 + " done");
}
public class RichTest
{
public string variable1 { get; set; }
}
When I set the content.Headers.ContentLength, the Transfer-Encoding: chunked is removed. Now my code is working!!
I am still curious why the original PostAsJsonAsync does not work...

How to modify token endpoint response body with Owin OAuth2 in Asp.Net Web API 2

I want to modify the response body from the token endpoint response.
I've tried to intercept the /Token request with a MessageHandler but it doesn't work.
I'm able to add some additional informations to the response by overriding the OAuthAuthorizationServerProvider.TokenEndpointmethod, but I'm not able to create my own response body.
Is there a way to intercept the /Token request?
Edit
I found out how to remove the response body content from the token endpoint response, like this: HttpContext.Current.Response.SuppressContent = true;
It seems the right way to achieve my goal, but now when I use the context.AdditionalResponseParameters.Add() method to add my custom information, the SuppressContent block any alterations.
Now I have something like this:
// Removing the body from the token endpoint response
HttpContext.Current.Response.SuppressContent = true;
// Add custom informations
context.AdditionalResponseParameters.Add("a", "test");
To simply add new items to the JSON token response, you can use TokenEndpointResponse instead of the TokenEndpoint notification.
If you're looking for a way to completely replace the token response prepared by the OAuth2 authorization server by your own one, there's sadly no easy way to do that because OAuthAuthorizationServerHandler.InvokeTokenEndpointAsync doesn't check the OAuthTokenEndpointContext.IsRequestCompleted property after invoking the TokenEndpointResponse notification.
https://github.com/aspnet/AspNetKatana/blob/dev/src/Microsoft.Owin.Security.OAuth/OAuthAuthorizationServerHandler.cs
This is a known issue, but it was too late to include it in Katana 3 when I suggested to fix it.
You should give Owin.Security.OpenIdConnect.Server a try: it's an a fork of the OAuthAuthorizationServerMiddleware designed for Katana 3.0 and 4.0.
https://www.nuget.org/packages/Owin.Security.OpenIdConnect.Server/1.0.2
Of course, it includes the correct check to allow bypassing the default token request processing (this was even one of the first things I fixed when forking it).
You were almost there +Samoji #Samoji and really helped/inspired me to get the answer.
// Add custom informations
context.AdditionalResponseParameters.Add("a", "test");
// Overwrite the old content
var newToken = context.AccessToken;
context.AdditionalResponseParameters.Add("access_token", newToken);
I found it just replaced my old token with my new.
This question is similar to How to extend IdentityServer4 workflow to run custom code
So you can create custom middleware and register it before OAuth2 service in Startup:
public void Configuration(IAppBuilder app)
{
....
app.Use(ResponseBodyEditorMiddleware.EditResponse);
app.UseOAuthAuthorizationServer(...);
...
}
where custom middleware is:
public static async Task EditResponse(IOwinContext context, Func<Task> next)
{
// get the original body
var body = context.Response.Body;
// replace the original body with a memory stream
var buffer = new MemoryStream();
context.Response.Body = buffer;
// invoke the next middleware from the pipeline
await next.Invoke();
// get a body as string
var bodyString = Encoding.UTF8.GetString(buffer.GetBuffer());
// make some changes to the body
bodyString = $"The body has been replaced!{Environment.NewLine}Original body:{Environment.NewLine}{bodyString}";
// update the memory stream
var bytes = Encoding.UTF8.GetBytes(bodyString);
buffer.SetLength(0);
buffer.Write(bytes, 0, bytes.Length);
// replace the memory stream with updated body
buffer.Position = 0;
await buffer.CopyToAsync(body);
context.Response.Body = body;
}
The best way to intercept request and response is via MessageHandler if you want to avoid doing so after a request has reached the IControllerFactory handler in the pipeline - obviously in that case use a custom 'Attribute'
I have used MessageHandlers in the past to intercept request to api/token, create a new request and get the response, create a new response.
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
//create a new auth request
var authrequest = new HttpRequestMessage();
authrequest.RequestUri = new Uri(string.Format("{0}{1}", customBaseUriFromConfig, yourApiTokenPathFromConfig));
//copy headers from the request into the new authrequest
foreach(var header in request.Headers)
{
authrequest.Headers.Add(header.Key, header.Value);
}
//add authorization header for your SPA application's client and secret verification
//this to avoid adding client id and secret in your SPA
var authorizationHeader =
Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", _clientIdFromConfig, _secretKeyFromConfig)));
//copy content from original request
authrequest.Content = request.Content;
//add the authorization header to the client for api token
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(request.Headers.Authorization.Scheme, authorizationHeader);
var response = await client.PostAsync(authrequest.RequestUri, authrequest.Content, cancellationToken);
if(response.StatusCode == HttpStatusCode.OK)
{
response.Headers.Add("MyCustomHeader", "Value");
//modify other attributes on the response
}
return response;
}
This works for me perfectly. There is, however, the configuration for this handler required in the WebApiConfig.cs file (RouteConfig.cs if you're using ASP.NET MVC).
Can you elaborate on what it is that does not work for you on the handler?

Securing ASP.NET MVC controller action which returns JSON

I have an MVC3 application, and my controller actions are secured using the [Authorize] attribute. So far, so good, forms auth works great. Now I want to add a JSON API to my application so some actions are accessible to non-browser clients.
I'm having trouble figuring out the 'right' design.
1) Each user has secret API key.
2) User ID 5 calls http://myapp.com/foocontroller/baraction/5?param1=value1&param2=value2&secure_hash=someValue. Here, secure_hash is simply the SHA1 hash of param1 and param2's values appended with the secret API key for the user
2) /foocontroller/baraction will be decorated with [CustomAuthorize]. This will be an implementation of AuthorizeAttribute which will check if the request is coming in as JSON. If it is, it will check the hash and see if it matches. Otherwise, if the request is HTML, then I call into existing authorization.
I am not at all sure if this will work. Is it normal to pass a secure hash in the query string or should I be passing it in as an HTTP header? Is it better to use HTTP basic auth instead of a hash made using the secret API key?
Tips from anyone who has made a web API using ASP.NET MVC would be welcome!
I pass the secret API key along with username and password in the request body. Once authorized, a token is generated and the client has to pass that in the Authorization header. This gets checked in the base controller on each request.
Client calls myapp.com/authorize which return auth token.
Client stores auth token locally.
Client calls myapp.com/anycontroller, with authtoken in Authorization header.
AuthorizeController inherits from controller.
Anycontroller inherits from a custom base controller which performs the authorization code.
My example requires the following route which directs POST requests to an ActionResult named post in any controller. I am typing this in by hand to simplify it as much as possible to give you the general idea. Don't expect to cut and paste and have it work :)
routes.MapRoute(
"post-object",
"{controller}",
new { controller = "Home", action = "post" {,
new { httpMethod = new HttpMethodConstraint("POST")}
);
Your auth controller can use this
public class AuthorizationController : Controller
{
public ActionResult Post()
{
string authBody;
var request = ControllerContext.HttpContext.Request;
var response = ControllerContext.HttpContext.Response;
using(var reader = new StreamReader(request.InputStream))
authBody = reader.ReadToEnd();
// authorize based on credentials passed in request body
var authToken = {result of your auth method}
response.Write(authToken);
}
}
Your other controllers inherit from a base controller
public class BaseController : Controller
{
protected override void Execute(RequestContext requestContext)
{
var request = requestContext.HttpContext.Request;
var response = requestContext.HttpContext.Response;
var authToken = Request.Headers["Authorization"];
// use token to authorize in your own method
var authorized = AmIAuthorized();
if(authorized = false) {
response.StatusCode = 401;
response.Write("Invalid token");
return;
}
response.StatusCode = 200; // OK
base.Execute(requestContext); // allow inheriting controller to continue
}
}
Sample code to call the api
public static void ExecutePostRequest(string contentType)
{
request = (HttpWebRequest)WebRequest.Create(Uri + Querystring);
request.Method = "POST";
request.ContentType = contentType; // application/json usually
request.Headers["Authorization"] = token;
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
writer.Write(postRequestData);
// GetResponse reaises an exception on http status code 400
// We can pull response out of the exception and continue on our way
try
{
response = (HttpWebResponse)request.GetResponse();
}
catch (WebException ex)
{
response = (HttpWebResponse)ex.Response;
}
finally
{
using (StreamReader reader =
new StreamReader(response.GetResponseStream()))
responseText = reader.ReadToEnd();
httpcontext = HttpContext.Current;
}
}

Resources