Consuming Web API from MVC4 controller? - rest

I am currently working on a website and I had a good separation of concerns following a repository pattern with repositories and managers. Now, I am attempting to implement a Web API as I would greatly benefit from it in the future being able to use it from various clients. Since I am fairly new to REST services, I am having trouble with the correct procedure to consume my web API from a Service in my MVC4 application to then use that service in my MVC controllers. I do not want to have to use knockout for every call to the API.
My Web APIs look something like this(simplified):
public class UserController : ApiController
{
private readonly IUserManager _manager;
public UserController(IUserManager manager)
{
this._manager = manager;
}
// GET api/user
public IEnumerable<User> Get()
{
return _manager.GetAll();
}
// GET api/user/5
public User Get(int id)
{
return _manager.GetById(id);
}
// POST api/user
public void Post(User user)
{
_manager.Add(user);
}
// PUT api/user/5
public void Put(User user)
{
_manager.Update(user);
}
// DELETE api/user/5
public void Delete(User user)
{
_manager.Delete(user);
}
}
I essentially would like to create a service to consume my web API as such:
public class UserService : IUserService
{
....Implement something to get,post,put,and delete using the api.
}
so then I can use it in my mvc controller:
public class UserController: Controller
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
this._userService = userService;
}
//And then I will be able to communicate with my WebAPI from my MVC controller
}
I know this is possible because I have seen it done at some workplaces but it is very difficult to find articles about this, I have only found articles explaining how to consume web API through knockout. Any help or tips would be greatly appreciated.

Have a look at the implementation over here: https://github.com/NBusy/NBusy.SDK/blob/master/src/NBusy.Client/Resources/Messages.cs
It basically makes use of HttpClient class to consume Web API. One caveat though, all responses are wrapped in a custom HttpResponse class in that sample. You don't need to do that and can simply use the retrieved DTO object as the return type or a raw HttpResponseMessage class.

You might want to create a static class, I created a separate Class Library to use across solutions that might want to use the API.
NOTE: I use RestSharp for POST and PUT operation since I haven't been able to get them to work using the regular HttpClient over SSL. As you can see documented in this question.
internal static class Container
{
private static bool isInitialized;
internal static HttpClient Client { get; set; }
internal static RestClient RestClient { get; set; }
/// <summary>
/// Verifies the initialized.
/// </summary>
/// <param name="throwException">if set to <c>true</c> [throw exception].</param>
/// <returns>
/// <c>true</c> if it has been initialized; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="System.InvalidOperationException">Service must be initialized first.</exception>
internal static bool VerifyInitialized(bool throwException = true)
{
if (!isInitialized)
{
if (throwException)
{
throw new InvalidOperationException("Service must be initialized first.");
}
}
return true;
}
/// <summary>
/// Initializes the Service communication, all methods throw a System.InvalidOperationException if it hasn't been initialized.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="connectionUserName">Name of the connection user.</param>
/// <param name="connectionPassword">The connection password.</param>
internal static void Initialize(string url, string connectionUserName, string connectionPassword)
{
RestClient = new RestClient(url);
if (connectionUserName != null && connectionPassword != null)
{
HttpClientHandler handler = new HttpClientHandler
{
Credentials = new NetworkCredential(connectionUserName, connectionPassword)
};
Client = new HttpClient(handler);
RestClient.Authenticator = new HttpBasicAuthenticator(connectionUserName, connectionPassword);
}
else
{
Client = new HttpClient();
}
Client.BaseAddress = new Uri(url);
isInitialized = true;
}
}
public static class UserService
{
public static void Initialize(string url = "https://serverUrl/", string connectionUserName = null, string connectionPassword = null)
{
Container.Initialize(url, connectionUserName, connectionPassword);
}
public static async Task<IEnumerable<User>> GetServiceSites()
{
// RestSharp example
Container.VerifyInitialized();
var request = new RestRequest("api/Users", Method.GET);
request.RequestFormat = DataFormat.Json;
var response = await Task.Factory.StartNew(() => { return Container.RestClient.Execute<List<User>>(request); }).ConfigureAwait(false);
return response.Data;
// HttpClient example
var response = await Container.Client.GetAsync("api/Users/").ConfigureAwait(false);
return await response.Content.ReadAsAsync<IEnumerable<User>>().ConfigureAwait(false);
}
public static async Task<User> Get(int id)
{
Container.VerifyInitialized();
var request = new RestRequest("api/Users/" + id, Method.GET);
var response = await Task.Factory.StartNew(() => { return Container.RestClient.Execute<User>(request); }).ConfigureAwait(false);
return response.Data;
}
public static async Task Put(int id, User user)
{
Container.VerifyInitialized();
var request = new RestRequest("api/Users/" + id, Method.PATCH);
request.RequestFormat = DataFormat.Json;
request.AddBody(user);
var response = await Task.Factory.StartNew(() => { return Container.RestClient.Execute(request); }).ConfigureAwait(false);
}
public static async Task Post(User user)
{
Container.VerifyInitialized();
var request = new RestRequest("api/Users", Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddBody(user);
var response = await Task.Factory.StartNew(() => { return Container.RestClient.Execute(request); }).ConfigureAwait(false);
}
public static async Task Delete(int id)
{
Container.VerifyInitialized();
var request = new RestRequest("api/Users/" + id, Method.DELETE);
var response = await Task.Factory.StartNew(() => { return Container.RestClient.Execute(request); }).ConfigureAwait(false);
}
}

Related

Error handling Web Api .net core and Repository Pattern

I have question about web api and Repository may be its a duplicate question.
but i tried to search on it and i did not get any satisfactory answer.
In my Repository i am getting data with the help of httpclient.
My question is that i can get an error inside my response or i can get required json data which i can map to my product class.I am returning IEnumerable.
1) If i get an error how can i bubble it up to controller and display an error to user.
2) Return the MessageResponse instead of IEnumerable and handle it inside the controller.
What is the best way.
enter code here
public interface IProduct{
Task<IEnumerable<Product>> All();
}
public class Product:IProduct
{
public async Task<IEnumerable<Product>> All(){
var ResponseMessage=//some response.
}
}
You could customize a ApiException which is used to get the error message of the response, and call the UseExceptionHandler in your startup.cs ,refer to the following :
ProductRep
public class ProductRep : IProduct
{
private readonly HttpClient _client;
public ProductRep(HttpClient client)
{
_client = client;
}
public async Task<IEnumerable<Product>> All()
{
List<Product> productlist = new List<Product>();
var response = await _client.GetAsync("https://localhost:44357/api/values/GetProducts");
string apiResponse = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode == false)
{
JObject message = JObject.Parse(apiResponse);
var value = message.GetValue("error").ToString();
throw new ApiException(value);
}
productlist = JsonConvert.DeserializeObject<List<Product>>(apiResponse);
return productlist;
}
public class ApiException : Exception
{
public ApiException(string message): base(message)
{ }
}
}
Startup.cs
app.UseExceptionHandler(a => a.Run(async context =>
{
var feature = context.Features.Get<IExceptionHandlerPathFeature>();
var exception = feature.Error;
var result = JsonConvert.SerializeObject(new { error = exception.Message });
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(result);
}));

Asp.Net-Core + MongoDb - How to search database by "code" and return the original url?

I am unsure how to go about searching for the "Code" stored in my Database in order to return the "OriginalUrl".
I know I can search for the ObjectId but I want to be able to search by the "Code" assigned to that ObjectId.
Currently I have a working program that takes a Url as well as a "title" and sends it to the database:
It is assigned an Objectid _id and a randomly generated 12 character "Code":
If it helps this is my Controller class:
namespace ShortenUrls.Controllers
{
[Route("api/codes")]
public class ShortUrlsController : Controller
{
private readonly ShortUrlRepository _repo;
public ShortUrlsController(ShortUrlRepository repo)
{
_repo = repo;
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(string id)
{
var su = await _repo.GetAsync(id);
if (su == null)
return NotFound();
return Ok(su);
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] ShortUrl su)
{
await _repo.CreateAsync(su);
return Ok(su);
}
}
And Repository class:
namespace ShortenUrls.Models.Repository
{
public class ShortUrlRepository
{
private const string alphabet = "23456789bcdfghjkmnpqrstvwxyz-_";
private static readonly Random rand = new Random();
private readonly Database _db;
public ShortUrlRepository(Database db)
{
_db = db;
}
private static string GenerateCode()
{
const int codeLength = 12;
var chars = new char[codeLength];
for (var i = 0; i < codeLength; i++)
{
chars[i] = alphabet[rand.Next(0, alphabet.Length)];
}
return new string(chars);
}
public Task<ShortUrl> GetAsync(string id)
{
var objId = ObjectId.Parse(id);
return _db.Urls.Find(x => x.Id == objId).FirstOrDefaultAsync();
}
public Task CreateAsync(ShortUrl su)
{
su.Code = GenerateCode();
return _db.Urls.InsertOneAsync(su);
}
}
Just use a filter. Doing it this way let's you create a query specifically for the "code".
public async Task<ShortUrl> GetAsync(string code)
{
var filterBuilder = new FilterDefinitionBuilder<ShortUrl>();
var filter = filterBuilder.Eq(s => s.Code, code);
var cursor = await _db.Urls.FindAsync(filter);
return await cursor.FirstOrDefaultAsync();
}
Assuming you already know the code when calling this and that ObjectId is created on InsertOneAsync call. First change your repository to take Code as searchable input.
public Task<ShortUrl> GetAsync(string code)
{
return await _db.Urls.FirstOrDefaultAsync(x => x.Code == code);
}
Then change your controller Get to this:
[HttpGet("{code}")]
public async Task<IActionResult> Get(string code)
{
var su = await _repo.GetAsync(code);
if (su == null)
return NotFound();
return Ok(su);
}
In your controller you can access su.OriginalUrl if you need to only return that after getting the object.
Then in postman you can just call http://localhost:51767/api/codes?code=cmg3fjjr_gtv
Remember only Id works for default url parameters as setup by your default routes in Startup.cs.
app.UseMvc(routes => { /*...*/ })
So this wont work: /api/codes/cmg3fjjr_gtv unless you specifically set up routing or change {code} back to {id}. Readability of your code suffers though.

ASP.NET Core JWT and Claims

I have a question regarding JWT authentication in ASP.NET Core and Claims, because I don't know if I get everything correctly.
When I create a JWT token in ASP.NET I add some Claims, some of which can be custom. What happens when the request with JWT token is sent from the client to API. How is User.Claims filled ? Does it use the claims that are read from JWT?
I would like to create a custom Identity provider ( don't want to use this provided by ASP.NET), with my own tables for user data, roles etc. I don't want store all important data required to fulfill the policy in JWT token (the amount of information stored in token matters, as well as security matters). Is it possible to store only basic claims (like user id, name etc) in JWT token, and then re-fetch other required data DB/ Cache? Along with that, I would like to use the standard mechanism for [Authorize] and the Policy mechanism.
How to make this all work: Custom User Identity + JWT + Standard ASP.NET policy-based authorization + claims fetched from DB/Cache on every request? How to achieve this?
Asp Net Core
First step is write the method that configure Jwt authentication:
// Configure authentication with JWT (Json Web Token).
public void ConfigureJwtAuthService(IServiceCollection services)
{
// Enable the use of an [Authorize(AuthenticationSchemes =
// JwtBearerDefaults.AuthenticationScheme)]
// attribute on methods and classes to protect.
services.AddAuthentication().AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
IssuerSigningKey = JwtController.SecurityKey,
ValidAudience = JwtController.Audience,
ValidIssuer = JwtController.Issuer,
// When receiving a token, check that we've signed it.
ValidateIssuerSigningKey = true,
// When receiving a token, check that it is still valid.
ValidateLifetime = true,
// This defines the maximum allowable clock skew when validating
// the lifetime. As we're creating the tokens locally and validating
// them on the same machines which should have synchronised time,
// this can be set to zero.
ClockSkew = TimeSpan.FromMinutes(0)
};
});
}
Now inside the ConfigureServices() method of the Startup.cs, we can call ConfigureJwtAuthService() method to configure the Jwt authentication.
This is the complete Startup.cs:
using System;
using Autofac;
using ExpertCodeBlogWebApp.Controllers;
using ExpertCodeBlogWebApp.Domain;
using ExpertCodeBlogWebApp.Domain.Interfaces;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
namespace ExpertCodeBlogWebApp
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add
// services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Configure jwt autenticazione
ConfigureJwtAuthService(services);
// Repositories
services.AddScoped<IUserRepository, UserRepository>();
// Create the Autofac container builder for dependency injection
var builder = new ContainerBuilder();
// Add any Autofac modules or registrations.
builder.RegisterModule(new AutofacModule());
// Return ServiceProvider
var serviceProvider = services.BuildServiceProvider();
return serviceProvider;
}
// Configure authentication with JWT (Json Web Token).
public void ConfigureJwtAuthService(IServiceCollection services)
{
// Enable the use of an [Authorize(AuthenticationSchemes =
// JwtBearerDefaults.AuthenticationScheme)]
// attribute on methods and classes to protect.
services.AddAuthentication().AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
IssuerSigningKey = JwtController.SecurityKey,
ValidAudience = JwtController.Audience,
ValidIssuer = JwtController.Issuer,
// When receiving a token, check that we've signed it.
ValidateIssuerSigningKey = true,
// When receiving a token, check that it is still valid.
ValidateLifetime = true,
// This defines the maximum allowable clock skew when validating
// the lifetime.
// As we're creating the tokens locally and validating them on the
// same machines which should have synchronised time, this can be
// set to zero.
ClockSkew = TimeSpan.FromMinutes(0)
};
});
}
// This method gets called by the runtime. Use this method to configure
// the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
}
// For dependency injection.
public class AutofacModule : Module
{
// Dependency Injection with Autofact
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<UserRepository>().As<IUserRepository>()
.SingleInstance();
}
}
}
The JwtController.cs
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using ExpertCodeBlogWebApp.Domain;
using ExpertCodeBlogWebApp.Domain.Interfaces;
using ExpertCodeBlogWebApp.Domain.Models;
using ExpertCodeBlogWebApp.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
namespace ExpertCodeBlogWebApp.Controllers
{
[Route("api/[controller]")]
public class JwtController : Controller
{
#region Private Members
// JWT-related members
private TimeSpan TokenExpiration;
private SigningCredentials SigningCredentials;
// EF and Identity members, available through DI
private MyDbContext DbContext;
private IUserRepository _userRepository;
private readonly ILogger _logger;
#endregion Private Members
#region Static Members
private static readonly string PrivateKey = "my_PrivateKey";
public static readonly SymmetricSecurityKey SecurityKey =
new SymmetricSecurityKey(Encoding.ASCII.GetBytes(PrivateKey));
public static readonly string Issuer = "my_Issuer";
public static readonly string Audience = "my_Audience";
#endregion Static Members
#region Constructor
// I have used Autofac in the Startup.cs for dependency injection)
public JwtController(
MyDbContext dbContext,
IUserRepository userRepository,
ILogger<JwtController> logger)
{
_logger = logger;
_userRepository = userRepository;
// Instantiate JWT-related members
TokenExpiration = TimeSpan.FromMinutes(10);
SigningCredentials = new SigningCredentials(SecurityKey,
SecurityAlgorithms.HmacSha256);
// Instantiate through Dependency Injection with Autofact
DbContext = dbContext;
}
#endregion Constructor
#region Public Methods
// Manages the request for a new authentication or the refresh of an
// already established one
[HttpPost("token")]
public async Task<IActionResult>
Authentication([FromBody]JwtRequestViewModel jwt)
{
if (ModelState.IsValid)
{
string grantType = jwt.GrantType;
if (grantType == "password")
{
string userName = jwt.UserName;
string password = jwt.Password;
// Password check required
var user = await
_userRepository.GetUserInfoWithCheckPwd(userName, password);
// Check if user is expired (check the ExpireDate property)
if (UserExpired(user))
return BadRequest($"Account of {user.Name} expired!");
if (UserEnabled(user))
return await GenerateToken(user);
else
return BadRequest("User name or password invalid.");
}
}
else if (grantType == "refresh_token")
{
string userName = jwt.UserName;
// Refresh token (no password check required)
var user = await _userRepository.GetUserInfoByName(userName);
// Check if user is expired (check the ExpireDate property)
if (UserExpired(user))
return BadRequest($"Account of {user.Name} expired!");
string token = jwt.Token;
if (token == user.Token)
{
// Generate token and send it via a json-formatted string
return await GenerateToken(user);
}
else
{
return BadRequest("User token invalid.");
}
}
else
return BadRequest("Authentication type invalid.");
}
else
return BadRequest("Request invalid.");
}
#endregion Public Methods
#region Private Methods
private bool UserExpired(Users utente)
{
if (utente != null)
return utente.ExpireDate.CompareTo(DateTime.Now) < 0;
return true;
}
private bool UserEnabled(Users utente)
{
if (utente != null)
return utente.Enabled == true;
return false;
}
private JsonSerializerSettings DefaultJsonSettings
{
get
{
return new JsonSerializerSettings()
{
Formatting = Formatting.Indented
};
}
}
private async Task<IActionResult> GenerateToken(Users user)
{
try
{
if (user != null)
{
var handler = new JwtSecurityTokenHandler();
DateTime newTokenExpiration = DateTime.Now.Add(TokenExpiration);
ClaimsIdentity identity = new ClaimsIdentity(
new GenericIdentity(user.Name, "TokenAuth"),
new[] { new Claim("ID", user.Id.ToString())}
);
var securityToken = handler.CreateToken(new SecurityTokenDescriptor
{
Issuer = JwtController.Issuer,
Audience = JwtController.Audience,
SigningCredentials = SigningCredentials,
Subject = identity,
Expires = newTokenExpiration
});
string encodedToken = handler.WriteToken(securityToken);
// Update token data on database
await _userRepository.UpdateTokenData(user.Name, encodedToken,
newTokenExpiration);
// Build the json response
// (I use Automapper to maps an object into another object)
var jwtResponse = Mapper.Map<JwtResponseViewModel>(user);
jwtResponse.AccessToken = encodedToken;
jwtResponse.Expiration = (int)TokenExpiration.TotalSeconds;
return Ok(jwtResponse);
}
return NotFound();
}
catch(Exception e)
{
return BadRequest(e.Message);
}
}
#endregion
}
}
On my project I use Angular. For call JwtController method by Angular:
login(userName: string, password: string)
{
return this.getLoginEndpoint(userName, password)
.map((response: Response) => this.processLoginResponse(response));
}
getLoginEndpoint(userName: string, password: string): Observable<Response>
{
// Body
// JwtRequest is a model class that I use to send info to the controller
let jwt = new JwtRequest();
jwt.GrantType = "password";
jwt.UserName = userName;
jwt.Password = password;
jwt.ClientId = "my_Issuer";
// Post requiest (I use getAuthHeader that attach to the header the
// authentication token, but it can also be omitted because it is ignored
// by the JwtController
return this.http.post(this.loginUrl, JSON.stringify(jwt),
this.getAuthHeader(true))
}
protected getAuthHeader(includeJsonContentType?: boolean): RequestOptions
{
// Hera I use this.authService.accessToken that is a my service where
// I have store the token received from the server
let headers = new Headers({
'Authorization': 'Bearer ' + this.authService.accessToken });
if (includeJsonContentType)
headers.append("Content-Type", "application/json");
headers.append("Accept", `application/vnd.iman.v01+json,
application/json, text/plain, */*`);
headers.append("App-Version", "01");
return new RequestOptions({ headers: headers });
}
private processLoginResponse(response: Response)
{
// process the response..
}
On the controllers classes (or methods) that you want to be accessible only by authenticated users (not on your JwtController because its method must be accessible by all users) you can set:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
To call from Angular the controller method that require authentication, you need to attach the token into the header with the getAuthHeader() method.
I hope this post can help you.
yes it uses the claim stored in jwt token
look at the httpcontext object for claims that are stored in token when you created the token
this link can also be helpfull https://joonasw.net/view/adding-custom-claims-aspnet-core-2

Sending a message to a azure service bus queue in .Net Core Core using the REST API

I want to send a message to my Azure Service Bus Queue in .Net Core but the WindowsAzure.ServiceBus Package is not compatible with .Net Core.
Can anyone show me how to send a message to the queue using the REST API?
While the current client is not .NET Core compatible, the new client, that is a work in progress, is 100% compatible. The pre-release package will be available on April 3rd and the status can be tracked here. You could pull down the course code and compile it already today with the caveat that API will be changing as the team is trying to flesh out the design details.
Can anyone show me how to send a message to the queue using the REST API?
As 4c74356b41 mentioned in his comment, we could send a message to Azure Service Bus queue via this REST API:
POST http{s}://{serviceNamespace}.servicebus.windows.net/{queuePath|topicPath}/messages
here is a example
In above request, I provide a Shared Access Signature (token), to generate a Shared Access Signature (token), please refer to this article.
Thanks to Fred's answer, I've expanded to include how to post the authentication header with signature.
public class AzureServiceBusSettings
{
public string BaseUrl { get; set; }
public string SharedAccessKey { get; set; }
public string SharedAccessKeyName { get; set; }
}
public interface IServiceBus
{
/// <summary>
/// Publish domain events to domain topic.
/// </summary>
Task PublishAsync<T>(T #event)
/// <summary>
/// Send commands to command queue.
/// </summary>
Task SendAsync<T>(T command)
}
public class ServiceBus : IServiceBus
{
private readonly AzureServiceBusSettings _settings;
public ServiceBus(IOptions<AzureServiceBusSettings> azureServiceBusSettings)
{
_settings = azureServiceBusSettings.Value;
}
/// <summary>
/// Publish domain events to domain topic.
/// </summary>
public async Task PublishAsync<T>(T #event)
{
await SendInternalAsync(#event, "domain");
}
/// <summary>
/// Send commands to command queue.
/// </summary>
public async Task SendAsync<T>(T command)
{
await SendInternalAsync(command, "commands");
}
private async Task SendInternalAsync<T>(T command, string queueName)
{
var json = JsonConvert.SerializeObject(command);
var content = new StringContent(json, Encoding.UTF8, "application/json");
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(_settings.BaseUrl);
try
{
var url = $"/{queueName}/messages";
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("SharedAccessSignature", GetSasToken(queueName));
var response = await httpClient.PostAsync(url, content);
// Success returns 201 Created.
if (!response.IsSuccessStatusCode)
{
// Handle this.
}
}
catch (Exception ex)
{
// Handle this.
// throw;
}
}
}
private string GetSasToken(string queueName)
{
var url = $"{_settings.BaseUrl}/{queueName}";
// Expiry minutes should be a setting.
var expiry = (int)DateTime.UtcNow.AddMinutes(20).Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
var signature = GetSignature(url, _settings.SharedAccessKey);
var token = $"sr={WebUtility.UrlEncode(url)}&sig={WebUtility.UrlEncode(signature)}&se={expiry}&skn={_settings.SharedAccessKeyName}";
return token;
}
private static string GetSignature(string url, string key)
{
var expiry = (int)DateTime.UtcNow.AddMinutes(20).Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
var value = WebUtility.UrlEncode(url) + "\n" + expiry;
var encoding = new UTF8Encoding();
var keyByte = encoding.GetBytes(key);
var valueBytes = encoding.GetBytes(value);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
var hashmessage = hmacsha256.ComputeHash(valueBytes);
var result = Convert.ToBase64String(hashmessage);
return result;
}
}
}
And a simple xunit test to post:
public class ServiceBusTests
{
public class FooCommand : ICommand
{
public Guid CommandId { get; set; }
}
private Mock<IOptions<AzureServiceBusSettings>> _mockAzureServiceBusOptions;
private ServiceBus _sut;
public ServiceBusTests()
{
var settings = new AzureServiceBusSettings
{
BaseUrl = "https://my-domain.servicebus.windows.net",
SharedAccessKey = "my-key-goes-here",
SharedAccessKeyName = "RootManageSharedAccessKey"
};
_mockAzureServiceBusOptions = new Mock<IOptions<AzureServiceBusSettings>>();
_mockAzureServiceBusOptions.SetupGet(o => o.Value).Returns(settings);
_sut = new ServiceBus(
_mockAzureServiceBusOptions.Object);
}
[Fact]
public async Task should_send_message()
{
// Arrange.
var command = new FooCommand {CommandId = Guid.NewGuid()};
// Act.
await _sut.SendAsync(command);
// Assert.
// TODO: Get the command from the queue and assert something.
}
}

CSRF token validation failed in Odata4j

I'm trying to post the entry to Odata service Url which is created in SAP ABAP backend. When i'm trying to send the data from java code to SAP ABAP system via Odata service, I'm getting CSRF Token validation error. Below is the code snippet for Odata Post service
ODataConsumer.Builder builder = ODataConsumers.newBuilder(URL_ODATASERVICE);
// LOGGER.info(TAG+"Authentication values are been set");
builder.setClientBehaviors(new BasicAuthenticationBehavior(USERNAME, PASSWORD), new SAPCSRFBehavior());
ODataConsumer consumer = builder.build();
OCreateRequest<OEntity> createRequest = consumer.createEntity("LogSet")
.properties(OProperties.string("TestplanId", "111")).properties(OProperties.string("ProcessId", "222"))
.properties(OProperties.string("Seqno", "33"));
// Execute the OData post
OEntity newMaterial = createRequest.execute();
And the SAPSCRBehaviour class will be
public class SAPCSRFBehaviour implements JerseyClientBehavior {
private static final String CSRF_HEADER = "X-CSRF-Token";
private static final String SAP_COOKIES = "SAP_SESSIONID";
private String xsrfCookieName;
private String xsrfCookieValue;
private String xsrfTokenValue;
#Override
public ODataClientRequest transform(ODataClientRequest request) {
if (request.getMethod().equals("GET")) {
request = request.header(CSRF_HEADER, "Fetch");
return request;
} else {
return request.header(CSRF_HEADER, xsrfTokenValue).header("Cookie", xsrfCookieName + "=" + xsrfCookieValue);
}
}
#Override
public void modifyWebResourceFilters(final Filterable arg0) {
}
#Override
public void modifyClientFilters(final Filterable client) {
client.addFilter(new ClientFilter() {
#Override
public ClientResponse handle(final ClientRequest clientRequest) throws ClientHandlerException {
ClientResponse response = getNext().handle(clientRequest);
List<NewCookie> cookies = response.getCookies();
for (NewCookie cookie : cookies) {
if (cookie.getName().startsWith(SAP_COOKIES)) {
xsrfCookieName = cookie.getName();
xsrfCookieValue = cookie.getValue();
break;
}
}
MultivaluedMap<String, String> responseHeaders = response.getHeaders();
xsrfTokenValue = responseHeaders.getFirst(CSRF_HEADER);
return response;
}
});
}
#Override
public void modify(final ClientConfig arg0) {
}}
Please suggest me the solution to avoid this issue
Best Regards,
Naveen