1

Fooわかりました。ちょっとの間、次のような名前のエンティティの API を取得したとします。

.../api/foo (GET, PUT, POST) or (SELECT, INSERT, UPDATE internally)

また、これは非常に簡潔で軽量であるため、多くの消費者、特にモバイル デバイスに適しています。ComeForthここで、REST に固執する場合、という名前の操作が存在し、次のようになると仮定しましょう。

.../api/foo/1/comeforth (POST) or (perform the come forth operation)

わかりました。では、その操作について消費者からもう少し情報が必要であると仮定しましょう。簡潔にするために、FooID とその他の情報を保持する新しいリソースを作成します。名前が付けられComeForth、API は次のようになります。

.../api/comeforth (POST) or (perform the come forth operation)

さて、前の API.../api/foo/1/comeforthは問題ないように思えますが、2 つ目の API は丸い穴に四角いペグをはめようとしているように感じます。気まぐれでリソースを作成できるからといって、そうすべきであるとは限りません。だから、私の質問は次のとおりです。

  1. 実際には、ComeForth操作のために SOAP ベースのサービスを公開するだけでよいのでしょうか?
  2. SOAP を使用する場合、それはコンシューマーに影響を与えませんか (つまり、JavaScriptモバイル デバイスなどのコンシューマーにとってはより多くの作業が必要になるのでしょうか?
  3. SOAP は操作ベースで、REST はエンティティ ベースであるというのは、本当に厳格な規則なのでしょうか? もしそうなら、API でさえ.../api/foo/1/comeforthそのルールを破っているのではないでしょうか?

いずれにせよ、必要に応じて適切なテクノロジを使用していることを確認したいだけです。

4

2 に答える 2

2

あなたが説明している場合、操作対象のリソースは Foo ではなく、代わりにトランザクションです (コメントに基づく)。特定のアクション タイプ (ComeForth) のタイプ T (Foo) のエンティティに対する長時間実行トランザクションをモデル化します。

コントローラーは、処理のためにトランザクション POST 要求を受け取り、その進行状況を追跡するために使用できる、トランザクションに割り当てられた一意の識別子を含むトランザクションの表現を返します。

クライアントは、トランザクションが処理のために受け入れられたときに受け取った一意の識別子を使用して、長時間実行されているトランザクションのステータスを取得するために GET 操作を実行します。

私はデモ目的で XML シリアル化を使用することにしましたが、トランザクションに参加しているエンティティをバイト配列または意味のあるものとしてシリアル化することができます。

Web API の例:

/トランザクション/{id}

  • POST: 処理用の新しいトランザクションを作成します
  • GET: 指定された ID のトランザクションを取得して、トランザクションが完了したかどうかを確認します

Web API サービス モデル:

[DataContract()]
public class Transaction
{
    public Transaction()
    {
        this.Id = Guid.Empty;
    }

    /// <summary>
    /// Gets or sets the unique identifier for this transaction.
    /// </summary>
    /// <value>
    /// A <see cref="Guid"/> that represents the unique identifier for this transaction.
    /// </value>
    [DataMember()]
    public Guid Id
    {
        get;
        set;
    }

    /// <summary>
    /// Gets or sets a value indicating if this transaction has been completed.
    /// </summary>
    /// <value>
    /// <see langword="true"/> if this transaction has been completed; otherwise, <see langword="false"/>.
    /// </value>
    [DataMember()]
    public bool IsComplete
    {
        get;
        set;
    }

    /// <summary>
    /// Gets or sets the action being performed.
    /// </summary>
    /// <value>The action being performed.</value>
    [DataMember()]
    public string Action
    {
        get;
        set;
    }

    /// <summary>
    /// Gets or sets the serialized representation of the entity participating in the transaction.
    /// </summary>
    /// <value>The serialized representation of the entity participating in the transaction.</value>
    [DataMember()]
    public string Entity
    {
        get;
        set;
    }

    /// <summary>
    /// Gets or sets the assembly qualified name of the entity participating in the transaction.
    /// </summary>
    /// <value>
    /// The <see cref="Type.AssemblyQualifiedName"/> of the <see cref="Entity"/>.
    /// </value>
    [DataMember()]
    public string EntityType
    {
        get;
        set;
    }

    /// <summary>
    /// Returns the <see cref="Entity"/> as a type of <typeparamref name="T"/>.
    /// </summary>
    /// <typeparam name="T">The type to project the <see cref="Entity"/> as.</typeparam>
    /// <returns>
    /// An object of type <typeparamref name="T"/> that represents the <see cref="Entity"/>.
    /// </returns>
    public T As<T>() where T : class
    {
        T result    = default(T);

        var serializer = new XmlSerializer(typeof(T));

        using (var reader = XmlReader.Create(new MemoryStream(Encoding.UTF8.GetBytes(this.Entity))))
        {
            result  = serializer.Deserialize(reader) as T;
        }

        return result;
    }

    /// <summary>
    /// Serializes the specified <paramref name="entity"/>.
    /// </summary>
    /// <typeparam name="T">The type of entity being serialized.</typeparam>
    /// <param name="entity">The entity to serialize.</param>
    public static Transaction From<T>(T entity, string action = null) where T : class
    {
        var transaction = new Transaction();

        transaction.EntityType  = typeof(T).AssemblyQualifiedName;
        transaction.Action      = action;

        var serializer  = new XmlSerializer(typeof(T));
        byte[] data     = null;

        using (var stream = new MemoryStream())
        {
            serializer.Serialize(stream, entity);
            stream.Flush();

            data        = stream.ToArray();
        }

        transaction.Entity = Encoding.UTF8.GetString(data);

        return transaction;
    }
}

[DataContract()]
public class Foo
{
    public Foo()
    {

    }

    [DataMember()]
    public string PropertyA
    {
        get;
        set;
    }

    [DataMember()]
    public int PropertyB
    {
        get;
        set;
    }

    [DataMember()]
    public Foo PropertyC
    {
        get;
        set;
    }
}

トランザクションコントローラー:

public class TransactionsController : ApiController
{
    public TransactionsController() : base()
    {

    }

    private static ConcurrentDictionary<Guid, Transaction> _transactions = new ConcurrentDictionary<Guid, Transaction>();

    /// <summary>
    /// Using to initiate the processing of a transaction
    /// </summary>
    /// <param name="transaction"></param>
    /// <returns></returns>
    [HttpPost()]
    public HttpResponseMessage Post(Transaction transaction)
    {
        if(transaction == null)
        {
            return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, new HttpError("Unable to model bind request."));
        }

        transaction.Id  = Guid.NewGuid();

        // Execute asynchronous long running transaction here using the model.
        _transactions.TryAdd(transaction.Id, transaction);

        // Return response indicating request has been accepted fro processing
        return this.Request.CreateResponse<Transaction>(HttpStatusCode.Accepted, transaction);

    }

    /// <summary>
    /// Used to retrieve status of a pending transaction.
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet()]
    public HttpResponseMessage Get(Guid id)
    {
        Transaction transaction = null;

        if(!_transactions.TryGetValue(id, out transaction))
        {
            return this.Request.CreateErrorResponse(HttpStatusCode.NotFound, new HttpError("Transaction does not exist"));
        }

        return this.Request.CreateResponse<Transaction>(HttpStatusCode.OK, transaction);
    }
}

トランザクション コントローラーへのクライアント呼び出しの例:

var foo = new Foo()
{
    PropertyA   = "ABC",
    PropertyB   = 123,

    PropertyC   = new Foo()
    {
        PropertyA   = "DEF",
        PropertyB   = 456
    }
};

var transaction = Transaction.From<Foo>(foo, "ComeForth");

Guid pendingTransactionId = Guid.Empty;

// Initiate a transaction
using(var client = new HttpClient())
{
    client.BaseAddress  = new Uri("http://localhost:12775/api/", UriKind.Absolute);

    using (var response = client.PostAsJsonAsync<Transaction>("transactions", transaction).Result)
    {
        response.EnsureSuccessStatusCode();

        pendingTransactionId = response.Content.ReadAsAsync<Transaction>().Result.Id;
    }
}

// Retrieve status of transaction
Transaction pendingTransaction = null;

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri("http://localhost:12775/api/", UriKind.Absolute);

    var requestUri = String.Format(null, "transactions\\{0}", pendingTransactionId.ToString());

    using (var response = client.GetAsync(requestUri).Result)
    {
        response.EnsureSuccessStatusCode();

        pendingTransaction = response.Content.ReadAsAsync<Transaction>().Result;
    }
}

// Check if transaction has completed
if(pendingTransaction.IsComplete)
{

}

したがって、REST と ASP.NET Web API を引き続き使用して、実行時間の長いプロセスの開始をモデル化できます。必要なのは、実行する操作を独自の個別のリソースとして表すことだけです。これが開発作業に役立つことを願っています。

于 2012-09-21T19:31:14.360 に答える
1

私には、これは非常に自由形式の質問のように聞こえます。多くの要因を考慮する必要があります。

通話がCRUD(作成、取得、更新、削除)と一致する場合、RESTは最適です。Twitterを例にとると、Twitterの投稿を作成、取得、更新、削除できます。

ここで、トランザクションを処理する支払いプロセッサを考慮に入れます。これを作成して(つまり、cc#を渡す)、条件付きで何かを実行し、トランザクション結果(成功または失敗)を返す可能性があります。トランザクションを実際に「更新」することはできません。また、トランザクションを「再取得」しても、送信したデータを実際に取得することはできません。確かに、トランザクションを「削除」することはできません。トランザクションを無効にするか、払い戻し(一部または全額)を実行することができます。この例では、RESTは意味がありません。

それはあなたがRESTと操作のハイブリッドを持つことができなかったということではありません。一部のエンティティがRESTに準拠しているが、RESTが適合しない追加の方法(支払いの処理など)がある場合。

ターゲットオーディエンスが決定する必要があるRESTまたはSOAPを選択する決定は、WCFサービス(SOAPを使用)はRESTよりも.NETでの実装がはるかに簡単であり、消費するテクノロジがルビーの場合はその逆になります。

于 2012-09-21T15:15:38.407 に答える