0

私は、OpenRasta を使用して、OpenSource の同僚がアプリケーションにアクセスするための Web サービスをホストする前任者からプロジェクトを継承しました。これは OpenRasta への私の最初の進出です。100% 確実ではありませんが、手動のブラウザー リクエストを介して機能する多くの追加機能を追加しましたが、それはおそらく後で別の質問になります。そのため、機能をテストするための一連の単体テストの作成に着手しました。とにかくこれを行う必要があります。すべてが通過する GET 要求ごとに 1 つまたは 2 つの単体テストを正常に作成しましたが、プロジェクトにある単一の POST のテストに行き詰まっています。

HTTP 415 エラー '8-[2012-12-07 11:23:19Z] Information(0) Executing OperationResult OperationResult: type=RequestMediaTypeUnsupported, statusCode=415.' が表示されます。出力ウィンドウから。Nate Taylor の投稿http://taylonr.com/integration-testing-openrastaからインスピレーションを得て、彼に同じ質問をしたところ、親切に回答してくれました。私はまだ彼の答えを解読しようとしていますが、おそらく誰かが私の理解のギャップを拡大して埋めることができるでしょうか? これが私が試してきたコードです:

[Test]
public void AuthenticateUserJSONPOSTTest()
    {
        object content = new AuthenticationStructure { Username = "matthew.radford", Password = "obviously not going to tell you that bit and will replace with a domain test user when the time comes", AppId = 4 };

        OpenRastaJSONTestMehods.POST<AuthenticationResult, AuthenticationStructure>("http://localhost/user", content);
    }

[Test]
    public static void POST<T, U>(string uri, object content)
    {
        const string LocalHost = "http://localhost/";
        if (uri.Contains(LocalHost))
            POST<T, U>(new Uri(uri), content);
        else
            throw new UriFormatException(string.Format("The uri doesn't contain {0}", LocalHost));
    }
    [Test, WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
    public static void POST<T,U>(Uri serviceuri, object content)
    {
        using (var host = new InMemoryHost(new Configuration()))
        {
            var request = new InMemoryRequest()
            {
                Uri = serviceuri,
                HttpMethod = "POST"
            };

            request.Entity.ContentType = MediaType.Json;
            request.Entity.Headers["Accept"] = "application/json";

            var serializer = new DataContractJsonSerializer(typeof(T), new [] { typeof(AuthenticationStructure) });
            serializer.WriteObject(request.Entity.Stream, content); 
            request.Entity.Stream.Seek(0, SeekOrigin.Begin); 
            request.Entity.ContentLength = request.Entity.Stream.Length;

            //Just a read test, not necessary for the output
            byte[] readbyte = new byte[(int)request.Entity.ContentLength];
            request.Entity.Stream.Read(readbyte, 0, (int)request.Entity.ContentLength);
            request.Entity.Stream.Seek(0, SeekOrigin.Begin);

            U readObject = (U)serializer.ReadObject(request.Entity.Stream);
            request.Entity.Stream.Seek(0, SeekOrigin.Begin);

            NUnit.Framework.Assert.AreEqual(content, readObject);

            var response = new InMemoryResponse();

            response.Entity.ContentType = MediaType.Json;
            response.Entity.Headers["Accept"] = "application/json";

            response = (InMemoryResponse)host.ProcessRequest(request);
            int statusCode = response.StatusCode;

//this is where the test fails because the above response isn't correct and gives the 415 statusCode
            NUnit.Framework.Assert.AreEqual(201, statusCode, string.Format("Http StatusCode Error: {0}", statusCode));

            object returnedObject;
            if (response.Entity.ContentLength > 0)
            {
                response.Entity.Stream.Seek(0, SeekOrigin.Begin);

                //Just a read test, not necessary for the output
                readbyte = new byte[(int)response.Entity.ContentLength];
                response.Entity.Stream.Read(readbyte, 0, (int)response.Entity.ContentLength);
                response.Entity.Stream.Seek(0, SeekOrigin.Begin);

                returnedObject = serializer.ReadObject(response.Entity.Stream);
                //return returnedObject;
            }
        }
    }

前もって感謝します。

4

1 に答える 1

1

今朝、これを機能させるために、さまざまなことを試しました。JSON ストリームを文字列として読み取って、オブジェクトがどのようにシリアル化されているかを実際に確認することで、最初の一歩を踏み出しました。

それを行うには、C# で Stream を byte[] に変換する方法を見つけましたか? これにより、ストリームを文字列に読み取る正しい方向に進みました。したがって、出力ウィンドウに書き込むために次の行を思いつきました。

Debug.WriteLine(Encoding.UTF8.GetString(StreamHelper.ReadToEnd(request.Entity.Stream), 0, (int)request.Entity.Stream.Length).ToString());

これが結果でした: {"__type":"AuthenticationStructure","username":"matthew.radford","appid":4,"password":"###########"}

この出力には 2 つの問題があることに気付きました。まず、簡単に言うと、パスワードは appid の前にある必要があります。これは、私が間違えた AuthenticationStructure クラスで簡単に修正できました。DataMember Order は、AppId の 3 に等しい必要があります

[DataMember(Name="appid", Order=3)]
    public int AppId { get; set; }

第二に、デフォルトのシリアライゼーションには、表記の先頭に「__type」メンバーが含まれています。これは明らかに、ハンドラーの POST メソッドのパラメーターと一致しません。

[HttpOperation(HttpMethod.POST)] 
    public OperationResult Post(string username, string password, int appid)

この時点で、JSON 文字列から型表記を削除しようとしました。DataContractJsonSerializer を使用して配列値を .NET プロパティにデシリアライズする良いサイトを見つけました。コンストラクターを記述して、false に設定された alwaysEmitTypeInformation を含める方法の両方を示しましたが、型情報をエミットしたかったので、true に変更しました。また、AuthenticationTypeSurrogate と呼ばれる IDataContractSurrogate に基づいてサロゲートを作成する方法も示しました。

public class AuthenticationTypeSurrogate : IDataContractSurrogate
{
    public Type GetDataContractType(Type type)
    {
        // "Book" will be serialized as an object array
        // This method is called during serialization, deserialization, and schema export. 
        if (typeof(AuthenticationStructure).IsAssignableFrom(type))
        {
            return typeof(object[]);
        }
        return type;
    }
    public object GetObjectToSerialize(object obj, Type targetType)
    {
        // This method is called on serialization.
        if (obj is AuthenticationStructure)
        {
            AuthenticationStructure authenticationStructure = (AuthenticationStructure)obj;
            return new object[] { authenticationStructure.Username, authenticationStructure.Password, authenticationStructure.AppId };
        }
        return obj;
    }
    public object GetDeserializedObject(object obj, Type targetType)
    {
        // This method is called on deserialization.
        if (obj is object[])
        {
            object[] arr = (object[])obj;
            AuthenticationStructure authenticationStructure = new AuthenticationStructure { Username = (string)arr[0], Password = (string)arr[1], AppId = (int)arr[2] };
            return authenticationStructure;
        }
        return obj;
    }
    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
    {
        return null; // not used
    }
    public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
    {
        return typeDeclaration; // Not used
    }
    public object GetCustomDataToExport(Type clrType, Type dataContractType)
    {
        return null; // not used
    }
    public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
    {
        return null; // not used
    }
    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
    {
        return; // not used
    }
}

シリアライゼーションはこれで機能しましたが、今はデシリアライゼーションです。JSON を作成する代わりにオブジェクト配列をシリアライズしていたため、まだ機能していなかったため、わざわざデバッグして正しくすることはありませんでした。メソッドが返されます。したがって、これを JSON に変換する方法を見つけられない限り、別のレンガの壁にぶつかりました。

最後に、オーバーロードされた POST メソッドに __type パラメータを追加し、他のパラメータを元の POST メソッドに渡すことにしました。

[HttpOperation(HttpMethod.POST)]
    public OperationResult Post(Type __type, string username, string password, int appid)
    {
        return Post(username, password, appid);
    }

これはほとんど正しかったのですが、それでもうまくいきませんでした。そのため、最後に別のオーバーロードされたメソッドを作成し、型全体を渡しました。

[HttpOperation(HttpMethod.POST)]
    public OperationResult Post(AuthenticationStructure astruct)
    {
        return Post(astruct.Username, astruct.Password, astruct.AppId);
    }

そして最後にこれが機能しました。私はそれに完全に満足しておらず、既存の方法に直接リンクしたかったのですが、うまくいきました。

見てくれた皆さん、特にネイトに感謝します。最初の回答に感謝します。うまくいけば、これが将来人々に役立つことを願っています。

于 2012-12-10T15:57:44.207 に答える