| [391] | 1 | using System;
 | 
|---|
| [402] | 2 | using System.Collections.Generic;
 | 
|---|
| [391] | 3 | using System.IO;
 | 
|---|
 | 4 | using System.Net;
 | 
|---|
| [402] | 5 | using Utf8Json;
 | 
|---|
| [391] | 6 | 
 | 
|---|
 | 7 | namespace Webserver.WebAPI {
 | 
|---|
 | 8 |         public abstract class AbsRestApi : AbsWebAPI {
 | 
|---|
| [394] | 9 |                 private static readonly UnityEngine.Profiling.CustomSampler jsonDeserializeSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Deserialize");
 | 
|---|
 | 10 | 
 | 
|---|
| [410] | 11 |                 protected AbsRestApi (string _name = null) : this(null, _name) {
 | 
|---|
 | 12 |                 }
 | 
|---|
 | 13 | 
 | 
|---|
 | 14 |                 protected AbsRestApi (Web _parentWeb, string _name = null) : base(_parentWeb, _name) {
 | 
|---|
 | 15 |                 }
 | 
|---|
 | 16 | 
 | 
|---|
| [391] | 17 |                 public sealed override void HandleRequest (RequestContext _context) {
 | 
|---|
| [402] | 18 |                         IDictionary<string, object> inputJson = null;
 | 
|---|
 | 19 |                         byte[] jsonInputData = null;
 | 
|---|
 | 20 |                         
 | 
|---|
| [391] | 21 |                         if (_context.Request.HasEntityBody) {
 | 
|---|
| [402] | 22 |                                 Stream requestInputStream = _context.Request.InputStream;
 | 
|---|
 | 23 |                                 
 | 
|---|
 | 24 |                                 jsonInputData = new byte[_context.Request.ContentLength64];
 | 
|---|
 | 25 |                                 requestInputStream.Read (jsonInputData, 0, (int)_context.Request.ContentLength64);
 | 
|---|
 | 26 |                                 
 | 
|---|
 | 27 |                                 try {
 | 
|---|
 | 28 |                                         jsonDeserializeSampler.Begin ();
 | 
|---|
 | 29 |                                         inputJson = JsonSerializer.Deserialize<IDictionary<string, object>> (jsonInputData);
 | 
|---|
 | 30 |                                         
 | 
|---|
 | 31 |                                         // Log.Out ("JSON body:");
 | 
|---|
 | 32 |                                         // foreach ((string key, object value) in inputJson) {
 | 
|---|
 | 33 |                                         //      Log.Out ($" - {key} = {value} ({value.GetType ()})");
 | 
|---|
 | 34 |                                         // }
 | 
|---|
 | 35 |                                         
 | 
|---|
 | 36 |                                         jsonDeserializeSampler.End ();
 | 
|---|
 | 37 |                                 } catch (Exception e) {
 | 
|---|
 | 38 |                                         jsonDeserializeSampler.End ();
 | 
|---|
| [391] | 39 | 
 | 
|---|
| [402] | 40 |                                         SendErrorResult (_context, HttpStatusCode.BadRequest, null, "INVALID_BODY", e);
 | 
|---|
 | 41 |                                         return;
 | 
|---|
| [391] | 42 |                                 }
 | 
|---|
 | 43 |                         }
 | 
|---|
 | 44 | 
 | 
|---|
 | 45 |                         try {
 | 
|---|
 | 46 |                                 switch (_context.Request.HttpMethod) {
 | 
|---|
 | 47 |                                         case "GET":
 | 
|---|
| [402] | 48 |                                                 if (inputJson != null) {
 | 
|---|
 | 49 |                                                         SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "GET_WITH_BODY");
 | 
|---|
| [391] | 50 |                                                         return;
 | 
|---|
 | 51 |                                                 }
 | 
|---|
 | 52 | 
 | 
|---|
 | 53 |                                                 HandleRestGet (_context);
 | 
|---|
 | 54 |                                                 return;
 | 
|---|
 | 55 |                                         case "POST":
 | 
|---|
 | 56 |                                                 if (!string.IsNullOrEmpty (_context.RequestPath)) {
 | 
|---|
| [402] | 57 |                                                         SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "POST_WITH_ID");
 | 
|---|
| [391] | 58 |                                                         return;
 | 
|---|
 | 59 |                                                 }
 | 
|---|
 | 60 | 
 | 
|---|
| [402] | 61 |                                                 if (inputJson == null) {
 | 
|---|
 | 62 |                                                         SendErrorResult (_context, HttpStatusCode.BadRequest, null, "POST_WITHOUT_BODY");
 | 
|---|
| [391] | 63 |                                                         return;
 | 
|---|
 | 64 |                                                 }
 | 
|---|
 | 65 | 
 | 
|---|
| [402] | 66 |                                                 HandleRestPost (_context, inputJson, jsonInputData);
 | 
|---|
| [391] | 67 |                                                 return;
 | 
|---|
 | 68 |                                         case "PUT":
 | 
|---|
 | 69 |                                                 if (string.IsNullOrEmpty (_context.RequestPath)) {
 | 
|---|
| [402] | 70 |                                                         SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "PUT_WITHOUT_ID");
 | 
|---|
| [391] | 71 |                                                         return;
 | 
|---|
 | 72 |                                                 }
 | 
|---|
 | 73 | 
 | 
|---|
| [402] | 74 |                                                 if (inputJson == null) {
 | 
|---|
 | 75 |                                                         SendErrorResult (_context, HttpStatusCode.BadRequest, null, "PUT_WITHOUT_BODY");
 | 
|---|
| [391] | 76 |                                                         return;
 | 
|---|
 | 77 |                                                 }
 | 
|---|
 | 78 | 
 | 
|---|
| [402] | 79 |                                                 HandleRestPut (_context, inputJson, jsonInputData);
 | 
|---|
| [391] | 80 |                                                 return;
 | 
|---|
 | 81 |                                         case "DELETE":
 | 
|---|
 | 82 |                                                 if (string.IsNullOrEmpty (_context.RequestPath)) {
 | 
|---|
| [402] | 83 |                                                         SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "DELETE_WITHOUT_ID");
 | 
|---|
| [391] | 84 |                                                         return;
 | 
|---|
 | 85 |                                                 }
 | 
|---|
 | 86 | 
 | 
|---|
| [402] | 87 |                                                 if (inputJson != null) {
 | 
|---|
 | 88 |                                                         SendErrorResult (_context, HttpStatusCode.BadRequest, null, "DELETE_WITH_BODY");
 | 
|---|
| [391] | 89 |                                                         return;
 | 
|---|
 | 90 |                                                 }
 | 
|---|
 | 91 | 
 | 
|---|
 | 92 |                                                 HandleRestDelete (_context);
 | 
|---|
 | 93 |                                                 return;
 | 
|---|
 | 94 |                                         default:
 | 
|---|
| [402] | 95 |                                                 SendErrorResult (_context, HttpStatusCode.BadRequest, null, "INVALID_METHOD");
 | 
|---|
| [391] | 96 |                                                 return;
 | 
|---|
 | 97 |                                 }
 | 
|---|
 | 98 |                         } catch (Exception e) {
 | 
|---|
| [402] | 99 |                                 SendErrorResult (_context, HttpStatusCode.InternalServerError, jsonInputData, "ERROR_PROCESSING", e);
 | 
|---|
| [391] | 100 |                         }
 | 
|---|
 | 101 |                 }
 | 
|---|
 | 102 | 
 | 
|---|
| [402] | 103 |                 static AbsRestApi () {
 | 
|---|
 | 104 |                         JsonWriter writer = new JsonWriter ();
 | 
|---|
 | 105 |                         writer.WriteBeginArray ();
 | 
|---|
 | 106 |                         writer.WriteEndArray ();
 | 
|---|
 | 107 |                         JsonEmptyData = writer.ToUtf8ByteArray ();
 | 
|---|
 | 108 |                 }
 | 
|---|
| [391] | 109 | 
 | 
|---|
| [404] | 110 |                 protected virtual void HandleRestGet (RequestContext _context) {
 | 
|---|
 | 111 |                         SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, null, "Unsupported");
 | 
|---|
 | 112 |                 }
 | 
|---|
 | 113 | 
 | 
|---|
 | 114 |                 protected virtual void HandleRestPost (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
 | 
|---|
 | 115 |                         SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, _jsonInputData, "Unsupported");
 | 
|---|
 | 116 |                 }
 | 
|---|
 | 117 | 
 | 
|---|
 | 118 |                 protected virtual void HandleRestPut (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
 | 
|---|
 | 119 |                         SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, _jsonInputData, "Unsupported");
 | 
|---|
 | 120 |                 }
 | 
|---|
 | 121 | 
 | 
|---|
 | 122 |                 protected virtual void HandleRestDelete (RequestContext _context) {
 | 
|---|
 | 123 |                         SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, null, "Unsupported");
 | 
|---|
 | 124 |                 }
 | 
|---|
 | 125 | 
 | 
|---|
 | 126 | 
 | 
|---|
 | 127 | #region Helpers
 | 
|---|
 | 128 | 
 | 
|---|
| [402] | 129 |                 protected static readonly byte[] JsonEmptyData;
 | 
|---|
 | 130 |                 
 | 
|---|
| [404] | 131 |                 protected static void PrepareEnvelopedResult (out JsonWriter _writer) {
 | 
|---|
| [402] | 132 |                         WebUtils.PrepareEnvelopedResult (out _writer);
 | 
|---|
 | 133 |                 }
 | 
|---|
| [391] | 134 | 
 | 
|---|
| [404] | 135 |                 protected static void SendEnvelopedResult (RequestContext _context, ref JsonWriter _writer, HttpStatusCode _statusCode = HttpStatusCode.OK,
 | 
|---|
| [402] | 136 |                         byte[] _jsonInputData = null, string _errorCode = null, Exception _exception = null) {
 | 
|---|
 | 137 |                         
 | 
|---|
 | 138 |                         WebUtils.SendEnvelopedResult (_context, ref _writer, _statusCode, _jsonInputData, _errorCode, _exception);
 | 
|---|
| [391] | 139 |                 }
 | 
|---|
 | 140 | 
 | 
|---|
| [404] | 141 |                 protected static void SendErrorResult (RequestContext _context, HttpStatusCode _statusCode, byte[] _jsonInputData = null, string _errorCode = null, Exception _exception = null) {
 | 
|---|
 | 142 |                         PrepareEnvelopedResult (out JsonWriter writer);
 | 
|---|
 | 143 |                         writer.WriteRaw (JsonEmptyData);
 | 
|---|
 | 144 |                         SendEnvelopedResult (_context, ref writer, _statusCode, _jsonInputData, _errorCode, _exception);
 | 
|---|
 | 145 |                 }
 | 
|---|
 | 146 | 
 | 
|---|
 | 147 |                 protected static bool TryGetJsonField (IDictionary<string, object> _jsonObject, string _fieldName, out int _value) {
 | 
|---|
| [391] | 148 |                         _value = default;
 | 
|---|
 | 149 |                         
 | 
|---|
| [402] | 150 |                         if (!_jsonObject.TryGetValue (_fieldName, out object fieldNode)) {
 | 
|---|
| [391] | 151 |                                 return false;
 | 
|---|
 | 152 |                         }
 | 
|---|
 | 153 | 
 | 
|---|
| [402] | 154 |                         if (fieldNode is not double value) {
 | 
|---|
| [391] | 155 |                                 return false;
 | 
|---|
 | 156 |                         }
 | 
|---|
 | 157 | 
 | 
|---|
 | 158 |                         try {
 | 
|---|
| [402] | 159 |                                 _value = (int)value;
 | 
|---|
| [391] | 160 |                                 return true;
 | 
|---|
 | 161 |                         } catch (Exception) {
 | 
|---|
 | 162 |                                 return false;
 | 
|---|
 | 163 |                         }
 | 
|---|
 | 164 |                 }
 | 
|---|
 | 165 | 
 | 
|---|
| [404] | 166 |                 protected static bool TryGetJsonField (IDictionary<string, object> _jsonObject, string _fieldName, out double _value) {
 | 
|---|
| [391] | 167 |                         _value = default;
 | 
|---|
 | 168 |                         
 | 
|---|
| [402] | 169 |                         if (!_jsonObject.TryGetValue (_fieldName, out object fieldNode)) {
 | 
|---|
| [391] | 170 |                                 return false;
 | 
|---|
 | 171 |                         }
 | 
|---|
 | 172 | 
 | 
|---|
| [402] | 173 |                         if (fieldNode is not double value) {
 | 
|---|
| [391] | 174 |                                 return false;
 | 
|---|
 | 175 |                         }
 | 
|---|
 | 176 | 
 | 
|---|
 | 177 |                         try {
 | 
|---|
| [402] | 178 |                                 _value = value;
 | 
|---|
| [391] | 179 |                                 return true;
 | 
|---|
 | 180 |                         } catch (Exception) {
 | 
|---|
 | 181 |                                 return false;
 | 
|---|
 | 182 |                         }
 | 
|---|
 | 183 |                 }
 | 
|---|
 | 184 | 
 | 
|---|
| [404] | 185 |                 protected static bool TryGetJsonField (IDictionary<string, object> _jsonObject, string _fieldName, out string _value) {
 | 
|---|
| [391] | 186 |                         _value = default;
 | 
|---|
 | 187 |                         
 | 
|---|
| [402] | 188 |                         if (!_jsonObject.TryGetValue (_fieldName, out object fieldNode)) {
 | 
|---|
| [391] | 189 |                                 return false;
 | 
|---|
 | 190 |                         }
 | 
|---|
 | 191 | 
 | 
|---|
| [402] | 192 |                         if (fieldNode is not string value) {
 | 
|---|
| [391] | 193 |                                 return false;
 | 
|---|
 | 194 |                         }
 | 
|---|
 | 195 | 
 | 
|---|
 | 196 |                         try {
 | 
|---|
| [402] | 197 |                                 _value = value;
 | 
|---|
| [391] | 198 |                                 return true;
 | 
|---|
 | 199 |                         } catch (Exception) {
 | 
|---|
 | 200 |                                 return false;
 | 
|---|
 | 201 |                         }
 | 
|---|
 | 202 |                 }
 | 
|---|
| [404] | 203 |                 
 | 
|---|
| [391] | 204 | 
 | 
|---|
| [404] | 205 | #endregion
 | 
|---|
| [391] | 206 |         }
 | 
|---|
 | 207 | }
 | 
|---|