I want to accept http request to order prefab move, so how to receive http request in unity 3D?
If you mean you want to build a web service in your Unity app.
RESTful-Unity is an easy-to-use plugin.
Define the api routing
RoutingManager routingManager = new RoutingManager();
routingManager.AddRoute(new Route(Route.Type.POST, "/path/to/call", "PrefabInvoke.Move"));
Create an Invoke to response the request
namespace RESTfulHTTPServer.src.invoker
{
public class PrefabInvoke : MonoBehaviour
{
public static Response Move(Request request)
{
Response response = new Response();
string responseData = "";
string json = request.GetPOSTData();
bool valid = true;
UnityInvoker.ExecuteOnMainThread.Enqueue (() => {
Debug.Log(json);
try
{
//TODO: Parse Json string and do somthing
response.SetHTTPStatusCode((int)HttpStatusCode.OK);
responseData = "sucess message";
}
catch (Exception ex)
{
valid = false;
string msg = "failed to deseiralised JSON";
responseData = msg;
}
});
// Wait for the main thread
while (responseData.Equals("")) {}
// Filling up the response with data
if (valid) {
// 200 - OK
response.SetContent(responseData);
response.SetHTTPStatusCode ((int)HttpStatusCode.OK);
response.SetMimeType (Response.MIME_CONTENT_TYPE_JSON);
} else {
// 406 - Not acceptable
response.SetContent("Somthing wrong");
response.SetHTTPStatusCode((int) HttpStatusCode.NotAcceptable);
response.SetMimeType(Response.MIME_CONTENT_TYPE_HTML);
}
return response;
}
}
}
Related
Is there a way of logging the request and response from the client layer(not from controller as we can use middleware to log the same there).
I am looking to eliminate developer code for audit log here (//log request ,//log response and and creating a provider context ) instead move them to a common handler , may be inherit from delegating handler delegating handler and have the Audit log code there.
Any ideas ?
Currently we have audit logging in the client where another service is called but the developer has to do the following :
Client layer code:
{
IRestResponse response = null;
ConnectorHTMLResponse CCMSResponse = null;
request.Validate(request.TemplateName);
var providerContext = _messageTracker.CreateProviderContext(correlationId, "MailTrigger", "GetHTML", OperationProtocols.HTTPS);
//log request
await providerContext.StartAsync(request, param => request.TemplateName);
var bodyJson = ToBodyJson(request, TemplateType.HTML);
try
{
response = await ExecuteAsync(bodyJson, correlationId);
}
catch (Exception ex)
{
await providerContext.RaiseExceptionAsync(ex);
throw;
}
Response = ConstructHTMLDocumentDetails(ValidateResponse(response));
//log response
await providerContext.CompletedAsync(Response);
return Response;
}
//and in the message tracker(Common code )
public static ProviderContext CreateProviderContext(this IMessageTracker messageTracker, string correlationId, string systemId, string operationName, OperationProtocols protocol)
{
var context = new ProviderContext(
messageTracker,
correlationId,
systemId,
operationName,
Assembly.GetCallingAssembly().GetName().Name,
protocol
);
return context;
}
public async Task StartAsync<T>(T payload, Func<T, string> primaryIdentifierFunc = null, Func<T, string> secondaryIdentifierFunc = null)
{
await StartAsync(payload, primaryIdentifierFunc?.Invoke(payload), secondaryIdentifierFunc?.Invoke(payload));
}
public async Task CompletedAsync<T>(T payload, Func<T, string> primaryIdentifierFunc = null, Func<T, string> secondaryIdentifierFunc = null)
{
_source.Payload = payload.AsPayload();
_source.PrimaryIdentifier = primaryIdentifierFunc?.Invoke(payload) ?? _source.PrimaryIdentifier;
_source.SecondaryIdentifier = secondaryIdentifierFunc?.Invoke(payload) ?? _source.SecondaryIdentifier;
await _tracker.TrackProviderResponseAsync(
//track in cloud
);
}``
I use HttpClient object for PostAsync. I need to add BackgroundSessionConfiguration for iOS while I am creating HttpClient object. So I changed my code like this:
var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration ("my.app.identifier");
_client = new HttpClient (new NSUrlSessionHandler (configuration));
This works when I send first request with PostAsync. But when I send request second time, it doesn't work.
I did it for Login Operation like this: (It works first time but if I logout and login again, it doesn't work.)
public class LoginService
{
private HttpClient _client;
public LoginService()
{
if (_client == null)
{
_client = Helper.CreateHttpClientLogin(_client);
}
}
public async Task<LoginResponse<LoginDataResponse>> Login(LoginRequest request)
{
LoginResponse<LoginDataResponse> responseModel = new LoginResponse<LoginDataResponse>();
try
{
string json = JsonConvert.SerializeObject(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var jsonBody = await _client.PostAsync(App.ServiceURL.Login_Url, content);
string jsonstr = await jsonBody.Content.ReadAsStringAsync();
if (jsonstr == null || jsonstr == "")
{
responseModel.Success = false;
responseModel.Status = 0;
responseModel.Message = AppResources.UnknownHostException;
}
else
responseModel = (LoginResponse<LoginDataResponse>)JsonConvert.DeserializeObject(jsonstr, typeof(LoginResponse<LoginDataResponse>));
}
catch (Exception ex)
{
string text = ex.ToString();
responseModel.Status = 0;
AppResources.Culture = CrossMultilingual.Current.CurrentCultureInfo;
responseModel.Message = AppResources.UnknownHostException;
}
return responseModel;
}
}
public class Helper
{
public static HttpClient CreateHttpClientLogin(HttpClient _client)
{
if (Device.RuntimePlatform == Device.iOS)
{
var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration("my.app.identifier");
_client = new HttpClient(new NSUrlSessionHandler(configuration));
}
else
{
//_client = new HttpClient(new System.Net.Http.HttpClientHandler());
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
_client = new HttpClient(handler);
}
return _client;
}
}
And I have this code on AppDelegate: (I don't know but maybe it causes the bug)
public static Action BackgroundSessionCompletionHandler;
public override void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler)
{
// We get a completion handler which we are supposed to call if our transfer is done.
BackgroundSessionCompletionHandler = completionHandler;
}
What must I do for this?
Edit:
I solved the problem I mentioned above by creating the Login Service object once the application was first opened. (After logout previously, I was rebuilding every time I login)
But now I have other error. When I run my app on "iPhone 7 plus - iOS 13.6" device I got this error:
System.Net.Http.HttpRequestException: unknown error ---> Foundation.NSErrorException: Error Domain=NSURLErrorDomain Code=-1 "unknown error" UserInfo={NSErrorFailingURLStringKey=https://mydomain/Api/Login, NSErrorFailingURLKey=https://mydomain/Api/Login, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"BackgroundDataTask <E69F3EAF-0AE9-4FAE-A01B-988167B7F6BC>.<3>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundDataTask <E69F3EAF-0AE9-4FAE-A01B-988167B7F6BC>.<3>, NSLocalizedDescription=unknown error}
--- End of inner exception stack trace ---
at System.Net.Http.NSUrlSessionHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x001d4] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.20.2.2/src/Xamarin.iOS/Foundation/NSUrlSessionHandler.cs:527
at System.Net.Http.HttpClient.FinishSendAsyncBuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) [0x0017e] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs:506
at App.Services.LoginService.Login (FileOrbis.Models.RequestModels.LoginRequest request) [0x00084] in C:\Users\PcName\Desktop\App\App\Services\LoginService.cs:40
And simulator log file is:
Startup:
arguments: --device=06098E5B-1853-4A83-8434-8071D8973A14 --launchsim=//Users/deytek/Library/Caches/Xamarin/mtbs/builds/App.iOS/b2c75f2acbd4ff91c305dba10ca791b7/bin/iPhoneSimulator/Debug/App.iOS.app -argument=-monodevelop-port -argument=51890 -setenv=__XAMARIN_DEBUG_PORT__=51890 --sdkroot=/Applications/Xcode.app -h=192.168.1.7 -ssh=deytek --launched-by=devenv-16.0
version: 16.7.0.0 (54a29526ef6f853bdd37adbcc3791ce90ca82735)
Connecting to existing client
Exit:
Exit Code: 0
I encounter with this error when I use Background Session Configuration. If I use normal HttpClient object (without Background Session Configuration), it works
NOTE: I also tried iPhone 5s iOS 12.4.8 and iPad Pro (3rd Generation) iOS 13.6.1 It works these devices. But it doesn't work on iPhone 7 Plus 13.6
I been working on a proxy-like controller on ASP.NET, it takes URL from incoming request and uses HttpClient to call the URL, then return the file.
This proxy-like controller work like this:
Works fine when executing one request
Works fine when executing multiple requests,those responses have content-type of "application/json" and "xml"
Request Time-out on 2nd request and Task Cancelled when executing 4 requests, those responses have content-type of "image/png". Like following:
(These 4 request are being sent to controller almost at the same time).
Request 1 - start at 0s , finished at 1s
Request 2 - start at 1s ,Time out and exception throw at 11:00(Time out is 10s), server hangs until timeout
Request 3 - start at 11s , finished at 12s
Request 4 - start at 12s , finsihed at 13s
Please Ignore the POST Situation
Controller
public async Task<FileStreamResult> Proxy(string method, string url, string data = "")
{
myHttpClient client = new myHttpClient();
Tuple<MemoryStream,string> result = await client.Proxy(this.Request, method, url).ConfigureAwait(false);
return File(result.Item1,result.Item2); //file stream and file type
}
HttpClient
public class myHttpClient
{
private static HttpClient _httpClient;
static myHttpClient()
{
if (_httpClient == null) {
_httpClient = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
_httpClient.Timeout = TimeSpan.FromSeconds(7);
Log ocLog = new Log();
ocLog.Write("init client " + ServicePointManager.DefaultConnectionLimit ,false);
}
}
public async Task<Tuple<MemoryStream, string>> Proxy(HttpRequestBase contextRequest, string method, string url)
{
var request = new HttpRequestMessage();
request.RequestUri = new Uri("http://localhost" + url);
if (method == "GET")
{
request.Method = HttpMethod.Get;
request.Content = null;
}
else if (method == "POST")
{
request.Method = HttpMethod.Post;
}
//copy request header from context.Request
foreach (string headerName in contextRequest.Headers)
{
string[] headerValues = contextRequest.Headers.GetValues(headerName);
if (!request.Headers.TryAddWithoutValidation(headerName, headerValues))
{
request.Content.Headers.TryAddWithoutValidation(headerName, headerValues);
}
}
try
{
using (HttpResponseMessage response = await _httpClient.SendAsync(request).ConfigureAwait(false))
{
if (response.IsSuccessStatusCode)
{
var contentBytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
MemoryStream ms = new MemoryStream(contentBytes);
return new Tuple<MemoryStream, string>(ms, response.Content.Headers.ContentType.ToString());
}
else
{
return null;
}
}
}
catch (Exception ex)
{
throw ex;
}
}
}
What have i tried
Checking ServicePointManager.DefaultConnectionLimit. It is very large.
Use System.net.WebRequest library instead of HttpClient, still the same.
The problem has been solved by setting the "Maximum Process Workers" of the application pool to 3. It did the trick
As follow this answer How to send Parameter/Query in HubConnection SignalR Core
I'm setting the client :
const connectionHub = new HubConnectionBuilder()
.withUrl(Constants.URL_WEB_SOCKET + '?token=123')
.build();
but how to get the token value server side?
public override async Task OnConnectedAsync()
{
_connectionId = Context.ConnectionId;
var token = Context.Items["token"]; // this is null
var token2 = Context.QueryString["token"]; // 'HubCallerContext' does not contain a definition for 'QueryString'
await base.OnConnectedAsync();
}
if you want to get token value in .net core, you can use the following code:
var httpContext = Context.GetHttpContext();
var tokenValue = httpContext.Request.Query["token"];
You can send a parameter in QueryString.
In your client, declare a string dictionary and connection
private Dictionary<string, string> _querystringdata = new Dictionary<string, string>();
private HubConnection _connection;
private const string HubUrl = "your hub url";
Then, assign the value you want to send
_querystringdata.Add("key", "Value");
_connection = new HubConnection(HubUrl, _querystringdata);
Start the connection
if (_connection.State == ConnectionState.Disconnected)
{
// Creating the signalrHub proxy
IHubProxy signalrHub = _connection.CreateHubProxy("SignalrHub");
Console.WriteLine("Initiating Connection");
// starting the signalr connection
_connection.Start().ContinueWith(task =>
{
if (task.IsFaulted)
{
Console.WriteLine("There was an error opening the connection:{0}", task.Exception.GetBaseException());
}
else
{
Console.WriteLine("Connected to server");
//Client methods which server can invoke
signalrHub.On<dynamic>("sendMessage", (data) =>
{
Console.WriteLine("Message:- {0}", data);
// do something
});
}
}).Wait();
}
then in your server signalR hub class
public override Task OnConnected()
{
try
{
// getting the value sent with query string
var token = Context.QueryString.Get("Key");
// do something like connection mapping etc
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return (base.OnConnected());
}
I'm using the following code in a Xamarin Forms app:
HttpResponseMessage response = null;
try
{
HttpContent content = new StringContent(JsonConvert.SerializeObject(register), Encoding.UTF8, "application/json");
response = await client.InvokeApiAsync("register", content, HttpMethod.Post, null, null);
if (!response.IsSuccessStatusCode)
{
string error = await response.Content.ReadAsStringAsync();
var def = new { Message = "" };
var errorMessage = JsonConvert.DeserializeAnonymousType(error, def);
return KloverResult.BuildError(true, errorMessage.Message);
}
}
catch (MobileServiceInvalidOperationException e)
{
if (e.Response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
string error = await e.Response.Content.ReadAsStringAsync();
var def = new { Message = "" };
var errorMessage = JsonConvert.DeserializeAnonymousType(error, def);
return KloverResult.BuildError(true, errorMessage.Message);
}
else
{
return KloverResult.BuildError(false, "Invalid username or password");
}
}
The issue that I'm having is when a MobileServiceInvalidOperationException is thrown as a result of a 500. When I try to read the content of the response (e.Response.Content) it's null. When I call the same API using Restlet I get the following response:
{
"Message": "Name jblogs is already taken."
}
This is what I expect to be in my error variable, however it's null.
My question is, should I be able to read the Content of the Response? If so, do I need to do some more setup on the client/server? The API being called is returning the error form a webapi using:
Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Name jblogs is already taken.");
Any help would be appreciated.
A 500 response means that the server crashed. It's likely that there was no content in that case.
If your API is returning status=500, then it is doing the wrong thing. What you should be doing is returning a status in the 400 series - 409 (conflict) seems appropriate to me.
If your API is not returning status=500 deliberately, then the server crashed and you don't get content.
According to your description, I built my Mobile App application with a custom WebApi endpoint to test this issue. Based on my test, I leverage Microsoft.Azure.Mobile.Client 3.1.0 to invoke custom WebApi, I could retrieve the content by Response.Content.ReadAsStringAsync() when the response status is 409 or 500 and so on. Here are my code snippet, you could refer to them:
WebApi
[MobileAppController]
public class ValuesController : ApiController
{
public async Task<HttpResponseMessage> Get()
{
await Task.Delay(TimeSpan.FromSeconds(2));
return Request.CreateErrorResponse(HttpStatusCode.Conflict, "Name jblogs is already taken.");
}
}
Client App
try
{
MobileServiceClient client = new MobileServiceClient("https://bruce-chen-002.azurewebsites.net/");
var response = await client.InvokeApiAsync("/api/values", HttpMethod.Get, null);
}
catch (MobileServiceInvalidOperationException e)
{
if (e.Response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
string error = await e.Response.Content.ReadAsStringAsync();
}
}
Result